#include <linux/netdevice.h>
#include <linux/in6.h>
#include <linux/icmpv6.h>
+#include <linux/mroute6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
u32 pkt_len;
struct inet6_dev *idev;
- if (dev->nd_net != &init_net) {
- kfree_skb(skb);
- return 0;
- }
-
if (skb->pkt_type == PACKET_OTHERHOST) {
kfree_skb(skb);
return 0;
if (hdr->version != 6)
goto err;
+ /*
+ * RFC4291 2.5.3
+ * A packet received on an interface with a destination address
+ * of loopback must be dropped.
+ */
+ if (!(dev->flags & IFF_LOOPBACK) &&
+ ipv6_addr_loopback(&hdr->daddr))
+ goto err;
+
skb->transport_header = skb->network_header + sizeof(*hdr);
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
rcu_read_unlock();
- return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
+ return NF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, dev, NULL,
+ ip6_rcv_finish);
err:
IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
drop:
static int ip6_input_finish(struct sk_buff *skb)
{
struct inet6_protocol *ipprot;
- struct sock *raw_sk;
unsigned int nhoff;
- int nexthdr;
+ int nexthdr, raw;
u8 hash;
struct inet6_dev *idev;
nhoff = IP6CB(skb)->nhoff;
nexthdr = skb_network_header(skb)[nhoff];
- raw_sk = sk_head(&raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)]);
- if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
- raw_sk = NULL;
+ raw = raw6_local_deliver(skb, nexthdr);
hash = nexthdr & (MAX_INET_PROTOS - 1);
if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {
else if (ret == 0)
IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
} else {
- if (!raw_sk) {
+ if (!raw) {
if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP6_INC_STATS_BH(idev, IPSTATS_MIB_INUNKNOWNPROTOS);
icmpv6_send(skb, ICMPV6_PARAMPROB,
int ip6_input(struct sk_buff *skb)
{
- return NF_HOOK(PF_INET6,NF_IP6_LOCAL_IN, skb, skb->dev, NULL, ip6_input_finish);
+ return NF_HOOK(PF_INET6, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
+ ip6_input_finish);
}
int ip6_mc_input(struct sk_buff *skb)
IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INMCASTPKTS);
hdr = ipv6_hdr(skb);
- deliver = unlikely(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) ||
- ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
+ deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
+#ifdef CONFIG_IPV6_MROUTE
/*
- * IPv6 multicast router mode isnt currently supported.
+ * IPv6 multicast router mode is now supported ;)
*/
-#if 0
- if (ipv6_config.multicast_route) {
- int addr_type;
-
- addr_type = ipv6_addr_type(&hdr->daddr);
-
- if (!(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
- struct sk_buff *skb2;
- struct dst_entry *dst;
+ if (ipv6_devconf.mc_forwarding &&
+ likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) {
+ /*
+ * Okay, we try to forward - split and duplicate
+ * packets.
+ */
+ struct sk_buff *skb2;
+ struct inet6_skb_parm *opt = IP6CB(skb);
+
+ /* Check for MLD */
+ if (unlikely(opt->ra)) {
+ /* Check if this is a mld message */
+ u8 *ptr = skb_network_header(skb) + opt->ra;
+ struct icmp6hdr *icmp6;
+ u8 nexthdr = hdr->nexthdr;
+ int offset;
+
+ /* Check if the value of Router Alert
+ * is for MLD (0x0000).
+ */
+ if ((ptr[2] | ptr[3]) == 0) {
+ deliver = 0;
+
+ if (!ipv6_ext_hdr(nexthdr)) {
+ /* BUG */
+ goto out;
+ }
+ offset = ipv6_skip_exthdr(skb, sizeof(*hdr),
+ &nexthdr);
+ if (offset < 0)
+ goto out;
+
+ if (nexthdr != IPPROTO_ICMPV6)
+ goto out;
+
+ if (!pskb_may_pull(skb, (skb_network_header(skb) +
+ offset + 1 - skb->data)))
+ goto out;
+
+ icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset);
+
+ switch (icmp6->icmp6_type) {
+ case ICMPV6_MGM_QUERY:
+ case ICMPV6_MGM_REPORT:
+ case ICMPV6_MGM_REDUCTION:
+ case ICMPV6_MLD2_REPORT:
+ deliver = 1;
+ break;
+ }
+ goto out;
+ }
+ /* unknown RA - process it normally */
+ }
- dst = skb->dst;
+ if (deliver)
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ else {
+ skb2 = skb;
+ skb = NULL;
+ }
- if (deliver) {
- skb2 = skb_clone(skb, GFP_ATOMIC);
- dst_output(skb2);
- } else {
- dst_output(skb);
- return 0;
- }
+ if (skb2) {
+ skb2->dev = skb2->dst->dev;
+ ip6_mr_input(skb2);
}
}
+out:
#endif
-
- if (likely(deliver)) {
+ if (likely(deliver))
ip6_input(skb);
- return 0;
+ else {
+ /* discard */
+ kfree_skb(skb);
}
- /* discard */
- kfree_skb(skb);
return 0;
}