linux/net/ieee802154/6lowpan/rx.c
<<
>>
Prefs
   1/* This program is free software; you can redistribute it and/or modify
   2 * it under the terms of the GNU General Public License version 2
   3 * as published by the Free Software Foundation.
   4 *
   5 * This program is distributed in the hope that it will be useful,
   6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   8 * GNU General Public License for more details.
   9 */
  10
  11#include <linux/if_arp.h>
  12
  13#include <net/6lowpan.h>
  14#include <net/mac802154.h>
  15#include <net/ieee802154_netdev.h>
  16
  17#include "6lowpan_i.h"
  18
  19#define LOWPAN_DISPATCH_FIRST           0xc0
  20#define LOWPAN_DISPATCH_FRAG_MASK       0xf8
  21
  22#define LOWPAN_DISPATCH_NALP            0x00
  23#define LOWPAN_DISPATCH_ESC             0x40
  24#define LOWPAN_DISPATCH_HC1             0x42
  25#define LOWPAN_DISPATCH_DFF             0x43
  26#define LOWPAN_DISPATCH_BC0             0x50
  27#define LOWPAN_DISPATCH_MESH            0x80
  28
  29static int lowpan_give_skb_to_device(struct sk_buff *skb)
  30{
  31        skb->protocol = htons(ETH_P_IPV6);
  32        skb->dev->stats.rx_packets++;
  33        skb->dev->stats.rx_bytes += skb->len;
  34
  35        return netif_rx(skb);
  36}
  37
  38static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
  39{
  40        switch (res) {
  41        case RX_CONTINUE:
  42                /* nobody cared about this packet */
  43                net_warn_ratelimited("%s: received unknown dispatch\n",
  44                                     __func__);
  45
  46                /* fall-through */
  47        case RX_DROP_UNUSABLE:
  48                kfree_skb(skb);
  49
  50                /* fall-through */
  51        case RX_DROP:
  52                return NET_RX_DROP;
  53        case RX_QUEUED:
  54                return lowpan_give_skb_to_device(skb);
  55        default:
  56                break;
  57        }
  58
  59        return NET_RX_DROP;
  60}
  61
  62static inline bool lowpan_is_frag1(u8 dispatch)
  63{
  64        return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1;
  65}
  66
  67static inline bool lowpan_is_fragn(u8 dispatch)
  68{
  69        return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN;
  70}
  71
  72static lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb)
  73{
  74        int ret;
  75
  76        if (!(lowpan_is_frag1(*skb_network_header(skb)) ||
  77              lowpan_is_fragn(*skb_network_header(skb))))
  78                return RX_CONTINUE;
  79
  80        ret = lowpan_frag_rcv(skb, *skb_network_header(skb) &
  81                              LOWPAN_DISPATCH_FRAG_MASK);
  82        if (ret == 1)
  83                return RX_QUEUED;
  84
  85        /* Packet is freed by lowpan_frag_rcv on error or put into the frag
  86         * bucket.
  87         */
  88        return RX_DROP;
  89}
  90
  91int lowpan_iphc_decompress(struct sk_buff *skb)
  92{
  93        struct ieee802154_hdr hdr;
  94
  95        if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
  96                return -EINVAL;
  97
  98        return lowpan_header_decompress(skb, skb->dev, &hdr.dest, &hdr.source);
  99}
 100
 101static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
 102{
 103        int ret;
 104
 105        if (!lowpan_is_iphc(*skb_network_header(skb)))
 106                return RX_CONTINUE;
 107
 108        /* Setting datagram_offset to zero indicates non frag handling
 109         * while doing lowpan_header_decompress.
 110         */
 111        lowpan_802154_cb(skb)->d_size = 0;
 112
 113        ret = lowpan_iphc_decompress(skb);
 114        if (ret < 0)
 115                return RX_DROP_UNUSABLE;
 116
 117        return RX_QUEUED;
 118}
 119
 120lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb)
 121{
 122        if (!lowpan_is_ipv6(*skb_network_header(skb)))
 123                return RX_CONTINUE;
 124
 125        /* Pull off the 1-byte of 6lowpan header. */
 126        skb_pull(skb, 1);
 127        return RX_QUEUED;
 128}
 129
 130static inline bool lowpan_is_esc(u8 dispatch)
 131{
 132        return dispatch == LOWPAN_DISPATCH_ESC;
 133}
 134
 135static lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb)
 136{
 137        if (!lowpan_is_esc(*skb_network_header(skb)))
 138                return RX_CONTINUE;
 139
 140        net_warn_ratelimited("%s: %s\n", skb->dev->name,
 141                             "6LoWPAN ESC not supported\n");
 142
 143        return RX_DROP_UNUSABLE;
 144}
 145
 146static inline bool lowpan_is_hc1(u8 dispatch)
 147{
 148        return dispatch == LOWPAN_DISPATCH_HC1;
 149}
 150
 151static lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb)
 152{
 153        if (!lowpan_is_hc1(*skb_network_header(skb)))
 154                return RX_CONTINUE;
 155
 156        net_warn_ratelimited("%s: %s\n", skb->dev->name,
 157                             "6LoWPAN HC1 not supported\n");
 158
 159        return RX_DROP_UNUSABLE;
 160}
 161
 162static inline bool lowpan_is_dff(u8 dispatch)
 163{
 164        return dispatch == LOWPAN_DISPATCH_DFF;
 165}
 166
 167static lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb)
 168{
 169        if (!lowpan_is_dff(*skb_network_header(skb)))
 170                return RX_CONTINUE;
 171
 172        net_warn_ratelimited("%s: %s\n", skb->dev->name,
 173                             "6LoWPAN DFF not supported\n");
 174
 175        return RX_DROP_UNUSABLE;
 176}
 177
 178static inline bool lowpan_is_bc0(u8 dispatch)
 179{
 180        return dispatch == LOWPAN_DISPATCH_BC0;
 181}
 182
 183static lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb)
 184{
 185        if (!lowpan_is_bc0(*skb_network_header(skb)))
 186                return RX_CONTINUE;
 187
 188        net_warn_ratelimited("%s: %s\n", skb->dev->name,
 189                             "6LoWPAN BC0 not supported\n");
 190
 191        return RX_DROP_UNUSABLE;
 192}
 193
 194static inline bool lowpan_is_mesh(u8 dispatch)
 195{
 196        return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH;
 197}
 198
 199static lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb)
 200{
 201        if (!lowpan_is_mesh(*skb_network_header(skb)))
 202                return RX_CONTINUE;
 203
 204        net_warn_ratelimited("%s: %s\n", skb->dev->name,
 205                             "6LoWPAN MESH not supported\n");
 206
 207        return RX_DROP_UNUSABLE;
 208}
 209
 210static int lowpan_invoke_rx_handlers(struct sk_buff *skb)
 211{
 212        lowpan_rx_result res;
 213
 214#define CALL_RXH(rxh)                   \
 215        do {                            \
 216                res = rxh(skb); \
 217                if (res != RX_CONTINUE) \
 218                        goto rxh_next;  \
 219        } while (0)
 220
 221        /* likely at first */
 222        CALL_RXH(lowpan_rx_h_iphc);
 223        CALL_RXH(lowpan_rx_h_frag);
 224        CALL_RXH(lowpan_rx_h_ipv6);
 225        CALL_RXH(lowpan_rx_h_esc);
 226        CALL_RXH(lowpan_rx_h_hc1);
 227        CALL_RXH(lowpan_rx_h_dff);
 228        CALL_RXH(lowpan_rx_h_bc0);
 229        CALL_RXH(lowpan_rx_h_mesh);
 230
 231rxh_next:
 232        return lowpan_rx_handlers_result(skb, res);
 233#undef CALL_RXH
 234}
 235
 236static inline bool lowpan_is_nalp(u8 dispatch)
 237{
 238        return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP;
 239}
 240
 241/* Lookup for reserved dispatch values at:
 242 * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1
 243 *
 244 * Last Updated: 2015-01-22
 245 */
 246static inline bool lowpan_is_reserved(u8 dispatch)
 247{
 248        return ((dispatch >= 0x44 && dispatch <= 0x4F) ||
 249                (dispatch >= 0x51 && dispatch <= 0x5F) ||
 250                (dispatch >= 0xc8 && dispatch <= 0xdf) ||
 251                (dispatch >= 0xe8 && dispatch <= 0xff));
 252}
 253
 254/* lowpan_rx_h_check checks on generic 6LoWPAN requirements
 255 * in MAC and 6LoWPAN header.
 256 *
 257 * Don't manipulate the skb here, it could be shared buffer.
 258 */
 259static inline bool lowpan_rx_h_check(struct sk_buff *skb)
 260{
 261        __le16 fc = ieee802154_get_fc_from_skb(skb);
 262
 263        /* check on ieee802154 conform 6LoWPAN header */
 264        if (!ieee802154_is_data(fc) ||
 265            !ieee802154_skb_is_intra_pan_addressing(fc, skb))
 266                return false;
 267
 268        /* check if we can dereference the dispatch */
 269        if (unlikely(!skb->len))
 270                return false;
 271
 272        if (lowpan_is_nalp(*skb_network_header(skb)) ||
 273            lowpan_is_reserved(*skb_network_header(skb)))
 274                return false;
 275
 276        return true;
 277}
 278
 279static int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev,
 280                      struct packet_type *pt, struct net_device *orig_wdev)
 281{
 282        struct net_device *ldev;
 283
 284        if (wdev->type != ARPHRD_IEEE802154 ||
 285            skb->pkt_type == PACKET_OTHERHOST ||
 286            !lowpan_rx_h_check(skb))
 287                goto drop;
 288
 289        ldev = wdev->ieee802154_ptr->lowpan_dev;
 290        if (!ldev || !netif_running(ldev))
 291                goto drop;
 292
 293        /* Replacing skb->dev and followed rx handlers will manipulate skb. */
 294        skb = skb_share_check(skb, GFP_ATOMIC);
 295        if (!skb)
 296                goto out;
 297        skb->dev = ldev;
 298
 299        /* When receive frag1 it's likely that we manipulate the buffer.
 300         * When recevie iphc we manipulate the data buffer. So we need
 301         * to unshare the buffer.
 302         */
 303        if (lowpan_is_frag1(*skb_network_header(skb)) ||
 304            lowpan_is_iphc(*skb_network_header(skb))) {
 305                skb = skb_unshare(skb, GFP_ATOMIC);
 306                if (!skb)
 307                        goto out;
 308        }
 309
 310        return lowpan_invoke_rx_handlers(skb);
 311
 312drop:
 313        kfree_skb(skb);
 314out:
 315        return NET_RX_DROP;
 316}
 317
 318static struct packet_type lowpan_packet_type = {
 319        .type = htons(ETH_P_IEEE802154),
 320        .func = lowpan_rcv,
 321};
 322
 323void lowpan_rx_init(void)
 324{
 325        dev_add_pack(&lowpan_packet_type);
 326}
 327
 328void lowpan_rx_exit(void)
 329{
 330        dev_remove_pack(&lowpan_packet_type);
 331}
 332