summaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_input.c
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-04-03 09:22:53 +0900
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-04-05 22:33:38 +0900
commit7bc570c8b4f75ddb3fd5dbeb38127cdc4acbcc9c (patch)
treeb688b728c3ea08479f75986d1e9f590fca1f8715 /net/ipv6/ip6_input.c
parent80a9492a33dd7d852465625022d56ff76d62174d (diff)
[IPV6] MROUTE: Support multicast forwarding.
Based on ancient patch by Mickael Hoerdt <hoerdt@clarinet.u-strasbg.fr>, which is available at <http://www-r2.u-strasbg.fr/~hoerdt/dev/linux_ipv6_mforwarding/patch-linux-ipv6-mforwarding-0.1a>. Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Diffstat (limited to 'net/ipv6/ip6_input.c')
-rw-r--r--net/ipv6/ip6_input.c87
1 files changed, 68 insertions, 19 deletions
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 43a617e2268..09a3201e408 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -29,6 +29,7 @@
#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>
@@ -236,36 +237,84 @@ int ip6_mc_input(struct sk_buff *skb)
hdr = ipv6_hdr(skb);
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) {
+ if (!ipv6_ext_hdr(nexthdr)) {
+ /* BUG */
+ goto discard;
+ }
+ offset = ipv6_skip_exthdr(skb, sizeof(*hdr),
+ &nexthdr);
+ if (offset < 0)
+ goto discard;
+
+ if (nexthdr != IPPROTO_ICMPV6)
+ goto discard;
+
+ if (!pskb_may_pull(skb, (skb_network_header(skb) +
+ offset + 1 - skb->data)))
+ goto discard;
+
+ 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:
+ break;
+ default:
+ /* Bogus */
+ goto discard;
+ }
+ deliver = 1;
+ 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);
}
}
#endif
-
+out:
if (likely(deliver)) {
ip6_input(skb);
return 0;
}
+discard:
/* discard */
kfree_skb(skb);