]> Pileus Git - ~andy/linux/blobdiff - net/ieee802154/6lowpan.c
6lowpan: handle context based source address
[~andy/linux] / net / ieee802154 / 6lowpan.c
index 3b9d5f20bd1c695de768db960b190beb4370fa35..c85e71e0c7ffc640bd9592ce52b03dbde6cad926 100644 (file)
@@ -67,39 +67,6 @@ static const u8 lowpan_ttl_values[] = {0, 1, 64, 255};
 
 static LIST_HEAD(lowpan_devices);
 
-/*
- * Uncompression of linklocal:
- *   0 -> 16 bytes from packet
- *   1 -> 2  bytes from prefix - bunch of zeroes and 8 from packet
- *   2 -> 2  bytes from prefix - zeroes + 2 from packet
- *   3 -> 2  bytes from prefix - infer 8 bytes from lladdr
- *
- *  NOTE: => the uncompress function does change 0xf to 0x10
- *  NOTE: 0x00 => no-autoconfig => unspecified
- */
-static const u8 lowpan_unc_llconf[] = {0x0f, 0x28, 0x22, 0x20};
-
-/*
- * Uncompression of ctx-based:
- *   0 -> 0 bits  from packet [unspecified / reserved]
- *   1 -> 8 bytes from prefix - bunch of zeroes and 8 from packet
- *   2 -> 8 bytes from prefix - zeroes + 2 from packet
- *   3 -> 8 bytes from prefix - infer 8 bytes from lladdr
- */
-static const u8 lowpan_unc_ctxconf[] = {0x00, 0x88, 0x82, 0x80};
-
-/*
- * Uncompression of ctx-base
- *   0 -> 0 bits from packet
- *   1 -> 2 bytes from prefix - bunch of zeroes 5 from packet
- *   2 -> 2 bytes from prefix - zeroes + 3 from packet
- *   3 -> 2 bytes from prefix - infer 1 bytes from lladdr
- */
-static const u8 lowpan_unc_mxconf[] = {0x0f, 0x25, 0x23, 0x21};
-
-/* Link local prefix */
-static const u8 lowpan_llprefix[] = {0xfe, 0x80};
-
 /* private device info */
 struct lowpan_dev_info {
        struct net_device       *real_dev; /* real WPAN device ptr */
@@ -191,55 +158,177 @@ lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift, const struct in6_addr *ipaddr,
        return rol8(val, shift);
 }
 
