root/net/ieee802154/6lowpan/reassembly.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lowpan_frag_init
  2. lowpan_frag_expire
  3. fq_find
  4. lowpan_frag_queue
  5. lowpan_frag_reasm
  6. lowpan_frag_rx_handlers_result
  7. lowpan_frag_rx_h_iphc
  8. lowpan_invoke_frag_rx_handlers
  9. lowpan_get_cb
  10. lowpan_frag_rcv
  11. lowpan_frags_ns_sysctl_register
  12. lowpan_frags_ns_sysctl_unregister
  13. lowpan_frags_sysctl_register
  14. lowpan_frags_sysctl_unregister
  15. lowpan_frags_ns_sysctl_register
  16. lowpan_frags_ns_sysctl_unregister
  17. lowpan_frags_sysctl_register
  18. lowpan_frags_sysctl_unregister
  19. lowpan_frags_init_net
  20. lowpan_frags_pre_exit_net
  21. lowpan_frags_exit_net
  22. lowpan_key_hashfn
  23. lowpan_obj_hashfn
  24. lowpan_obj_cmpfn
  25. lowpan_net_frag_init
  26. lowpan_net_frag_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*      6LoWPAN fragment reassembly
   3  *
   4  *      Authors:
   5  *      Alexander Aring         <aar@pengutronix.de>
   6  *
   7  *      Based on: net/ipv6/reassembly.c
   8  */
   9 
  10 #define pr_fmt(fmt) "6LoWPAN: " fmt
  11 
  12 #include <linux/net.h>
  13 #include <linux/list.h>
  14 #include <linux/netdevice.h>
  15 #include <linux/random.h>
  16 #include <linux/jhash.h>
  17 #include <linux/skbuff.h>
  18 #include <linux/slab.h>
  19 #include <linux/export.h>
  20 
  21 #include <net/ieee802154_netdev.h>
  22 #include <net/6lowpan.h>
  23 #include <net/ipv6_frag.h>
  24 #include <net/inet_frag.h>
  25 #include <net/ip.h>
  26 
  27 #include "6lowpan_i.h"
  28 
  29 static const char lowpan_frags_cache_name[] = "lowpan-frags";
  30 
  31 static struct inet_frags lowpan_frags;
  32 
  33 static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
  34                              struct sk_buff *prev,  struct net_device *ldev);
  35 
  36 static void lowpan_frag_init(struct inet_frag_queue *q, const void *a)
  37 {
  38         const struct frag_lowpan_compare_key *key = a;
  39 
  40         BUILD_BUG_ON(sizeof(*key) > sizeof(q->key));
  41         memcpy(&q->key, key, sizeof(*key));
  42 }
  43 
  44 static void lowpan_frag_expire(struct timer_list *t)
  45 {
  46         struct inet_frag_queue *frag = from_timer(frag, t, timer);
  47         struct frag_queue *fq;
  48 
  49         fq = container_of(frag, struct frag_queue, q);
  50 
  51         spin_lock(&fq->q.lock);
  52 
  53         if (fq->q.flags & INET_FRAG_COMPLETE)
  54                 goto out;
  55 
  56         inet_frag_kill(&fq->q);
  57 out:
  58         spin_unlock(&fq->q.lock);
  59         inet_frag_put(&fq->q);
  60 }
  61 
  62 static inline struct lowpan_frag_queue *
  63 fq_find(struct net *net, const struct lowpan_802154_cb *cb,
  64         const struct ieee802154_addr *src,
  65         const struct ieee802154_addr *dst)
  66 {
  67         struct netns_ieee802154_lowpan *ieee802154_lowpan =
  68                 net_ieee802154_lowpan(net);
  69         struct frag_lowpan_compare_key key = {};
  70         struct inet_frag_queue *q;
  71 
  72         key.tag = cb->d_tag;
  73         key.d_size = cb->d_size;
  74         key.src = *src;
  75         key.dst = *dst;
  76 
  77         q = inet_frag_find(ieee802154_lowpan->fqdir, &key);
  78         if (!q)
  79                 return NULL;
  80 
  81         return container_of(q, struct lowpan_frag_queue, q);
  82 }
  83 
  84 static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
  85                              struct sk_buff *skb, u8 frag_type)
  86 {
  87         struct sk_buff *prev_tail;
  88         struct net_device *ldev;
  89         int end, offset, err;
  90 
  91         /* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb
  92          * in inet_fragment.c
  93          */
  94         BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm));
  95         BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm));
  96 
  97         if (fq->q.flags & INET_FRAG_COMPLETE)
  98                 goto err;
  99 
 100         offset = lowpan_802154_cb(skb)->d_offset << 3;
 101         end = lowpan_802154_cb(skb)->d_size;
 102 
 103         /* Is this the final fragment? */
 104         if (offset + skb->len == end) {
 105                 /* If we already have some bits beyond end
 106                  * or have different end, the segment is corrupted.
 107                  */
 108                 if (end < fq->q.len ||
 109                     ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
 110                         goto err;
 111                 fq->q.flags |= INET_FRAG_LAST_IN;
 112                 fq->q.len = end;
 113         } else {
 114                 if (end > fq->q.len) {
 115                         /* Some bits beyond end -> corruption. */
 116                         if (fq->q.flags & INET_FRAG_LAST_IN)
 117                                 goto err;
 118                         fq->q.len = end;
 119                 }
 120         }
 121 
 122         ldev = skb->dev;
 123         if (ldev)
 124                 skb->dev = NULL;
 125         barrier();
 126 
 127         prev_tail = fq->q.fragments_tail;
 128         err = inet_frag_queue_insert(&fq->q, skb, offset, end);
 129         if (err)
 130                 goto err;
 131 
 132         fq->q.stamp = skb->tstamp;
 133         if (frag_type == LOWPAN_DISPATCH_FRAG1)
 134                 fq->q.flags |= INET_FRAG_FIRST_IN;
 135 
 136         fq->q.meat += skb->len;
 137         add_frag_mem_limit(fq->q.fqdir, skb->truesize);
 138 
 139         if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
 140             fq->q.meat == fq->q.len) {
 141                 int res;
 142                 unsigned long orefdst = skb->_skb_refdst;
 143 
 144                 skb->_skb_refdst = 0UL;
 145                 res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
 146                 skb->_skb_refdst = orefdst;
 147                 return res;
 148         }
 149         skb_dst_drop(skb);
 150 
 151         return -1;
 152 err:
 153         kfree_skb(skb);
 154         return -1;
 155 }
 156 
 157 /*      Check if this packet is complete.
 158  *
 159  *      It is called with locked fq, and caller must check that
 160  *      queue is eligible for reassembly i.e. it is not COMPLETE,
 161  *      the last and the first frames arrived and all the bits are here.
 162  */
 163 static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
 164                              struct sk_buff *prev_tail, struct net_device *ldev)
 165 {
 166         void *reasm_data;
 167 
 168         inet_frag_kill(&fq->q);
 169 
 170         reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
 171         if (!reasm_data)
 172                 goto out_oom;
 173         inet_frag_reasm_finish(&fq->q, skb, reasm_data, false);
 174 
 175         skb->dev = ldev;
 176         skb->tstamp = fq->q.stamp;
 177         fq->q.rb_fragments = RB_ROOT;
 178         fq->q.fragments_tail = NULL;
 179         fq->q.last_run_head = NULL;
 180 
 181         return 1;
 182 out_oom:
 183         net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
 184         return -1;
 185 }
 186 
 187 static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
 188                                           lowpan_rx_result res)
 189 {
 190         switch (res) {
 191         case RX_QUEUED:
 192                 return NET_RX_SUCCESS;
 193         case RX_CONTINUE:
 194                 /* nobody cared about this packet */
 195                 net_warn_ratelimited("%s: received unknown dispatch\n",
 196                                      __func__);
 197 
 198                 /* fall-through */
 199         default:
 200                 /* all others failure */
 201                 return NET_RX_DROP;
 202         }
 203 }
 204 
 205 static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
 206 {
 207         int ret;
 208 
 209         if (!lowpan_is_iphc(*skb_network_header(skb)))
 210                 return RX_CONTINUE;
 211 
 212         ret = lowpan_iphc_decompress(skb);
 213         if (ret < 0)
 214                 return RX_DROP;
 215 
 216         return RX_QUEUED;
 217 }
 218 
 219 static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
 220 {
 221         lowpan_rx_result res;
 222 
 223 #define CALL_RXH(rxh)                   \
 224         do {                            \
 225                 res = rxh(skb); \
 226                 if (res != RX_CONTINUE) \
 227                         goto rxh_next;  \
 228         } while (0)
 229 
 230         /* likely at first */
 231         CALL_RXH(lowpan_frag_rx_h_iphc);
 232         CALL_RXH(lowpan_rx_h_ipv6);
 233 
 234 rxh_next:
 235         return lowpan_frag_rx_handlers_result(skb, res);
 236 #undef CALL_RXH
 237 }
 238 
 239 #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK        0x07
 240 #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT       8
 241 
 242 static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
 243                          struct lowpan_802154_cb *cb)
 244 {
 245         bool fail;
 246         u8 high = 0, low = 0;
 247         __be16 d_tag = 0;
 248 
 249         fail = lowpan_fetch_skb(skb, &high, 1);
 250         fail |= lowpan_fetch_skb(skb, &low, 1);
 251         /* remove the dispatch value and use first three bits as high value
 252          * for the datagram size
 253          */
 254         cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
 255                 LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
 256         fail |= lowpan_fetch_skb(skb, &d_tag, 2);
 257         cb->d_tag = ntohs(d_tag);
 258 
 259         if (frag_type == LOWPAN_DISPATCH_FRAGN) {
 260                 fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
 261         } else {
 262                 skb_reset_network_header(skb);
 263                 cb->d_offset = 0;
 264                 /* check if datagram_size has ipv6hdr on FRAG1 */
 265                 fail |= cb->d_size < sizeof(struct ipv6hdr);
 266                 /* check if we can dereference the dispatch value */
 267                 fail |= !skb->len;
 268         }
 269 
 270         if (unlikely(fail))
 271                 return -EIO;
 272 
 273         return 0;
 274 }
 275 
 276 int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
 277 {
 278         struct lowpan_frag_queue *fq;
 279         struct net *net = dev_net(skb->dev);
 280         struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
 281         struct ieee802154_hdr hdr = {};
 282         int err;
 283 
 284         if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
 285                 goto err;
 286 
 287         err = lowpan_get_cb(skb, frag_type, cb);
 288         if (err < 0)
 289                 goto err;
 290 
 291         if (frag_type == LOWPAN_DISPATCH_FRAG1) {
 292                 err = lowpan_invoke_frag_rx_handlers(skb);
 293                 if (err == NET_RX_DROP)
 294                         goto err;
 295         }
 296 
 297         if (cb->d_size > IPV6_MIN_MTU) {
 298                 net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
 299                 goto err;
 300         }
 301 
 302         fq = fq_find(net, cb, &hdr.source, &hdr.dest);
 303         if (fq != NULL) {
 304                 int ret;
 305 
 306                 spin_lock(&fq->q.lock);
 307                 ret = lowpan_frag_queue(fq, skb, frag_type);
 308                 spin_unlock(&fq->q.lock);
 309 
 310                 inet_frag_put(&fq->q);
 311                 return ret;
 312         }
 313 
 314 err:
 315         kfree_skb(skb);
 316         return -1;
 317 }
 318 
 319 #ifdef CONFIG_SYSCTL
 320 
 321 static struct ctl_table lowpan_frags_ns_ctl_table[] = {
 322         {
 323                 .procname       = "6lowpanfrag_high_thresh",
 324                 .maxlen         = sizeof(unsigned long),
 325                 .mode           = 0644,
 326                 .proc_handler   = proc_doulongvec_minmax,
 327         },
 328         {
 329                 .procname       = "6lowpanfrag_low_thresh",
 330                 .maxlen         = sizeof(unsigned long),
 331                 .mode           = 0644,
 332                 .proc_handler   = proc_doulongvec_minmax,
 333         },
 334         {
 335                 .procname       = "6lowpanfrag_time",
 336                 .maxlen         = sizeof(int),
 337                 .mode           = 0644,
 338                 .proc_handler   = proc_dointvec_jiffies,
 339         },
 340         { }
 341 };
 342 
 343 /* secret interval has been deprecated */
 344 static int lowpan_frags_secret_interval_unused;
 345 static struct ctl_table lowpan_frags_ctl_table[] = {
 346         {
 347                 .procname       = "6lowpanfrag_secret_interval",
 348                 .data           = &lowpan_frags_secret_interval_unused,
 349                 .maxlen         = sizeof(int),
 350                 .mode           = 0644,
 351                 .proc_handler   = proc_dointvec_jiffies,
 352         },
 353         { }
 354 };
 355 
 356 static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 357 {
 358         struct ctl_table *table;
 359         struct ctl_table_header *hdr;
 360         struct netns_ieee802154_lowpan *ieee802154_lowpan =
 361                 net_ieee802154_lowpan(net);
 362 
 363         table = lowpan_frags_ns_ctl_table;
 364         if (!net_eq(net, &init_net)) {
 365                 table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
 366                                 GFP_KERNEL);
 367                 if (table == NULL)
 368                         goto err_alloc;
 369 
 370                 /* Don't export sysctls to unprivileged users */
 371                 if (net->user_ns != &init_user_ns)
 372                         table[0].procname = NULL;
 373         }
 374 
 375         table[0].data   = &ieee802154_lowpan->fqdir->high_thresh;
 376         table[0].extra1 = &ieee802154_lowpan->fqdir->low_thresh;
 377         table[1].data   = &ieee802154_lowpan->fqdir->low_thresh;
 378         table[1].extra2 = &ieee802154_lowpan->fqdir->high_thresh;
 379         table[2].data   = &ieee802154_lowpan->fqdir->timeout;
 380 
 381         hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
 382         if (hdr == NULL)
 383                 goto err_reg;
 384 
 385         ieee802154_lowpan->sysctl.frags_hdr = hdr;
 386         return 0;
 387 
 388 err_reg:
 389         if (!net_eq(net, &init_net))
 390                 kfree(table);
 391 err_alloc:
 392         return -ENOMEM;
 393 }
 394 
 395 static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
 396 {
 397         struct ctl_table *table;
 398         struct netns_ieee802154_lowpan *ieee802154_lowpan =
 399                 net_ieee802154_lowpan(net);
 400 
 401         table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
 402         unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
 403         if (!net_eq(net, &init_net))
 404                 kfree(table);
 405 }
 406 
 407 static struct ctl_table_header *lowpan_ctl_header;
 408 
 409 static int __init lowpan_frags_sysctl_register(void)
 410 {
 411         lowpan_ctl_header = register_net_sysctl(&init_net,
 412                                                 "net/ieee802154/6lowpan",
 413                                                 lowpan_frags_ctl_table);
 414         return lowpan_ctl_header == NULL ? -ENOMEM : 0;
 415 }
 416 
 417 static void lowpan_frags_sysctl_unregister(void)
 418 {
 419         unregister_net_sysctl_table(lowpan_ctl_header);
 420 }
 421 #else
 422 static inline int lowpan_frags_ns_sysctl_register(struct net *net)
 423 {
 424         return 0;
 425 }
 426 
 427 static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
 428 {
 429 }
 430 
 431 static inline int __init lowpan_frags_sysctl_register(void)
 432 {
 433         return 0;
 434 }
 435 
 436 static inline void lowpan_frags_sysctl_unregister(void)
 437 {
 438 }
 439 #endif
 440 
 441 static int __net_init lowpan_frags_init_net(struct net *net)
 442 {
 443         struct netns_ieee802154_lowpan *ieee802154_lowpan =
 444                 net_ieee802154_lowpan(net);
 445         int res;
 446 
 447 
 448         res = fqdir_init(&ieee802154_lowpan->fqdir, &lowpan_frags, net);
 449         if (res < 0)
 450                 return res;
 451 
 452         ieee802154_lowpan->fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH;
 453         ieee802154_lowpan->fqdir->low_thresh = IPV6_FRAG_LOW_THRESH;
 454         ieee802154_lowpan->fqdir->timeout = IPV6_FRAG_TIMEOUT;
 455 
 456         res = lowpan_frags_ns_sysctl_register(net);
 457         if (res < 0)
 458                 fqdir_exit(ieee802154_lowpan->fqdir);
 459         return res;
 460 }
 461 
 462 static void __net_exit lowpan_frags_pre_exit_net(struct net *net)
 463 {
 464         struct netns_ieee802154_lowpan *ieee802154_lowpan =
 465                 net_ieee802154_lowpan(net);
 466 
 467         fqdir_pre_exit(ieee802154_lowpan->fqdir);
 468 }
 469 
 470 static void __net_exit lowpan_frags_exit_net(struct net *net)
 471 {
 472         struct netns_ieee802154_lowpan *ieee802154_lowpan =
 473                 net_ieee802154_lowpan(net);
 474 
 475         lowpan_frags_ns_sysctl_unregister(net);
 476         fqdir_exit(ieee802154_lowpan->fqdir);
 477 }
 478 
 479 static struct pernet_operations lowpan_frags_ops = {
 480         .init           = lowpan_frags_init_net,
 481         .pre_exit       = lowpan_frags_pre_exit_net,
 482         .exit           = lowpan_frags_exit_net,
 483 };
 484 
 485 static u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed)
 486 {
 487         return jhash2(data,
 488                       sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
 489 }
 490 
 491 static u32 lowpan_obj_hashfn(const void *data, u32 len, u32 seed)
 492 {
 493         const struct inet_frag_queue *fq = data;
 494 
 495         return jhash2((const u32 *)&fq->key,
 496                       sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
 497 }
 498 
 499 static int lowpan_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
 500 {
 501         const struct frag_lowpan_compare_key *key = arg->key;
 502         const struct inet_frag_queue *fq = ptr;
 503 
 504         return !!memcmp(&fq->key, key, sizeof(*key));
 505 }
 506 
 507 static const struct rhashtable_params lowpan_rhash_params = {
 508         .head_offset            = offsetof(struct inet_frag_queue, node),
 509         .hashfn                 = lowpan_key_hashfn,
 510         .obj_hashfn             = lowpan_obj_hashfn,
 511         .obj_cmpfn              = lowpan_obj_cmpfn,
 512         .automatic_shrinking    = true,
 513 };
 514 
 515 int __init lowpan_net_frag_init(void)
 516 {
 517         int ret;
 518 
 519         lowpan_frags.constructor = lowpan_frag_init;
 520         lowpan_frags.destructor = NULL;
 521         lowpan_frags.qsize = sizeof(struct frag_queue);
 522         lowpan_frags.frag_expire = lowpan_frag_expire;
 523         lowpan_frags.frags_cache_name = lowpan_frags_cache_name;
 524         lowpan_frags.rhash_params = lowpan_rhash_params;
 525         ret = inet_frags_init(&lowpan_frags);
 526         if (ret)
 527                 goto out;
 528 
 529         ret = lowpan_frags_sysctl_register();
 530         if (ret)
 531                 goto err_sysctl;
 532 
 533         ret = register_pernet_subsys(&lowpan_frags_ops);
 534         if (ret)
 535                 goto err_pernet;
 536 out:
 537         return ret;
 538 err_pernet:
 539         lowpan_frags_sysctl_unregister();
 540 err_sysctl:
 541         inet_frags_fini(&lowpan_frags);
 542         return ret;
 543 }
 544 
 545 void lowpan_net_frag_exit(void)
 546 {
 547         lowpan_frags_sysctl_unregister();
 548         unregister_pernet_subsys(&lowpan_frags_ops);
 549         inet_frags_fini(&lowpan_frags);
 550 }

/* [<][>][^][v][top][bottom][index][help] */