diff options
-rw-r--r-- | include/linux/mroute6.h | 4 | ||||
-rw-r--r-- | net/ipv6/Kconfig | 7 | ||||
-rw-r--r-- | net/ipv6/ip6mr.c | 275 |
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 | ||
218 | struct mrt6msg { | 220 | struct 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 | ||
219 | config 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 | ||
79 | static int mroute_do_assert; /* Set in PIM assert */ | ||
80 | #ifdef CONFIG_IPV6_PIMSM_V2 | ||
81 | static int mroute_do_pim; | ||
82 | #else | ||
83 | #define mroute_do_pim 0 | ||
84 | #endif | ||
85 | |||
78 | static struct mfc6_cache *mfc6_cache_array[MFC_LINES]; /* Forwarding cache */ | 86 | static struct mfc6_cache *mfc6_cache_array[MFC_LINES]; /* Forwarding cache */ |
79 | 87 | ||
80 | static struct mfc6_cache *mfc_unres_queue; /* Queue of unresolved entries */ | 88 | static 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); | |||
97 | static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert); | 105 | static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert); |
98 | static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); | 106 | static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm); |
99 | 107 | ||
108 | #ifdef CONFIG_IPV6_PIMSM_V2 | ||
109 | static struct inet6_protocol pim6_protocol; | ||
110 | #endif | ||
111 | |||
100 | static struct timer_list ipmr_expire_timer; | 112 | static 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 | ||
355 | static int reg_vif_num = -1; | ||
356 | |||
357 | static 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 | |||
412 | static struct inet6_protocol pim6_protocol = { | ||
413 | .handler = pim6_rcv, | ||
414 | }; | ||
415 | |||
416 | /* Service routines creating virtual interfaces: PIMREG */ | ||
417 | |||
418 | static 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 | |||
429 | static struct net_device_stats *reg_vif_get_stats(struct net_device *dev) | ||
430 | { | ||
431 | return (struct net_device_stats *)netdev_priv(dev); | ||
432 | } | ||
433 | |||
434 | static 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 | |||
444 | static 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 | |||
470 | failure: | ||
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 | ||
1516 | dont_forward: | ||
1244 | kfree_skb(skb); | 1517 | kfree_skb(skb); |
1245 | return 0; | 1518 | return 0; |
1246 | } | 1519 | } |