diff options
| author | Alexander Aring <alex.aring@gmail.com> | 2014-02-28 01:32:50 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-02-28 17:05:22 -0500 |
| commit | 7240cdec60b136f3e64a453c7fbded4ed1aa047e (patch) | |
| tree | 1ef358c776a1493ba1d2e6786bf070b05e960c48 | |
| parent | 633fc86ff621bba79dcddfd4c67fb07ae5f8467c (diff) | |
6lowpan: handling 6lowpan fragmentation via inet_frag api
This patch drops the current way of 6lowpan fragmentation on receiving
side and replace it with a implementation which use the inet_frag api.
The old fragmentation handling has some race conditions and isn't
rfc4944 compatible. Also adding support to match fragments on
destination address, source address, tag value and datagram_size
which is missing in the current implementation.
Signed-off-by: Alexander Aring <alex.aring@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/net/netns/ieee802154_6lowpan.h | 9 | ||||
| -rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 256 | ||||
| -rw-r--r-- | net/ieee802154/Makefile | 2 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.c | 564 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.h | 66 |
5 files changed, 689 insertions, 208 deletions
diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h index 88110b7e2428..079030c853d8 100644 --- a/include/net/netns/ieee802154_6lowpan.h +++ b/include/net/netns/ieee802154_6lowpan.h | |||
| @@ -7,7 +7,16 @@ | |||
| 7 | #ifndef __NETNS_IEEE802154_6LOWPAN_H__ | 7 | #ifndef __NETNS_IEEE802154_6LOWPAN_H__ |
| 8 | #define __NETNS_IEEE802154_6LOWPAN_H__ | 8 | #define __NETNS_IEEE802154_6LOWPAN_H__ |
| 9 | 9 | ||
| 10 | struct netns_sysctl_lowpan { | ||
| 11 | #ifdef CONFIG_SYSCTL | ||
| 12 | struct ctl_table_header *frags_hdr; | ||
| 13 | #endif | ||
| 14 | }; | ||
| 15 | |||
| 10 | struct netns_ieee802154_lowpan { | 16 | struct netns_ieee802154_lowpan { |
| 17 | struct netns_sysctl_lowpan sysctl; | ||
| 18 | struct netns_frags frags; | ||
| 19 | u16 max_dsize; | ||
| 11 | }; | 20 | }; |
| 12 | 21 | ||
| 13 | #endif | 22 | #endif |
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index f9c954824ddb..c7bd8b55f7ce 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c | |||
| @@ -54,6 +54,7 @@ | |||
| 54 | #include <net/ieee802154_netdev.h> | 54 | #include <net/ieee802154_netdev.h> |
| 55 | #include <net/ipv6.h> | 55 | #include <net/ipv6.h> |
| 56 | 56 | ||
| 57 | #include "reassembly.h" | ||
| 57 | #include "6lowpan.h" | 58 | #include "6lowpan.h" |
| 58 | 59 | ||
| 59 | static LIST_HEAD(lowpan_devices); | 60 | static LIST_HEAD(lowpan_devices); |
| @@ -70,18 +71,6 @@ struct lowpan_dev_record { | |||
| 70 | struct list_head list; | 71 | struct list_head list; |
| 71 | }; | 72 | }; |
| 72 | 73 | ||
| 73 | struct lowpan_fragment { | ||
| 74 | struct sk_buff *skb; /* skb to be assembled */ | ||
| 75 | u16 length; /* length to be assemled */ | ||
| 76 | u32 bytes_rcv; /* bytes received */ | ||
| 77 | u16 tag; /* current fragment tag */ | ||
| 78 | struct timer_list timer; /* assembling timer */ | ||
| 79 | struct list_head list; /* fragments list */ | ||
| 80 | }; | ||
| 81 | |||
| 82 | static LIST_HEAD(lowpan_fragments); | ||
| 83 | static DEFINE_SPINLOCK(flist_lock); | ||
| 84 | |||
| 85 | static inline struct | 74 | static inline struct |
| 86 | lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) | 75 | lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) |
| 87 | { | 76 | { |
| @@ -179,69 +168,6 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, | |||
| 179 | return stat; | 168 | return stat; |
| 180 | } | 169 | } |
| 181 | 170 | ||
| 182 | static void lowpan_fragment_timer_expired(unsigned long entry_addr) | ||
| 183 | { | ||
| 184 | struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr; | ||
| 185 | |||
| 186 | pr_debug("timer expired for frame with tag %d\n", entry->tag); | ||
| 187 | |||
| 188 | list_del(&entry->list); | ||
| 189 | dev_kfree_skb(entry->skb); | ||
| 190 | kfree(entry); | ||
| 191 | } | ||
| 192 | |||
| 193 | static struct lowpan_fragment * | ||
| 194 | lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag) | ||
| 195 | { | ||
| 196 | struct lowpan_fragment *frame; | ||
| 197 | |||
| 198 | frame = kzalloc(sizeof(struct lowpan_fragment), | ||
| 199 | GFP_ATOMIC); | ||
| 200 | if (!frame) | ||
| 201 | goto frame_err; | ||
| 202 | |||
| 203 | INIT_LIST_HEAD(&frame->list); | ||
| 204 | |||
| 205 | frame->length = len; | ||
| 206 | frame->tag = tag; | ||
| 207 | |||
| 208 | /* allocate buffer for frame assembling */ | ||
| 209 | frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length + | ||
| 210 | sizeof(struct ipv6hdr)); | ||
| 211 | |||
| 212 | if (!frame->skb) | ||
| 213 | goto skb_err; | ||
| 214 | |||
| 215 | frame->skb->priority = skb->priority; | ||
| 216 | |||
| 217 | /* reserve headroom for uncompressed ipv6 header */ | ||
| 218 | skb_reserve(frame->skb, sizeof(struct ipv6hdr)); | ||
| 219 | skb_put(frame->skb, frame->length); | ||
| 220 | |||
| 221 | /* copy the first control block to keep a | ||
| 222 | * trace of the link-layer addresses in case | ||
| 223 | * of a link-local compressed address | ||
| 224 | */ | ||
| 225 | memcpy(frame->skb->cb, skb->cb, sizeof(skb->cb)); | ||
| 226 | |||
| 227 | init_timer(&frame->timer); | ||
| 228 | /* time out is the same as for ipv6 - 60 sec */ | ||
| 229 | frame->timer.expires = jiffies + LOWPAN_FRAG_TIMEOUT; | ||
| 230 | frame->timer.data = (unsigned long)frame; | ||
| 231 | frame->timer.function = lowpan_fragment_timer_expired; | ||
| 232 | |||
| 233 | add_timer(&frame->timer); | ||
| 234 | |||
| 235 | list_add_tail(&frame->list, &lowpan_fragments); | ||
| 236 | |||
| 237 | return frame; | ||
| 238 | |||
| 239 | skb_err: | ||
| 240 | kfree(frame); | ||
| 241 | frame_err: | ||
| 242 | return NULL; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int process_data(struct sk_buff *skb) | 171 | static int process_data(struct sk_buff *skb) |
| 246 | { | 172 | { |
| 247 | u8 iphc0, iphc1; | 173 | u8 iphc0, iphc1; |
| @@ -255,94 +181,6 @@ static int process_data(struct sk_buff *skb) | |||
| 255 | if (lowpan_fetch_skb_u8(skb, &iphc0)) | 181 | if (lowpan_fetch_skb_u8(skb, &iphc0)) |
| 256 | goto drop; | 182 | goto drop; |
| 257 | 183 | ||
| 258 | /* fragments assembling */ | ||
| 259 | switch (iphc0 & LOWPAN_DISPATCH_MASK) { | ||
| 260 | case LOWPAN_DISPATCH_FRAG1: | ||
| 261 | case LOWPAN_DISPATCH_FRAGN: | ||
| 262 | { | ||
| 263 | struct lowpan_fragment *frame; | ||
| 264 | /* slen stores the rightmost 8 bits of the 11 bits length */ | ||
| 265 | u8 slen, offset = 0; | ||
| 266 | u16 len, tag; | ||
| 267 | bool found = false; | ||
| 268 | |||
| 269 | if (lowpan_fetch_skb_u8(skb, &slen) || /* frame length */ | ||
| 270 | lowpan_fetch_skb_u16(skb, &tag)) /* fragment tag */ | ||
| 271 | goto drop; | ||
| 272 | |||
| 273 | /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */ | ||
| 274 | len = ((iphc0 & 7) << 8) | slen; | ||
| 275 | |||
| 276 | if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) { | ||
| 277 | pr_debug("%s received a FRAG1 packet (tag: %d, " | ||
| 278 | "size of the entire IP packet: %d)", | ||
| 279 | __func__, tag, len); | ||
| 280 | } else { /* FRAGN */ | ||
| 281 | if (lowpan_fetch_skb_u8(skb, &offset)) | ||
| 282 | goto unlock_and_drop; | ||
| 283 | pr_debug("%s received a FRAGN packet (tag: %d, " | ||
| 284 | "size of the entire IP packet: %d, " | ||
| 285 | "offset: %d)", __func__, tag, len, offset * 8); | ||
| 286 | } | ||
| 287 | |||
| 288 | /* | ||
| 289 | * check if frame assembling with the same tag is | ||
| 290 | * already in progress | ||
| 291 | */ | ||
| 292 | spin_lock_bh(&flist_lock); | ||
| 293 | |||
| 294 | list_for_each_entry(frame, &lowpan_fragments, list) | ||
| 295 | if (frame->tag == tag) { | ||
| 296 | found = true; | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* alloc new frame structure */ | ||
| 301 | if (!found) { | ||
| 302 | pr_debug("%s first fragment received for tag %d, " | ||
| 303 | "begin packet reassembly", __func__, tag); | ||
| 304 | frame = lowpan_alloc_new_frame(skb, len, tag); | ||
| 305 | if (!frame) | ||
| 306 | goto unlock_and_drop; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* if payload fits buffer, copy it */ | ||
| 310 | if (likely((offset * 8 + skb->len) <= frame->length)) | ||
| 311 | skb_copy_to_linear_data_offset(frame->skb, offset * 8, | ||
| 312 | skb->data, skb->len); | ||
| 313 | else | ||
| 314 | goto unlock_and_drop; | ||
| 315 | |||
| 316 | frame->bytes_rcv += skb->len; | ||
| 317 | |||
| 318 | /* frame assembling complete */ | ||
| 319 | if ((frame->bytes_rcv == frame->length) && | ||
| 320 | frame->timer.expires > jiffies) { | ||
| 321 | /* if timer haven't expired - first of all delete it */ | ||
| 322 | del_timer_sync(&frame->timer); | ||
| 323 | list_del(&frame->list); | ||
| 324 | spin_unlock_bh(&flist_lock); | ||
| 325 | |||
| 326 | pr_debug("%s successfully reassembled fragment " | ||
| 327 | "(tag %d)", __func__, tag); | ||
| 328 | |||
| 329 | dev_kfree_skb(skb); | ||
| 330 | skb = frame->skb; | ||
| 331 | kfree(frame); | ||
| 332 | |||
| 333 | if (lowpan_fetch_skb_u8(skb, &iphc0)) | ||
| 334 | goto drop; | ||
| 335 | |||
| 336 | break; | ||
| 337 | } | ||
| 338 | spin_unlock_bh(&flist_lock); | ||
| 339 | |||
| 340 | return kfree_skb(skb), 0; | ||
| 341 | } | ||
| 342 | default: | ||
| 343 | break; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (lowpan_fetch_skb_u8(skb, &iphc1)) | 184 | if (lowpan_fetch_skb_u8(skb, &iphc1)) |
| 347 | goto drop; | 185 | goto drop; |
| 348 | 186 | ||
| @@ -355,8 +193,6 @@ static int process_data(struct sk_buff *skb) | |||
| 355 | IEEE802154_ADDR_LEN, iphc0, iphc1, | 193 | IEEE802154_ADDR_LEN, iphc0, iphc1, |
| 356 | lowpan_give_skb_to_devices); | 194 | lowpan_give_skb_to_devices); |
| 357 | 195 | ||
| 358 | unlock_and_drop: | ||
| 359 | spin_unlock_bh(&flist_lock); | ||
| 360 | drop: | 196 | drop: |
| 361 | kfree_skb(skb); | 197 | kfree_skb(skb); |
| 362 | return -EINVAL; | 198 | return -EINVAL; |
| @@ -603,44 +439,53 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, | |||
| 603 | struct packet_type *pt, struct net_device *orig_dev) | 439 | struct packet_type *pt, struct net_device *orig_dev) |
| 604 | { | 440 | { |
| 605 | struct sk_buff *local_skb; | 441 | struct sk_buff *local_skb; |
| 442 | int ret; | ||
| 606 | 443 | ||
| 607 | if (!netif_running(dev)) | 444 | if (!netif_running(dev)) |
| 608 | goto drop; | 445 | goto drop_skb; |
| 609 | 446 | ||
| 610 | if (dev->type != ARPHRD_IEEE802154) | 447 | if (dev->type != ARPHRD_IEEE802154) |
| 611 | goto drop; | 448 | goto drop_skb; |
| 449 | |||
| 450 | local_skb = skb_clone(skb, GFP_ATOMIC); | ||
| 451 | if (!local_skb) | ||
| 452 | goto drop_skb; | ||
| 453 | |||
| 454 | kfree_skb(skb); | ||
| 612 | 455 | ||
| 613 | /* check that it's our buffer */ | 456 | /* check that it's our buffer */ |
| 614 | if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { | 457 | if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { |
| 615 | /* Copy the packet so that the IPv6 header is | ||
| 616 | * properly aligned. | ||
| 617 | */ | ||
| 618 | local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1, | ||
| 619 | skb_tailroom(skb), GFP_ATOMIC); | ||
| 620 | if (!local_skb) | ||
| 621 | goto drop; | ||
| 622 | |||
| 623 | local_skb->protocol = htons(ETH_P_IPV6); | 458 | local_skb->protocol = htons(ETH_P_IPV6); |
| 624 | local_skb->pkt_type = PACKET_HOST; | 459 | local_skb->pkt_type = PACKET_HOST; |
| 625 | 460 | ||
| 626 | /* Pull off the 1-byte of 6lowpan header. */ | 461 | /* Pull off the 1-byte of 6lowpan header. */ |
| 627 | skb_pull(local_skb, 1); | 462 | skb_pull(local_skb, 1); |
| 628 | 463 | ||
| 629 | lowpan_give_skb_to_devices(local_skb, NULL); | 464 | ret = lowpan_give_skb_to_devices(local_skb, NULL); |
| 630 | 465 | if (ret == NET_RX_DROP) | |
| 631 | kfree_skb(local_skb); | 466 | goto drop; |
| 632 | kfree_skb(skb); | ||
| 633 | } else { | 467 | } else { |
| 634 | switch (skb->data[0] & 0xe0) { | 468 | switch (skb->data[0] & 0xe0) { |
| 635 | case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ | 469 | case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ |
| 470 | ret = process_data(local_skb); | ||
| 471 | if (ret == NET_RX_DROP) | ||
| 472 | goto drop; | ||
| 473 | break; | ||
| 636 | case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ | 474 | case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ |
| 475 | ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAG1); | ||
| 476 | if (ret == 1) { | ||
| 477 | ret = process_data(local_skb); | ||
| 478 | if (ret == NET_RX_DROP) | ||
| 479 | goto drop; | ||
| 480 | } | ||
| 481 | break; | ||
| 637 | case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ | 482 | case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ |
| 638 | local_skb = skb_clone(skb, GFP_ATOMIC); | 483 | ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAGN); |
| 639 | if (!local_skb) | 484 | if (ret == 1) { |
| 640 | goto drop; | 485 | ret = process_data(local_skb); |
| 641 | process_data(local_skb); | 486 | if (ret == NET_RX_DROP) |
| 642 | 487 | goto drop; | |
| 643 | kfree_skb(skb); | 488 | } |
| 644 | break; | 489 | break; |
| 645 | default: | 490 | default: |
| 646 | break; | 491 | break; |
| @@ -648,9 +493,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, | |||
| 648 | } | 493 | } |
| 649 | 494 | ||
| 650 | return NET_RX_SUCCESS; | 495 | return NET_RX_SUCCESS; |
| 651 | 496 | drop_skb: | |
| 652 | drop: | ||
| 653 | kfree_skb(skb); | 497 | kfree_skb(skb); |
| 498 | drop: | ||
| 654 | return NET_RX_DROP; | 499 | return NET_RX_DROP; |
| 655 | } | 500 | } |
| 656 | 501 | ||
| @@ -778,43 +623,40 @@ static int __init lowpan_init_module(void) | |||
| 778 | { | 623 | { |
| 779 | int err = 0; | 624 | int err = 0; |
| 780 | 625 | ||
| 781 | err = lowpan_netlink_init(); | 626 | err = lowpan_net_frag_init(); |
| 782 | if (err < 0) | 627 | if (err < 0) |
| 783 | goto out; | 628 | goto out; |
| 784 | 629 | ||
| 630 | err = lowpan_netlink_init(); | ||
| 631 | if (err < 0) | ||
| 632 | goto out_frag; | ||
| 633 | |||
| 785 | dev_add_pack(&lowpan_packet_type); | 634 | dev_add_pack(&lowpan_packet_type); |
| 786 | 635 | ||
| 787 | err = register_netdevice_notifier(&lowpan_dev_notifier); | 636 | err = register_netdevice_notifier(&lowpan_dev_notifier); |
| 788 | if (err < 0) { | 637 | if (err < 0) |
| 789 | dev_remove_pack(&lowpan_packet_type); | 638 | goto out_pack; |
| 790 | lowpan_netlink_fini(); | 639 | |
| 791 | } | 640 | return 0; |
| 641 | |||
| 642 | out_pack: | ||
| 643 | dev_remove_pack(&lowpan_packet_type); | ||
| 644 | lowpan_netlink_fini(); | ||
| 645 | out_frag: | ||
| 646 | lowpan_net_frag_exit(); | ||
| 792 | out: | 647 | out: |
| 793 | return err; | 648 | return err; |
| 794 | } | 649 | } |
| 795 | 650 | ||
| 796 | static void __exit lowpan_cleanup_module(void) | 651 | static void __exit lowpan_cleanup_module(void) |
| 797 | { | 652 | { |
| 798 | struct lowpan_fragment *frame, *tframe; | ||
| 799 | |||
| 800 | lowpan_netlink_fini(); | 653 | lowpan_netlink_fini(); |
| 801 | 654 | ||
| 802 | dev_remove_pack(&lowpan_packet_type); | 655 | dev_remove_pack(&lowpan_packet_type); |
| 803 | 656 | ||
| 804 | unregister_netdevice_notifier(&lowpan_dev_notifier); | 657 | lowpan_net_frag_exit(); |
| 805 | 658 | ||
| 806 | /* Now 6lowpan packet_type is removed, so no new fragments are | 659 | unregister_netdevice_notifier(&lowpan_dev_notifier); |
| 807 | * expected on RX, therefore that's the time to clean incomplete | ||
| 808 | * fragments. | ||
| 809 | */ | ||
| 810 | spin_lock_bh(&flist_lock); | ||
| 811 | list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { | ||
| 812 | del_timer_sync(&frame->timer); | ||
| 813 | list_del(&frame->list); | ||
| 814 | dev_kfree_skb(frame->skb); | ||
| 815 | kfree(frame); | ||
| 816 | } | ||
| 817 | spin_unlock_bh(&flist_lock); | ||
| 818 | } | 660 | } |
| 819 | 661 | ||
| 820 | module_init(lowpan_init_module); | 662 | module_init(lowpan_init_module); |
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 3d08adfcd175..b113fc4be3e0 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile | |||
| @@ -2,6 +2,6 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o | |||
| 2 | obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o | 2 | obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o |
| 3 | obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o | 3 | obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o |
| 4 | 4 | ||
| 5 | 6lowpan-y := 6lowpan_rtnl.o | 5 | 6lowpan-y := 6lowpan_rtnl.o reassembly.o |
| 6 | ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o | 6 | ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o |
| 7 | af_802154-y := af_ieee802154.o raw.o dgram.o | 7 | af_802154-y := af_ieee802154.o raw.o dgram.o |
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c new file mode 100644 index 000000000000..eb5995e74316 --- /dev/null +++ b/net/ieee802154/reassembly.c | |||
| @@ -0,0 +1,564 @@ | |||
| 1 | /* 6LoWPAN fragment reassembly | ||
| 2 | * | ||
| 3 | * | ||
| 4 | * Authors: | ||
| 5 | * Alexander Aring <aar@pengutronix.de> | ||
| 6 | * | ||
| 7 | * Based on: net/ipv6/reassembly.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License | ||
| 11 | * as published by the Free Software Foundation; either version | ||
| 12 | * 2 of the License, or (at your option) any later version. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #define pr_fmt(fmt) "6LoWPAN: " fmt | ||
| 16 | |||
| 17 | #include <linux/net.h> | ||
| 18 | #include <linux/list.h> | ||
| 19 | #include <linux/netdevice.h> | ||
| 20 | #include <linux/random.h> | ||
| 21 | #include <linux/jhash.h> | ||
| 22 | #include <linux/skbuff.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/export.h> | ||
| 25 | |||
| 26 | #include <net/ieee802154_netdev.h> | ||
| 27 | #include <net/ipv6.h> | ||
| 28 | #include <net/inet_frag.h> | ||
| 29 | |||
| 30 | #include "6lowpan.h" | ||
| 31 | #include "reassembly.h" | ||
| 32 | |||
| 33 | static struct inet_frags lowpan_frags; | ||
| 34 | |||
| 35 | static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, | ||
| 36 | struct sk_buff *prev, struct net_device *dev); | ||
| 37 | |||
| 38 | static unsigned int lowpan_hash_frag(__be16 tag, __be16 d_size, | ||
| 39 | const struct ieee802154_addr *saddr, | ||
| 40 | const struct ieee802154_addr *daddr) | ||
| 41 | { | ||
| 42 | u32 c; | ||
| 43 | |||
| 44 | net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd)); | ||
| 45 | c = jhash_3words(ieee802154_addr_hash(saddr), | ||
| 46 | ieee802154_addr_hash(daddr), | ||
| 47 | (__force u32)(tag + (d_size << 16)), | ||
| 48 | lowpan_frags.rnd); | ||
| 49 | |||
| 50 | return c & (INETFRAGS_HASHSZ - 1); | ||
| 51 | } | ||
| 52 | |||
| 53 | static unsigned int lowpan_hashfn(struct inet_frag_queue *q) | ||
| 54 | { | ||
| 55 | struct lowpan_frag_queue *fq; | ||
| 56 | |||
| 57 | fq = container_of(q, struct lowpan_frag_queue, q); | ||
| 58 | return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); | ||
| 59 | } | ||
| 60 | |||
| 61 | bool lowpan_frag_match(struct inet_frag_queue *q, void *a) | ||
| 62 | { | ||
| 63 | struct lowpan_frag_queue *fq; | ||
| 64 | struct lowpan_create_arg *arg = a; | ||
| 65 | |||
| 66 | fq = container_of(q, struct lowpan_frag_queue, q); | ||
| 67 | return fq->tag == arg->tag && fq->d_size == arg->d_size && | ||
| 68 | ieee802154_addr_addr_equal(&fq->saddr, arg->src) && | ||
| 69 | ieee802154_addr_addr_equal(&fq->daddr, arg->dst); | ||
| 70 | } | ||
| 71 | EXPORT_SYMBOL(lowpan_frag_match); | ||
| 72 | |||
| 73 | void lowpan_frag_init(struct inet_frag_queue *q, void *a) | ||
| 74 | { | ||
| 75 | struct lowpan_frag_queue *fq; | ||
| 76 | struct lowpan_create_arg *arg = a; | ||
| 77 | |||
| 78 | fq = container_of(q, struct lowpan_frag_queue, q); | ||
| 79 | |||
| 80 | fq->tag = arg->tag; | ||
| 81 | fq->d_size = arg->d_size; | ||
| 82 | fq->saddr = *arg->src; | ||
| 83 | fq->daddr = *arg->dst; | ||
| 84 | } | ||
| 85 | EXPORT_SYMBOL(lowpan_frag_init); | ||
| 86 | |||
| 87 | void lowpan_expire_frag_queue(struct frag_queue *fq, struct inet_frags *frags) | ||
| 88 | { | ||
| 89 | spin_lock(&fq->q.lock); | ||
| 90 | |||
| 91 | if (fq->q.last_in & INET_FRAG_COMPLETE) | ||
| 92 | goto out; | ||
| 93 | |||
| 94 | inet_frag_kill(&fq->q, frags); | ||
| 95 | out: | ||
| 96 | spin_unlock(&fq->q.lock); | ||
| 97 | inet_frag_put(&fq->q, frags); | ||
| 98 | } | ||
| 99 | EXPORT_SYMBOL(lowpan_expire_frag_queue); | ||
| 100 | |||
| 101 | static void lowpan_frag_expire(unsigned long data) | ||
| 102 | { | ||
| 103 | struct frag_queue *fq; | ||
| 104 | struct net *net; | ||
| 105 | |||
| 106 | fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); | ||
| 107 | net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); | ||
| 108 | |||
| 109 | lowpan_expire_frag_queue(fq, &lowpan_frags); | ||
| 110 | } | ||
| 111 | |||
| 112 | static inline struct lowpan_frag_queue * | ||
| 113 | fq_find(struct net *net, const struct ieee802154_frag_info *frag_info, | ||
| 114 | const struct ieee802154_addr *src, const struct ieee802154_addr *dst) | ||
| 115 | { | ||
| 116 | struct inet_frag_queue *q; | ||
| 117 | struct lowpan_create_arg arg; | ||
| 118 | unsigned int hash; | ||
| 119 | |||
| 120 | arg.tag = frag_info->d_tag; | ||
| 121 | arg.d_size = frag_info->d_size; | ||
| 122 | arg.src = src; | ||
| 123 | arg.dst = dst; | ||
| 124 | |||
| 125 | read_lock(&lowpan_frags.lock); | ||
| 126 | hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); | ||
| 127 | |||
| 128 | q = inet_frag_find(&net->ieee802154_lowpan.frags, | ||
| 129 | &lowpan_frags, &arg, hash); | ||
| 130 | if (IS_ERR_OR_NULL(q)) { | ||
| 131 | inet_frag_maybe_warn_overflow(q, pr_fmt()); | ||
| 132 | return NULL; | ||
| 133 | } | ||
| 134 | return container_of(q, struct lowpan_frag_queue, q); | ||
| 135 | } | ||
| 136 | |||
| 137 | static int lowpan_frag_queue(struct lowpan_frag_queue *fq, | ||
| 138 | struct sk_buff *skb, const u8 frag_type) | ||
| 139 | { | ||
| 140 | struct sk_buff *prev, *next; | ||
| 141 | struct net_device *dev; | ||
| 142 | int end, offset; | ||
| 143 | |||
| 144 | if (fq->q.last_in & INET_FRAG_COMPLETE) | ||
| 145 | goto err; | ||
| 146 | |||
| 147 | offset = mac_cb(skb)->frag_info.d_offset << 3; | ||
| 148 | end = mac_cb(skb)->frag_info.d_size; | ||
| 149 | |||
| 150 | /* Is this the final fragment? */ | ||
| 151 | if (offset + skb->len == end) { | ||
| 152 | /* If we already have some bits beyond end | ||
| 153 | * or have different end, the segment is corrupted. | ||
| 154 | */ | ||
| 155 | if (end < fq->q.len || | ||
| 156 | ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) | ||
| 157 | goto err; | ||
| 158 | fq->q.last_in |= INET_FRAG_LAST_IN; | ||
| 159 | fq->q.len = end; | ||
| 160 | } else { | ||
| 161 | if (end > fq->q.len) { | ||
| 162 | /* Some bits beyond end -> corruption. */ | ||
| 163 | if (fq->q.last_in & INET_FRAG_LAST_IN) | ||
| 164 | goto err; | ||
| 165 | fq->q.len = end; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Find out which fragments are in front and at the back of us | ||
| 170 | * in the chain of fragments so far. We must know where to put | ||
| 171 | * this fragment, right? | ||
| 172 | */ | ||
| 173 | prev = fq->q.fragments_tail; | ||
| 174 | if (!prev || mac_cb(prev)->frag_info.d_offset < | ||
| 175 | mac_cb(skb)->frag_info.d_offset) { | ||
| 176 | next = NULL; | ||
| 177 | goto found; | ||
| 178 | } | ||
| 179 | prev = NULL; | ||
| 180 | for (next = fq->q.fragments; next != NULL; next = next->next) { | ||
| 181 | if (mac_cb(next)->frag_info.d_offset >= | ||
| 182 | mac_cb(skb)->frag_info.d_offset) | ||
| 183 | break; /* bingo! */ | ||
| 184 | prev = next; | ||
| 185 | } | ||
| 186 | |||
| 187 | found: | ||
| 188 | /* Insert this fragment in the chain of fragments. */ | ||
| 189 | skb->next = next; | ||
| 190 | if (!next) | ||
| 191 | fq->q.fragments_tail = skb; | ||
| 192 | if (prev) | ||
| 193 | prev->next = skb; | ||
| 194 | else | ||
| 195 | fq->q.fragments = skb; | ||
| 196 | |||
| 197 | dev = skb->dev; | ||
| 198 | if (dev) | ||
| 199 | skb->dev = NULL; | ||
| 200 | |||
| 201 | fq->q.stamp = skb->tstamp; | ||
| 202 | if (frag_type == LOWPAN_DISPATCH_FRAG1) { | ||
| 203 | /* Calculate uncomp. 6lowpan header to estimate full size */ | ||
| 204 | fq->q.meat += lowpan_uncompress_size(skb, NULL); | ||
| 205 | fq->q.last_in |= INET_FRAG_FIRST_IN; | ||
| 206 | } else { | ||
| 207 | fq->q.meat += skb->len; | ||
| 208 | } | ||
| 209 | add_frag_mem_limit(&fq->q, skb->truesize); | ||
| 210 | |||
| 211 | if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && | ||
| 212 | fq->q.meat == fq->q.len) { | ||
| 213 | int res; | ||
| 214 | unsigned long orefdst = skb->_skb_refdst; | ||
| 215 | |||
| 216 | skb->_skb_refdst = 0UL; | ||
| 217 | res = lowpan_frag_reasm(fq, prev, dev); | ||
| 218 | skb->_skb_refdst = orefdst; | ||
| 219 | return res; | ||
| 220 | } | ||
| 221 | |||
| 222 | inet_frag_lru_move(&fq->q); | ||
| 223 | return -1; | ||
| 224 | err: | ||
| 225 | kfree_skb(skb); | ||
| 226 | return -1; | ||
| 227 | } | ||
| 228 | |||
| 229 | /* Check if this packet is complete. | ||
| 230 | * Returns NULL on failure by any reason, and pointer | ||
| 231 | * to current nexthdr field in reassembled frame. | ||
| 232 | * | ||
| 233 | * It is called with locked fq, and caller must check that | ||
| 234 | * queue is eligible for reassembly i.e. it is not COMPLETE, | ||
| 235 | * the last and the first frames arrived and all the bits are here. | ||
| 236 | */ | ||
| 237 | static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, | ||
| 238 | struct net_device *dev) | ||
| 239 | { | ||
| 240 | struct sk_buff *fp, *head = fq->q.fragments; | ||
| 241 | int sum_truesize; | ||
| 242 | |||
| 243 | inet_frag_kill(&fq->q, &lowpan_frags); | ||
| 244 | |||
| 245 | /* Make the one we just received the head. */ | ||
| 246 | if (prev) { | ||
| 247 | head = prev->next; | ||
| 248 | fp = skb_clone(head, GFP_ATOMIC); | ||
| 249 | |||
| 250 | if (!fp) | ||
| 251 | goto out_oom; | ||
| 252 | |||
| 253 | fp->next = head->next; | ||
| 254 | if (!fp->next) | ||
| 255 | fq->q.fragments_tail = fp; | ||
| 256 | prev->next = fp; | ||
| 257 | |||
| 258 | skb_morph(head, fq->q.fragments); | ||
| 259 | head->next = fq->q.fragments->next; | ||
| 260 | |||
| 261 | consume_skb(fq->q.fragments); | ||
| 262 | fq->q.fragments = head; | ||
| 263 | } | ||
| 264 | |||
| 265 | /* Head of list must not be cloned. */ | ||
| 266 | if (skb_unclone(head, GFP_ATOMIC)) | ||
| 267 | goto out_oom; | ||
| 268 | |||
| 269 | /* If the first fragment is fragmented itself, we split | ||
| 270 | * it to two chunks: the first with data and paged part | ||
| 271 | * and the second, holding only fragments. | ||
| 272 | */ | ||
| 273 | if (skb_has_frag_list(head)) { | ||
| 274 | struct sk_buff *clone; | ||
| 275 | int i, plen = 0; | ||
| 276 | |||
| 277 | clone = alloc_skb(0, GFP_ATOMIC); | ||
| 278 | if (!clone) | ||
| 279 | goto out_oom; | ||
| 280 | clone->next = head->next; | ||
| 281 | head->next = clone; | ||
| 282 | skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; | ||
| 283 | skb_frag_list_init(head); | ||
| 284 | for (i = 0; i < skb_shinfo(head)->nr_frags; i++) | ||
| 285 | plen += skb_frag_size(&skb_shinfo(head)->frags[i]); | ||
| 286 | clone->len = head->data_len - plen; | ||
| 287 | clone->data_len = clone->len; | ||
| 288 | head->data_len -= clone->len; | ||
| 289 | head->len -= clone->len; | ||
| 290 | add_frag_mem_limit(&fq->q, clone->truesize); | ||
| 291 | } | ||
| 292 | |||
| 293 | WARN_ON(head == NULL); | ||
| 294 | |||
| 295 | sum_truesize = head->truesize; | ||
| 296 | for (fp = head->next; fp;) { | ||
| 297 | bool headstolen; | ||
| 298 | int delta; | ||
| 299 | struct sk_buff *next = fp->next; | ||
| 300 | |||
| 301 | sum_truesize += fp->truesize; | ||
| 302 | if (skb_try_coalesce(head, fp, &headstolen, &delta)) { | ||
| 303 | kfree_skb_partial(fp, headstolen); | ||
| 304 | } else { | ||
| 305 | if (!skb_shinfo(head)->frag_list) | ||
| 306 | skb_shinfo(head)->frag_list = fp; | ||
| 307 | head->data_len += fp->len; | ||
| 308 | head->len += fp->len; | ||
| 309 | head->truesize += fp->truesize; | ||
| 310 | } | ||
| 311 | fp = next; | ||
| 312 | } | ||
| 313 | sub_frag_mem_limit(&fq->q, sum_truesize); | ||
| 314 | |||
| 315 | head->next = NULL; | ||
| 316 | head->dev = dev; | ||
| 317 | head->tstamp = fq->q.stamp; | ||
| 318 | |||
| 319 | fq->q.fragments = NULL; | ||
| 320 | fq->q.fragments_tail = NULL; | ||
| 321 | |||
| 322 | return 1; | ||
| 323 | out_oom: | ||
| 324 | net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); | ||
| 325 | return -1; | ||
| 326 | } | ||
| 327 | |||
| 328 | static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, | ||
| 329 | struct ieee802154_frag_info *frag_info) | ||
| 330 | { | ||
| 331 | bool fail; | ||
| 332 | u8 pattern = 0, low = 0; | ||
| 333 | |||
| 334 | fail = lowpan_fetch_skb(skb, &pattern, 1); | ||
| 335 | fail |= lowpan_fetch_skb(skb, &low, 1); | ||
| 336 | frag_info->d_size = (pattern & 7) << 8 | low; | ||
| 337 | fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2); | ||
| 338 | |||
| 339 | if (frag_type == LOWPAN_DISPATCH_FRAGN) { | ||
| 340 | fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); | ||
| 341 | } else { | ||
| 342 | skb_reset_network_header(skb); | ||
| 343 | frag_info->d_offset = 0; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (unlikely(fail)) | ||
| 347 | return -EIO; | ||
| 348 | |||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) | ||
| 353 | { | ||
| 354 | struct lowpan_frag_queue *fq; | ||
| 355 | struct net *net = dev_net(skb->dev); | ||
| 356 | struct ieee802154_frag_info *frag_info = &mac_cb(skb)->frag_info; | ||
| 357 | int err; | ||
| 358 | |||
| 359 | err = lowpan_get_frag_info(skb, frag_type, frag_info); | ||
| 360 | if (err < 0) | ||
| 361 | goto err; | ||
| 362 | |||
| 363 | if (frag_info->d_size > net->ieee802154_lowpan.max_dsize) | ||
| 364 | goto err; | ||
| 365 | |||
| 366 | inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false); | ||
| 367 | |||
| 368 | fq = fq_find(net, frag_info, &mac_cb(skb)->sa, &mac_cb(skb)->da); | ||
| 369 | if (fq != NULL) { | ||
| 370 | int ret; | ||
| 371 | spin_lock(&fq->q.lock); | ||
| 372 | ret = lowpan_frag_queue(fq, skb, frag_type); | ||
| 373 | spin_unlock(&fq->q.lock); | ||
| 374 | |||
| 375 | inet_frag_put(&fq->q, &lowpan_frags); | ||
| 376 | return ret; | ||
| 377 | } | ||
| 378 | |||
| 379 | err: | ||
| 380 | kfree_skb(skb); | ||
| 381 | return -1; | ||
| 382 | } | ||
| 383 | EXPORT_SYMBOL(lowpan_frag_rcv); | ||
| 384 | |||
| 385 | #ifdef CONFIG_SYSCTL | ||
| 386 | static struct ctl_table lowpan_frags_ns_ctl_table[] = { | ||
| 387 | { | ||
| 388 | .procname = "6lowpanfrag_high_thresh", | ||
| 389 | .data = &init_net.ieee802154_lowpan.frags.high_thresh, | ||
| 390 | .maxlen = sizeof(int), | ||
| 391 | .mode = 0644, | ||
| 392 | .proc_handler = proc_dointvec | ||
| 393 | }, | ||
| 394 | { | ||
| 395 | .procname = "6lowpanfrag_low_thresh", | ||
| 396 | .data = &init_net.ieee802154_lowpan.frags.low_thresh, | ||
| 397 | .maxlen = sizeof(int), | ||
| 398 | .mode = 0644, | ||
| 399 | .proc_handler = proc_dointvec | ||
| 400 | }, | ||
| 401 | { | ||
| 402 | .procname = "6lowpanfrag_time", | ||
| 403 | .data = &init_net.ieee802154_lowpan.frags.timeout, | ||
| 404 | .maxlen = sizeof(int), | ||
| 405 | .mode = 0644, | ||
| 406 | .proc_handler = proc_dointvec_jiffies, | ||
| 407 | }, | ||
| 408 | { | ||
| 409 | .procname = "6lowpanfrag_max_datagram_size", | ||
| 410 | .data = &init_net.ieee802154_lowpan.max_dsize, | ||
| 411 | .maxlen = sizeof(int), | ||
| 412 | .mode = 0644, | ||
| 413 | .proc_handler = proc_dointvec | ||
| 414 | }, | ||
| 415 | { } | ||
| 416 | }; | ||
| 417 | |||
| 418 | static struct ctl_table lowpan_frags_ctl_table[] = { | ||
| 419 | { | ||
| 420 | .procname = "6lowpanfrag_secret_interval", | ||
| 421 | .data = &lowpan_frags.secret_interval, | ||
| 422 | .maxlen = sizeof(int), | ||
| 423 | .mode = 0644, | ||
| 424 | .proc_handler = proc_dointvec_jiffies, | ||
| 425 | }, | ||
| 426 | { } | ||
| 427 | }; | ||
| 428 | |||
| 429 | static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) | ||
| 430 | { | ||
| 431 | struct ctl_table *table; | ||
| 432 | struct ctl_table_header *hdr; | ||
| 433 | |||
| 434 | table = lowpan_frags_ns_ctl_table; | ||
| 435 | if (!net_eq(net, &init_net)) { | ||
| 436 | table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), | ||
| 437 | GFP_KERNEL); | ||
| 438 | if (table == NULL) | ||
| 439 | goto err_alloc; | ||
| 440 | |||
| 441 | table[0].data = &net->ieee802154_lowpan.frags.high_thresh; | ||
| 442 | table[1].data = &net->ieee802154_lowpan.frags.low_thresh; | ||
| 443 | table[2].data = &net->ieee802154_lowpan.frags.timeout; | ||
| 444 | table[2].data = &net->ieee802154_lowpan.max_dsize; | ||
| 445 | |||
| 446 | /* Don't export sysctls to unprivileged users */ | ||
| 447 | if (net->user_ns != &init_user_ns) | ||
| 448 | table[0].procname = NULL; | ||
| 449 | } | ||
| 450 | |||
| 451 | hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); | ||
| 452 | if (hdr == NULL) | ||
| 453 | goto err_reg; | ||
| 454 | |||
| 455 | net->ieee802154_lowpan.sysctl.frags_hdr = hdr; | ||
| 456 | return 0; | ||
| 457 | |||
| 458 | err_reg: | ||
| 459 | if (!net_eq(net, &init_net)) | ||
| 460 | kfree(table); | ||
| 461 | err_alloc: | ||
| 462 | return -ENOMEM; | ||
| 463 | } | ||
| 464 | |||
| 465 | static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) | ||
| 466 | { | ||
| 467 | struct ctl_table *table; | ||
| 468 | |||
| 469 | table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg; | ||
| 470 | unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr); | ||
| 471 | if (!net_eq(net, &init_net)) | ||
| 472 | kfree(table); | ||
| 473 | } | ||
| 474 | |||
| 475 | static struct ctl_table_header *lowpan_ctl_header; | ||
| 476 | |||
| 477 | static int lowpan_frags_sysctl_register(void) | ||
| 478 | { | ||
| 479 | lowpan_ctl_header = register_net_sysctl(&init_net, | ||
| 480 | "net/ieee802154/6lowpan", | ||
| 481 | lowpan_frags_ctl_table); | ||
| 482 | return lowpan_ctl_header == NULL ? -ENOMEM : 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | static void lowpan_frags_sysctl_unregister(void) | ||
| 486 | { | ||
| 487 | unregister_net_sysctl_table(lowpan_ctl_header); | ||
| 488 | } | ||
| 489 | #else | ||
| 490 | static inline int lowpan_frags_ns_sysctl_register(struct net *net) | ||
| 491 | { | ||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | static inline void lowpan_frags_ns_sysctl_unregister(struct net *net) | ||
| 496 | { | ||
| 497 | } | ||
| 498 | |||
| 499 | static inline int lowpan_frags_sysctl_register(void) | ||
| 500 | { | ||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | |||
| 504 | static inline void lowpan_frags_sysctl_unregister(void) | ||
| 505 | { | ||
| 506 | } | ||
| 507 | #endif | ||
| 508 | |||
| 509 | static int __net_init lowpan_frags_init_net(struct net *net) | ||
| 510 | { | ||
| 511 | net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; | ||
| 512 | net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH; | ||
| 513 | net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT; | ||
| 514 | net->ieee802154_lowpan.max_dsize = 0xFFFF; | ||
| 515 | |||
| 516 | inet_frags_init_net(&net->ieee802154_lowpan.frags); | ||
| 517 | |||
| 518 | return lowpan_frags_ns_sysctl_register(net); | ||
| 519 | } | ||
| 520 | |||
| 521 | static void __net_exit lowpan_frags_exit_net(struct net *net) | ||
| 522 | { | ||
| 523 | lowpan_frags_ns_sysctl_unregister(net); | ||
| 524 | inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags); | ||
| 525 | } | ||
| 526 | |||
| 527 | static struct pernet_operations lowpan_frags_ops = { | ||
| 528 | .init = lowpan_frags_init_net, | ||
| 529 | .exit = lowpan_frags_exit_net, | ||
| 530 | }; | ||
| 531 | |||
| 532 | int __init lowpan_net_frag_init(void) | ||
| 533 | { | ||
| 534 | int ret; | ||
| 535 | |||
| 536 | ret = lowpan_frags_sysctl_register(); | ||
| 537 | if (ret) | ||
| 538 | goto out; | ||
| 539 | |||
| 540 | ret = register_pernet_subsys(&lowpan_frags_ops); | ||
| 541 | if (ret) | ||
| 542 | goto err_pernet; | ||
| 543 | |||
| 544 | lowpan_frags.hashfn = lowpan_hashfn; | ||
| 545 | lowpan_frags.constructor = lowpan_frag_init; | ||
| 546 | lowpan_frags.destructor = NULL; | ||
| 547 | lowpan_frags.skb_free = NULL; | ||
| 548 | lowpan_frags.qsize = sizeof(struct frag_queue); | ||
| 549 | lowpan_frags.match = lowpan_frag_match; | ||
| 550 | lowpan_frags.frag_expire = lowpan_frag_expire; | ||
| 551 | lowpan_frags.secret_interval = 10 * 60 * HZ; | ||
| 552 | inet_frags_init(&lowpan_frags); | ||
| 553 | err_pernet: | ||
| 554 | lowpan_frags_sysctl_unregister(); | ||
| 555 | out: | ||
| 556 | return ret; | ||
| 557 | } | ||
| 558 | |||
| 559 | void lowpan_net_frag_exit(void) | ||
| 560 | { | ||
| 561 | inet_frags_fini(&lowpan_frags); | ||
| 562 | lowpan_frags_sysctl_unregister(); | ||
| 563 | unregister_pernet_subsys(&lowpan_frags_ops); | ||
| 564 | } | ||
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h new file mode 100644 index 000000000000..055518b9da2d --- /dev/null +++ b/net/ieee802154/reassembly.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ | ||
| 2 | #define __IEEE802154_6LOWPAN_REASSEMBLY_H__ | ||
| 3 | |||
| 4 | #include <net/inet_frag.h> | ||
| 5 | |||
| 6 | struct lowpan_create_arg { | ||
| 7 | __be16 tag; | ||
| 8 | u16 d_size; | ||
| 9 | const struct ieee802154_addr *src; | ||
| 10 | const struct ieee802154_addr *dst; | ||
| 11 | }; | ||
| 12 | |||
| 13 | /* Equivalent of ipv4 struct ip | ||
| 14 | */ | ||
| 15 | struct lowpan_frag_queue { | ||
| 16 | struct inet_frag_queue q; | ||
| 17 | |||
| 18 | __be16 tag; | ||
| 19 | u16 d_size; | ||
| 20 | struct ieee802154_addr saddr; | ||
| 21 | struct ieee802154_addr daddr; | ||
| 22 | }; | ||
| 23 | |||
| 24 | static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) | ||
| 25 | { | ||
| 26 | switch (a->addr_type) { | ||
| 27 | case IEEE802154_ADDR_LONG: | ||
| 28 | return (__force u32)((((u32 *)a->hwaddr))[0] ^ | ||
| 29 | ((u32 *)(a->hwaddr))[1]); | ||
| 30 | case IEEE802154_ADDR_SHORT: | ||
| 31 | return (__force u32)(a->short_addr); | ||
| 32 | default: | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | static inline bool ieee802154_addr_addr_equal(const struct ieee802154_addr *a1, | ||
| 38 | const struct ieee802154_addr *a2) | ||
| 39 | { | ||
| 40 | if (a1->pan_id != a2->pan_id) | ||
| 41 | return false; | ||
| 42 | |||
| 43 | if (a1->addr_type != a2->addr_type) | ||
| 44 | return false; | ||
| 45 | |||
| 46 | switch (a1->addr_type) { | ||
| 47 | case IEEE802154_ADDR_LONG: | ||
| 48 | if (memcmp(a1->hwaddr, a2->hwaddr, IEEE802154_ADDR_LEN)) | ||
| 49 | return false; | ||
| 50 | break; | ||
| 51 | case IEEE802154_ADDR_SHORT: | ||
| 52 | if (a1->short_addr != a2->short_addr) | ||
| 53 | return false; | ||
| 54 | break; | ||
| 55 | default: | ||
| 56 | return false; | ||
| 57 | } | ||
| 58 | |||
| 59 | return true; | ||
| 60 | } | ||
| 61 | |||
| 62 | int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); | ||
| 63 | void lowpan_net_frag_exit(void); | ||
| 64 | int lowpan_net_frag_init(void); | ||
| 65 | |||
| 66 | #endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ | ||
