linux/net/ieee802154/6lowpan/reassembly.c
<<
>>
Prefs
   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
  29static const char lowpan_frags_cache_name[] = "lowpan-frags";
  30
  31static struct inet_frags lowpan_frags;
  32
  33static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
  34                             struct sk_buff *prev,  struct net_device *ldev);
  35
  36static 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
  44static 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);
  57out:
  58        spin_unlock(&fq->q.lock);
  59        inet_frag_put(&fq->q);
  60}
  61
  62static inline struct lowpan_frag_queue *
  63fq_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
  84static 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;
 152err:
 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 */
 163static 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;
 182out_oom:
 183        net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
 184        return -1;
 185}
 186
 187static 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
 205static 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
 219static 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
 234rxh_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
 242static 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
 276int 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
 314err:
 315        kfree_skb(skb);
 316        return -1;
 317}
 318
 319#ifdef CONFIG_SYSCTL
 320
 321static 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 */
 344static int lowpan_frags_secret_interval_unused;
 345static 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
 356static 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
 388err_reg:
 389        if (!net_eq(net, &init_net))
 390                kfree(table);
 391err_alloc:
 392        return -ENOMEM;
 393}
 394
 395static 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
 407static struct ctl_table_header *lowpan_ctl_header;
 408
 409static 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
 417static void lowpan_frags_sysctl_unregister(void)
 418{
 419        unregister_net_sysctl_table(lowpan_ctl_header);
 420}
 421#else
 422static inline int lowpan_frags_ns_sysctl_register(struct net *net)
 423{
 424        return 0;
 425}
 426
 427static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
 428{
 429}
 430
 431static inline int __init lowpan_frags_sysctl_register(void)
 432{
 433        return 0;
 434}
 435
 436static inline void lowpan_frags_sysctl_unregister(void)
 437{
 438}
 439#endif
 440
 441static 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
 462static 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
 470static 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
 479static 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
 485static 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
 491static 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
 499static 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
 507static 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
 515int __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;
 536out:
 537        return ret;
 538err_pernet:
 539        lowpan_frags_sysctl_unregister();
 540err_sysctl:
 541        inet_frags_fini(&lowpan_frags);
 542        return ret;
 543}
 544
 545void 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}
 551