-static void
-lowpan_uip_ds6_set_addr_iid(struct in6_addr *ipaddr, unsigned char *lladdr)
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int
+lowpan_uncompress_addr(struct sk_buff *skb,
+               struct in6_addr *ipaddr,
+               const u8 address_mode,
+               const struct ieee802154_addr *lladdr)
 {
-       memcpy(&ipaddr->s6_addr[8], lladdr, IEEE802154_ADDR_LEN);
-       /* second bit-flip (Universe/Local) is done according RFC2464 */
-       ipaddr->s6_addr[8] ^= 0x02;
+       bool fail;
+
+       switch (address_mode) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* for global link addresses */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* fe:80::XXXX:XXXX:XXXX:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+               break;
+       case LOWPAN_IPHC_ADDR_02:
+               /* fe:80::ff:fe00:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               ipaddr->s6_addr[11] = 0xFF;
+               ipaddr->s6_addr[12] = 0xFE;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+               break;
+       case LOWPAN_IPHC_ADDR_03:
+               fail = false;
+               switch (lladdr->addr_type) {
+               case IEEE802154_ADDR_LONG:
+                       /* fe:80::XXXX:XXXX:XXXX:XXXX
+                        *        \_________________/
+                        *              hwaddr
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       memcpy(&ipaddr->s6_addr[8], lladdr->hwaddr,
+                                       IEEE802154_ADDR_LEN);
+                       /* second bit-flip (Universe/Local)
+                        * is done according RFC2464
+                        */
+                       ipaddr->s6_addr[8] ^= 0x02;
+                       break;
+               case IEEE802154_ADDR_SHORT:
+                       /* fe:80::ff:fe00:XXXX
+                        *                \__/
+                        *             short_addr
+                        *
+                        * Universe/Local bit is zero.
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       ipaddr->s6_addr[11] = 0xFF;
+                       ipaddr->s6_addr[12] = 0xFE;
+                       ipaddr->s6_addr16[7] = htons(lladdr->short_addr);
+                       break;
+               default:
+                       pr_debug("Invalid addr_type set\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
+       lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 addr is:\n",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
 }
 
-/*
- * Uncompress addresses based on a prefix and a postfix with zeroes in
- * between. If the postfix is zero in length it will use the link address
- * to configure the IP address (autoconf style).
- * pref_post_count takes a byte where the first nibble specify prefix count
- * and the second postfix count (NOTE: 15/0xf => 16 bytes copy).
+/* Uncompress address function for source context
+ * based address(non-multicast).
  */
 static int
-lowpan_uncompress_addr(struct sk_buff *skb, struct in6_addr *ipaddr,
-       u8 const *prefix, u8 pref_post_count, unsigned char *lladdr)
+lowpan_uncompress_context_based_src_addr(struct sk_buff *skb,
+               struct in6_addr *ipaddr,
+               const u8 sam)
 {
-       u8 prefcount = pref_post_count >> 4;
-       u8 postcount = pref_post_count & 0x0f;
-
-       /* full nibble 15 => 16 */
-       prefcount = (prefcount == 15 ? 16 : prefcount);
-       postcount = (postcount == 15 ? 16 : postcount);
-
-       if (lladdr)
-               lowpan_raw_dump_inline(__func__, "linklocal address",
-                                               lladdr, IEEE802154_ADDR_LEN);
-       if (prefcount > 0)
-               memcpy(ipaddr, prefix, prefcount);
-
-       if (prefcount + postcount < 16)
-               memset(&ipaddr->s6_addr[prefcount], 0,
-                                       16 - (prefcount + postcount));
-
-       if (postcount > 0) {
-               memcpy(&ipaddr->s6_addr[16 - postcount], skb->data, postcount);
-               skb_pull(skb, postcount);
-       } else if (prefcount > 0) {
-               if (lladdr == NULL)
-                       return -EINVAL;
+       switch (sam) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* unspec address ::
+                * Do nothing, address is already ::
+                */
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_02:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_03:
+               /* TODO */
+               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+               return -EINVAL;
+       default:
+               pr_debug("Invalid sam value: 0x%x\n", sam);
+               return -EINVAL;
+       }
+
+       lowpan_raw_dump_inline(NULL,
+                       "Reconstructed context based ipv6 src addr is:\n",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
+}
 
-               /* no IID based configuration if no prefix and no data */
-               lowpan_uip_ds6_set_addr_iid(ipaddr, lladdr);
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+               struct in6_addr *ipaddr,
+               const u8 dam)
+{
+       bool fail;
+
+       switch (dam) {
+       case LOWPAN_IPHC_DAM_00:
+               /* 00:  128 bits.  The full address
+                * is carried in-line.
+                */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_DAM_01:
+               /* 01:  48 bits.  The address takes
+                * the form ffXX::00XX:XXXX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+               break;
+       case LOWPAN_IPHC_DAM_10:
+               /* 10:  32 bits.  The address takes
+                * the form ffXX::00XX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+               break;
+       case LOWPAN_IPHC_DAM_11:
+               /* 11:  8 bits.  The address takes
+                * the form ff02::00XX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               ipaddr->s6_addr[1] = 0x02;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+               break;
+       default:
+               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
        }
 
-       pr_debug("uncompressing %d + %d => ", prefcount, postcount);
-       lowpan_raw_dump_inline(NULL, NULL, ipaddr->s6_addr, 16);
+       lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is:\n",
+                       ipaddr->s6_addr, 16);
 
        return 0;
 }
@@ -702,6 +791,12 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
        skb_reserve(frame->skb, sizeof(struct ipv6hdr));
        skb_put(frame->skb, frame->length);
 
+       /* copy the first control block to keep a
+        * trace of the link-layer addresses in case
+        * of a link-local compressed address
+        */
+       memcpy(frame->skb->cb, skb->cb, sizeof(skb->cb));
+
        init_timer(&frame->timer);
        /* time out is the same as for ipv6 - 60 sec */
        frame->timer.expires = jiffies + LOWPAN_FRAG_TIMEOUT;
@@ -723,9 +818,9 @@ frame_err:
 static int
 lowpan_process_data(struct sk_buff *skb)
 {
-       struct ipv6hdr hdr;
+       struct ipv6hdr hdr = {};
        u8 tmp, iphc0, iphc1, num_context = 0;
-       u8 *_saddr, *_daddr;
+       const struct ieee802154_addr *_saddr, *_daddr;
        int err;
 
        lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
@@ -828,8 +923,8 @@ lowpan_process_data(struct sk_buff *skb)
        if (lowpan_fetch_skb_u8(skb, &iphc1))
                goto drop;
 
-       _saddr = mac_cb(skb)->sa.hwaddr;
-       _daddr = mac_cb(skb)->da.hwaddr;
+       _saddr = &mac_cb(skb)->sa;
+       _daddr = &mac_cb(skb)->da;
 
        pr_debug("iphc0 = %02x, iphc1 = %02x\n", iphc0, iphc1);
 
@@ -868,8 +963,6 @@ lowpan_process_data(struct sk_buff *skb)
 
                hdr.priority = ((tmp >> 2) & 0x0f);
                hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
-               hdr.flow_lbl[1] = 0;
-               hdr.flow_lbl[2] = 0;
                break;
        /*
         * Flow Label carried in-line
@@ -885,10 +978,6 @@ lowpan_process_data(struct sk_buff *skb)
                break;
        /* Traffic Class and Flow Label are elided */
        case 3: /* 11b */
-               hdr.priority = 0;
-               hdr.flow_lbl[0] = 0;
-               hdr.flow_lbl[1] = 0;
-               hdr.flow_lbl[2] = 0;
                break;
        default:
                break;
@@ -915,10 +1004,18 @@ lowpan_process_data(struct sk_buff *skb)
        /* Extract SAM to the tmp variable */
        tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
 
-       /* Source address uncompression */
-       pr_debug("source address stateless compression\n");
-       err = lowpan_uncompress_addr(skb, &hdr.saddr, lowpan_llprefix,
-                               lowpan_unc_llconf[tmp], skb->data);
+       if (iphc1 & LOWPAN_IPHC_SAC) {
+               /* Source address context based uncompression */
+               pr_debug("SAC bit is set. Handle context based source address.\n");
+               err = lowpan_uncompress_context_based_src_addr(
+                               skb, &hdr.saddr, tmp);
+       } else {
+               /* Source address uncompression */
+               pr_debug("source address stateless compression\n");
+               err = lowpan_uncompress_addr(skb, &hdr.saddr, tmp, _saddr);
+       }
+
+       /* Check on error of previous branch */
        if (err)
                goto drop;
 
@@ -931,23 +1028,14 @@ lowpan_process_data(struct sk_buff *skb)
                        pr_debug("dest: context-based mcast compression\n");
                        /* TODO: implement this */
                } else {
-                       u8 prefix[] = {0xff, 0x02};
-
-                       pr_debug("dest: non context-based mcast compression\n");
-                       if (0 < tmp && tmp < 3) {
-                               if (lowpan_fetch_skb_u8(skb, &prefix[1]))
-                                       goto drop;
-                       }
-
-                       err = lowpan_uncompress_addr(skb, &hdr.daddr, prefix,
-                                       lowpan_unc_mxconf[tmp], NULL);
+                       err = lowpan_uncompress_multicast_daddr(
+                                       skb, &hdr.daddr, tmp);
                        if (err)
                                goto drop;
                }
        } else {
                pr_debug("dest: stateless compression\n");
-               err = lowpan_uncompress_addr(skb, &hdr.daddr, lowpan_llprefix,
-                               lowpan_unc_llconf[tmp], skb->data);
+               err = lowpan_uncompress_addr(skb, &hdr.daddr, tmp, _daddr);
                if (err)
                        goto drop;
        }