diff options
Diffstat (limited to 'net/ieee802154/6lowpan_rtnl.c')
-rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 256 |
1 files changed, 49 insertions, 207 deletions
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); |