aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-04-02 20:22:54 -0400
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2008-04-05 09:33:39 -0400
commit14fb64e1f449ef6666f1c3a3fa4e13aec669b98d (patch)
tree92df482215861e4856fe84172d566ebb7d946f4a
parent7bc570c8b4f75ddb3fd5dbeb38127cdc4acbcc9c (diff)
[IPV6] MROUTE: Support PIM-SM (SSM).
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>
-rw-r--r--include/linux/mroute6.h4
-rw-r--r--net/ipv6/Kconfig7
-rw-r--r--net/ipv6/ip6mr.c275
3 files changed, 285 insertions, 1 deletions
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index b92190304e0b..f6469fb90840 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -23,6 +23,8 @@
23#define MRT6_ADD_MFC (MRT6_BASE+4) /* Add a multicast forwarding entry */ 23#define MRT6_ADD_MFC (MRT6_BASE+4) /* Add a multicast forwarding entry */
24#define MRT6_DEL_MFC (MRT6_BASE+5) /* Delete a multicast forwarding entry */ 24#define MRT6_DEL_MFC (MRT6_BASE+5) /* Delete a multicast forwarding entry */
25#define MRT6_VERSION (MRT6_BASE+6) /* Get the kernel multicast version */ 25#define MRT6_VERSION (MRT6_BASE+6) /* Get the kernel multicast version */
26#define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */
27#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */
26 28
27#define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */ 29#define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */
28#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1) 30#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1)
@@ -217,6 +219,8 @@ static inline int ip6mr_sk_done(struct sock *sk) { return 0; }
217 219
218struct mrt6msg { 220struct mrt6msg {
219#define MRT6MSG_NOCACHE 1 221#define MRT6MSG_NOCACHE 1
222#define MRT6MSG_WRONGMIF 2
223#define MRT6MSG_WHOLEPKT 3 /* used for use level encap */
220 __u8 im6_mbz; /* must be zero */ 224 __u8 im6_mbz; /* must be zero */
221 __u8 im6_msgtype; /* what type of message */ 225 __u8 im6_msgtype; /* what type of message */
222 __u16 im6_mif; /* mif rec'd on */ 226 __u16 im6_mif; /* mif rec'd on */
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 9a2ea81e499f..82f987b4ef84 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -216,3 +216,10 @@ config IPV6_MROUTE
216 Experimental support for IPv6 multicast forwarding. 216 Experimental support for IPv6 multicast forwarding.
217 If unsure, say N. 217 If unsure, say N.
218 218
219config IPV6_PIMSM_V2
220 bool "IPv6: PIM-SM version 2 support (EXPERIMENTAL)"
221 depends on IPV6_MROUTE
222 ---help---
223 Support for IPv6 PIM multicast routing protocol PIM-SMv2.
224 If unsure, say N.
225
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 1bdf3c177d58..2b70774be61f 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -54,6 +54,7 @@
54#include <net/ipv6.h> 54#include <net/ipv6.h>
55#include <net/ip6_route.h> 55#include <net/ip6_route.h>
56#include <linux/mroute6.h> 56#include <linux/mroute6.h>
57#include <linux/pim.h>
57#include <net/addrconf.h> 58#include <net/addrconf.h>
58#include <linux/netfilter_ipv6.h> 59#include <linux/netfilter_ipv6.h>
59 60
@@ -75,6 +76,13 @@ static int maxvif;
75 76
76#define MIF_EXISTS(idx) (vif6_table[idx].dev != NULL) 77#define MIF_EXISTS(idx) (vif6_table[idx].dev != NULL)
77 78
79static int mroute_do_assert; /* Set in PIM assert */
80#ifdef CONFIG_IPV6_PIMSM_V2
81static int mroute_do_pim;
82#else
83#define mroute_do_pim 0
84#endif
85
78static struct mfc6_cache *mfc6_cache_array[MFC_LINES]; /* Forwarding cache */ 86static struct mfc6_cache *mfc6_cache_array[MFC_LINES]; /* Forwarding cache */
79 87
80static struct mfc6_cache *mfc_unres_queue; /* Queue of unresolved entries */ 88static struct mfc6_cache *mfc_unres_queue; /* Queue of unresolved entries */
@@ -97,6 +105,10 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache);
97static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert); 105static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);
98static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); 106static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm);
99 107
108#ifdef CONFIG_IPV6_PIMSM_V2
109static struct inet6_protocol pim6_protocol;
110#endif
111
100static struct timer_list ipmr_expire_timer; 112static struct timer_list ipmr_expire_timer;
101 113
102 114
@@ -339,6 +351,132 @@ static struct file_operations ip6mr_mfc_fops = {
339}; 351};
340#endif 352#endif
341 353
354#ifdef CONFIG_IPV6_PIMSM_V2
355static int reg_vif_num = -1;
356
357static int pim6_rcv(struct sk_buff *skb)
358{
359 struct pimreghdr *pim;
360 struct ipv6hdr *encap;
361 struct net_device *reg_dev = NULL;
362
363 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
364 goto drop;
365
366 pim = (struct pimreghdr *)skb_transport_header(skb);
367 if (pim->type != ((PIM_VERSION << 4) | PIM_REGISTER) ||
368 (pim->flags & PIM_NULL_REGISTER) ||
369 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
370 (u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))))
371 goto drop;
372
373 /* check if the inner packet is destined to mcast group */
374 encap = (struct ipv6hdr *)(skb_transport_header(skb) +
375 sizeof(*pim));
376
377 if (!ipv6_addr_is_multicast(&encap->daddr) ||
378 encap->payload_len == 0 ||
379 ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
380 goto drop;
381
382 read_lock(&mrt_lock);
383 if (reg_vif_num >= 0)
384 reg_dev = vif6_table[reg_vif_num].dev;
385 if (reg_dev)
386 dev_hold(reg_dev);
387 read_unlock(&mrt_lock);
388
389 if (reg_dev == NULL)
390 goto drop;
391
392 skb->mac_header = skb->network_header;
393 skb_pull(skb, (u8 *)encap - skb->data);
394 skb_reset_network_header(skb);
395 skb->dev = reg_dev;
396 skb->protocol = htons(ETH_P_IP);
397 skb->ip_summed = 0;
398 skb->pkt_type = PACKET_HOST;
399 dst_release(skb->dst);
400 ((struct net_device_stats *)netdev_priv(reg_dev))->rx_bytes += skb->len;
401 ((struct net_device_stats *)netdev_priv(reg_dev))->rx_packets++;
402 skb->dst = NULL;
403 nf_reset(skb);
404 netif_rx(skb);
405 dev_put(reg_dev);
406 return 0;
407 drop:
408 kfree_skb(skb);
409 return 0;
410}
411
412static struct inet6_protocol pim6_protocol = {
413 .handler = pim6_rcv,
414};
415
416/* Service routines creating virtual interfaces: PIMREG */
417
418static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
419{
420 read_lock(&mrt_lock);
421 ((struct net_device_stats *)netdev_priv(dev))->tx_bytes += skb->len;
422 ((struct net_device_stats *)netdev_priv(dev))->tx_packets++;
423 ip6mr_cache_report(skb, reg_vif_num, MRT6MSG_WHOLEPKT);
424 read_unlock(&mrt_lock);
425 kfree_skb(skb);
426 return 0;
427}
428
429static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
430{
431 return (struct net_device_stats *)netdev_priv(dev);
432}
433
434static void reg_vif_setup(struct net_device *dev)
435{
436 dev->type = ARPHRD_PIMREG;
437 dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8;
438 dev->flags = IFF_NOARP;
439 dev->hard_start_xmit = reg_vif_xmit;
440 dev->get_stats = reg_vif_get_stats;
441 dev->destructor = free_netdev;
442}
443
444static struct net_device *ip6mr_reg_vif(void)
445{
446 struct net_device *dev;
447 struct inet6_dev *in_dev;
448
449 dev = alloc_netdev(sizeof(struct net_device_stats), "pim6reg",
450 reg_vif_setup);
451
452 if (dev == NULL)
453 return NULL;
454
455 if (register_netdevice(dev)) {
456 free_netdev(dev);
457 return NULL;
458 }
459 dev->iflink = 0;
460
461 in_dev = ipv6_find_idev(dev);
462 if (!in_dev)
463 goto failure;
464
465 if (dev_open(dev))
466 goto failure;
467
468 return dev;
469
470failure:
471 /* allow the register to be completed before unregistering. */
472 rtnl_unlock();
473 rtnl_lock();
474
475 unregister_netdevice(dev);
476 return NULL;
477}
478#endif
479
342/* 480/*
343 * Delete a VIF entry 481 * Delete a VIF entry
344 */ 482 */
@@ -361,6 +499,11 @@ static int mif6_delete(int vifi)
361 return -EADDRNOTAVAIL; 499 return -EADDRNOTAVAIL;
362 } 500 }
363 501
502#ifdef CONFIG_IPV6_PIMSM_V2
503 if (vifi == reg_vif_num)
504 reg_vif_num = -1;
505#endif
506
364 if (vifi + 1 == maxvif) { 507 if (vifi + 1 == maxvif) {
365 int tmp; 508 int tmp;
366 for (tmp = vifi - 1; tmp >= 0; tmp--) { 509 for (tmp = vifi - 1; tmp >= 0; tmp--) {
@@ -480,6 +623,19 @@ static int mif6_add(struct mif6ctl *vifc, int mrtsock)
480 return -EADDRINUSE; 623 return -EADDRINUSE;
481 624
482 switch (vifc->mif6c_flags) { 625 switch (vifc->mif6c_flags) {
626#ifdef CONFIG_IPV6_PIMSM_V2
627 case MIFF_REGISTER:
628 /*
629 * Special Purpose VIF in PIM
630 * All the packets will be sent to the daemon
631 */
632 if (reg_vif_num >= 0)
633 return -EADDRINUSE;
634 dev = ip6mr_reg_vif();
635 if (!dev)
636 return -ENOBUFS;
637 break;
638#endif
483 case 0: 639 case 0:
484 dev = dev_get_by_index(&init_net, vifc->mif6c_pifi); 640 dev = dev_get_by_index(&init_net, vifc->mif6c_pifi);
485 if (!dev) 641 if (!dev)
@@ -512,6 +668,10 @@ static int mif6_add(struct mif6ctl *vifc, int mrtsock)
512 write_lock_bh(&mrt_lock); 668 write_lock_bh(&mrt_lock);
513 dev_hold(dev); 669 dev_hold(dev);
514 v->dev = dev; 670 v->dev = dev;
671#ifdef CONFIG_IPV6_PIMSM_V2
672 if (v->flags & MIFF_REGISTER)
673 reg_vif_num = vifi;
674#endif
515 if (vifi + 1 > maxvif) 675 if (vifi + 1 > maxvif)
516 maxvif = vifi + 1; 676 maxvif = vifi + 1;
517 write_unlock_bh(&mrt_lock); 677 write_unlock_bh(&mrt_lock);
@@ -599,7 +759,13 @@ static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
599 struct mrt6msg *msg; 759 struct mrt6msg *msg;
600 int ret; 760 int ret;
601 761
602 skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC); 762#ifdef CONFIG_IPV6_PIMSM_V2
763 if (assert == MRT6MSG_WHOLEPKT)
764 skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt)
765 +sizeof(*msg));
766 else
767#endif
768 skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC);
603 769
604 if (!skb) 770 if (!skb)
605 return -ENOBUFS; 771 return -ENOBUFS;
@@ -609,6 +775,29 @@ static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
609 775
610 skb->ip_summed = CHECKSUM_UNNECESSARY; 776 skb->ip_summed = CHECKSUM_UNNECESSARY;
611 777
778#ifdef CONFIG_IPV6_PIMSM_V2
779 if (assert == MRT6MSG_WHOLEPKT) {
780 /* Ugly, but we have no choice with this interface.
781 Duplicate old header, fix length etc.
782 And all this only to mangle msg->im6_msgtype and
783 to set msg->im6_mbz to "mbz" :-)
784 */
785 skb_push(skb, -skb_network_offset(pkt));
786
787 skb_push(skb, sizeof(*msg));
788 skb_reset_transport_header(skb);
789 msg = (struct mrt6msg *)skb_transport_header(skb);
790 msg->im6_mbz = 0;
791 msg->im6_msgtype = MRT6MSG_WHOLEPKT;
792 msg->im6_mif = reg_vif_num;
793 msg->im6_pad = 0;
794 ipv6_addr_copy(&msg->im6_src, &ipv6_hdr(pkt)->saddr);
795 ipv6_addr_copy(&msg->im6_dst, &ipv6_hdr(pkt)->daddr);
796
797 skb->ip_summed = CHECKSUM_UNNECESSARY;
798 } else
799#endif
800 {
612 /* 801 /*
613 * Copy the IP header 802 * Copy the IP header
614 */ 803 */
@@ -635,6 +824,7 @@ static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
635 skb->ip_summed = CHECKSUM_UNNECESSARY; 824 skb->ip_summed = CHECKSUM_UNNECESSARY;
636 825
637 skb_pull(skb, sizeof(struct ipv6hdr)); 826 skb_pull(skb, sizeof(struct ipv6hdr));
827 }
638 828
639 if (mroute6_socket == NULL) { 829 if (mroute6_socket == NULL) {
640 kfree_skb(skb); 830 kfree_skb(skb);
@@ -1034,6 +1224,44 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
1034 return ret; 1224 return ret;
1035 1225
1036 /* 1226 /*
1227 * Control PIM assert (to activate pim will activate assert)
1228 */
1229 case MRT6_ASSERT:
1230 {
1231 int v;
1232 if (get_user(v, (int __user *)optval))
1233 return -EFAULT;
1234 mroute_do_assert = !!v;
1235 return 0;
1236 }
1237
1238#ifdef CONFIG_IPV6_PIMSM_V2
1239 case MRT6_PIM:
1240 {
1241 int v, ret;
1242 if (get_user(v, (int __user *)optval))
1243 return -EFAULT;
1244 v = !!v;
1245 rtnl_lock();
1246 ret = 0;
1247 if (v != mroute_do_pim) {
1248 mroute_do_pim = v;
1249 mroute_do_assert = v;
1250 if (mroute_do_pim)
1251 ret = inet6_add_protocol(&pim6_protocol,
1252 IPPROTO_PIM);
1253 else
1254 ret = inet6_del_protocol(&pim6_protocol,
1255 IPPROTO_PIM);
1256 if (ret < 0)
1257 ret = -EAGAIN;
1258 }
1259 rtnl_unlock();
1260 return ret;
1261 }
1262
1263#endif
1264 /*
1037 * Spurious command, or MRT_VERSION which you cannot 1265 * Spurious command, or MRT_VERSION which you cannot
1038 * set. 1266 * set.
1039 */ 1267 */
@@ -1056,6 +1284,14 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval,
1056 case MRT6_VERSION: 1284 case MRT6_VERSION:
1057 val = 0x0305; 1285 val = 0x0305;
1058 break; 1286 break;
1287#ifdef CONFIG_IPV6_PIMSM_V2
1288 case MRT6_PIM:
1289 val = mroute_do_pim;
1290 break;
1291#endif
1292 case MRT6_ASSERT:
1293 val = mroute_do_assert;
1294 break;
1059 default: 1295 default:
1060 return -ENOPROTOOPT; 1296 return -ENOPROTOOPT;
1061 } 1297 }
@@ -1151,6 +1387,18 @@ static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
1151 if (vif->dev == NULL) 1387 if (vif->dev == NULL)
1152 goto out_free; 1388 goto out_free;
1153 1389
1390#ifdef CONFIG_IPV6_PIMSM_V2
1391 if (vif->flags & MIFF_REGISTER) {
1392 vif->pkt_out++;
1393 vif->bytes_out += skb->len;
1394 ((struct net_device_stats *)netdev_priv(vif->dev))->tx_bytes += skb->len;
1395 ((struct net_device_stats *)netdev_priv(vif->dev))->tx_packets++;
1396 ip6mr_cache_report(skb, vifi, MRT6MSG_WHOLEPKT);
1397 kfree_skb(skb);
1398 return 0;
1399 }
1400#endif
1401
1154 ipv6h = ipv6_hdr(skb); 1402 ipv6h = ipv6_hdr(skb);
1155 1403
1156 fl = (struct flowi) { 1404 fl = (struct flowi) {
@@ -1220,6 +1468,30 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache)
1220 cache->mfc_un.res.pkt++; 1468 cache->mfc_un.res.pkt++;
1221 cache->mfc_un.res.bytes += skb->len; 1469 cache->mfc_un.res.bytes += skb->len;
1222 1470
1471 /*
1472 * Wrong interface: drop packet and (maybe) send PIM assert.
1473 */
1474 if (vif6_table[vif].dev != skb->dev) {
1475 int true_vifi;
1476
1477 cache->mfc_un.res.wrong_if++;
1478 true_vifi = ip6mr_find_vif(skb->dev);
1479
1480 if (true_vifi >= 0 && mroute_do_assert &&
1481 /* pimsm uses asserts, when switching from RPT to SPT,
1482 so that we cannot check that packet arrived on an oif.
1483 It is bad, but otherwise we would need to move pretty
1484 large chunk of pimd to kernel. Ough... --ANK
1485 */
1486 (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) &&
1487 time_after(jiffies,
1488 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
1489 cache->mfc_un.res.last_assert = jiffies;
1490 ip6mr_cache_report(skb, true_vifi, MRT6MSG_WRONGMIF);
1491 }
1492 goto dont_forward;
1493 }
1494
1223 vif6_table[vif].pkt_in++; 1495 vif6_table[vif].pkt_in++;
1224 vif6_table[vif].bytes_in += skb->len; 1496 vif6_table[vif].bytes_in += skb->len;
1225 1497
@@ -1241,6 +1513,7 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache)
1241 return 0; 1513 return 0;
1242 } 1514 }
1243 1515
1516dont_forward:
1244 kfree_skb(skb); 1517 kfree_skb(skb);
1245 return 0; 1518 return 0;
1246} 1519}