These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / 6lowpan / iphc.c
index 94a375c..346b5c1 100644 (file)
 
 #include <linux/bitops.h>
 #include <linux/if_arp.h>
-#include <linux/module.h>
 #include <linux/netdevice.h>
+
 #include <net/6lowpan.h>
 #include <net/ipv6.h>
-#include <net/af_ieee802154.h>
+
+/* special link-layer handling */
+#include <net/mac802154.h>
 
 #include "nhc.h"
 
+/* Values of fields within the IPHC encoding first byte */
+#define LOWPAN_IPHC_TF_MASK    0x18
+#define LOWPAN_IPHC_TF_00      0x00
+#define LOWPAN_IPHC_TF_01      0x08
+#define LOWPAN_IPHC_TF_10      0x10
+#define LOWPAN_IPHC_TF_11      0x18
+
+#define LOWPAN_IPHC_NH         0x04
+
+#define LOWPAN_IPHC_HLIM_MASK  0x03
+#define LOWPAN_IPHC_HLIM_00    0x00
+#define LOWPAN_IPHC_HLIM_01    0x01
+#define LOWPAN_IPHC_HLIM_10    0x02
+#define LOWPAN_IPHC_HLIM_11    0x03
+
+/* Values of fields within the IPHC encoding second byte */
+#define LOWPAN_IPHC_CID                0x80
+
+#define LOWPAN_IPHC_SAC                0x40
+
+#define LOWPAN_IPHC_SAM_MASK   0x30
+#define LOWPAN_IPHC_SAM_00     0x00
+#define LOWPAN_IPHC_SAM_01     0x10
+#define LOWPAN_IPHC_SAM_10     0x20
+#define LOWPAN_IPHC_SAM_11     0x30
+
+#define LOWPAN_IPHC_M          0x08
+
+#define LOWPAN_IPHC_DAC                0x04
+
+#define LOWPAN_IPHC_DAM_MASK   0x03
+#define LOWPAN_IPHC_DAM_00     0x00
+#define LOWPAN_IPHC_DAM_01     0x01
+#define LOWPAN_IPHC_DAM_10     0x02
+#define LOWPAN_IPHC_DAM_11     0x03
+
+/* ipv6 address based on mac
+ * second bit-flip (Universe/Local) is done according RFC2464
+ */
+#define is_addr_mac_addr_based(a, m) \
+       ((((a)->s6_addr[8])  == (((m)[0]) ^ 0x02)) &&   \
+        (((a)->s6_addr[9])  == (m)[1]) &&              \
+        (((a)->s6_addr[10]) == (m)[2]) &&              \
+        (((a)->s6_addr[11]) == (m)[3]) &&              \
+        (((a)->s6_addr[12]) == (m)[4]) &&              \
+        (((a)->s6_addr[13]) == (m)[5]) &&              \
+        (((a)->s6_addr[14]) == (m)[6]) &&              \
+        (((a)->s6_addr[15]) == (m)[7]))
+
+/* check whether we can compress the IID to 16 bits,
+ * it's possible for unicast addresses with first 49 bits are zero only.
+ */
+#define lowpan_is_iid_16_bit_compressable(a)   \
+       ((((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr[10]) == 0) &&           \
+        (((a)->s6_addr[11]) == 0xff) &&        \
+        (((a)->s6_addr[12]) == 0xfe) &&        \
+        (((a)->s6_addr[13]) == 0))
+
+/* check whether the 112-bit gid of the multicast address is mappable to: */
+
+/* 48 bits, FFXX::00XX:XXXX:XXXX */
+#define lowpan_is_mcast_addr_compressable48(a) \
+       ((((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr[10]) == 0))
+
+/* 32 bits, FFXX::00XX:XXXX */
+#define lowpan_is_mcast_addr_compressable32(a) \
+       ((((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr16[5]) == 0) &&          \
+        (((a)->s6_addr[12]) == 0))
+
+/* 8 bits, FF02::00XX */
+#define lowpan_is_mcast_addr_compressable8(a)  \
+       ((((a)->s6_addr[1])  == 2) &&           \
+        (((a)->s6_addr16[1]) == 0) &&          \
+        (((a)->s6_addr16[2]) == 0) &&          \
+        (((a)->s6_addr16[3]) == 0) &&          \
+        (((a)->s6_addr16[4]) == 0) &&          \
+        (((a)->s6_addr16[5]) == 0) &&          \
+        (((a)->s6_addr16[6]) == 0) &&          \
+        (((a)->s6_addr[14]) == 0))
+
+static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
+                                               const void *lladdr)
+{
+       /* fe:80::XXXX:XXXX:XXXX:XXXX
+        *        \_________________/
+        *              hwaddr
+        */
+       ipaddr->s6_addr[0] = 0xFE;
+       ipaddr->s6_addr[1] = 0x80;
+       memcpy(&ipaddr->s6_addr[8], lladdr, EUI64_ADDR_LEN);
+       /* second bit-flip (Universe/Local)
+        * is done according RFC2464
+        */
+       ipaddr->s6_addr[8] ^= 0x02;
+}
+
+static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
+                                                const void *lladdr)
+{
+       const struct ieee802154_addr *addr = lladdr;
+       u8 eui64[EUI64_ADDR_LEN] = { };
+
+       switch (addr->mode) {
+       case IEEE802154_ADDR_LONG:
+               ieee802154_le64_to_be64(eui64, &addr->extended_addr);
+               iphc_uncompress_eui64_lladdr(ipaddr, eui64);
+               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;
+               ieee802154_le16_to_be16(&ipaddr->s6_addr16[7],
+                                       &addr->short_addr);
+               break;
+       default:
+               /* should never handled and filtered by 802154 6lowpan */
+               WARN_ON_ONCE(1);
+               break;
+       }
+}
+
 /* Uncompress address function for source and
  * destination address(non-multicast).
  *
- * address_mode is sam value or dam value.
+ * address_mode is the masked value for sam or dam value
  */
-static int uncompress_addr(struct sk_buff *skb,
-                          struct in6_addr *ipaddr, const u8 address_mode,
-                          const u8 *lladdr, const u8 addr_type,
-                          const u8 addr_len)
+static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
+                          struct in6_addr *ipaddr, u8 address_mode,
+                          const void *lladdr)
 {
        bool fail;
 
        switch (address_mode) {
-       case LOWPAN_IPHC_ADDR_00:
+       /* SAM and DAM are the same here */
+       case LOWPAN_IPHC_DAM_00:
                /* for global link addresses */
                fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
                break;
-       case LOWPAN_IPHC_ADDR_01:
+       case LOWPAN_IPHC_SAM_01:
+       case LOWPAN_IPHC_DAM_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:
+       case LOWPAN_IPHC_SAM_10:
+       case LOWPAN_IPHC_DAM_10:
                /* fe:80::ff:fe00:XXXX */
                ipaddr->s6_addr[0] = 0xFE;
                ipaddr->s6_addr[1] = 0x80;
@@ -87,38 +228,16 @@ static int uncompress_addr(struct sk_buff *skb,
                ipaddr->s6_addr[12] = 0xFE;
                fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
                break;
-       case LOWPAN_IPHC_ADDR_03:
+       case LOWPAN_IPHC_SAM_11:
+       case LOWPAN_IPHC_DAM_11:
                fail = false;
-               switch (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, 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(*((u16 *)lladdr));
+               switch (lowpan_priv(dev)->lltype) {
+               case LOWPAN_LLTYPE_IEEE802154:
+                       iphc_uncompress_802154_lladdr(ipaddr, lladdr);
                        break;
                default:
-                       pr_debug("Invalid addr_type set\n");
-                       return -EINVAL;
+                       iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
+                       break;
                }
                break;
        default:
@@ -142,24 +261,25 @@ static int uncompress_addr(struct sk_buff *skb,
  */
 static int uncompress_context_based_src_addr(struct sk_buff *skb,
                                             struct in6_addr *ipaddr,
-                                            const u8 sam)
+                                            u8 address_mode)
 {
-       switch (sam) {
-       case LOWPAN_IPHC_ADDR_00:
+       switch (address_mode) {
+       case LOWPAN_IPHC_SAM_00:
                /* unspec address ::
                 * Do nothing, address is already ::
                 */
                break;
-       case LOWPAN_IPHC_ADDR_01:
+       case LOWPAN_IPHC_SAM_01:
                /* TODO */
-       case LOWPAN_IPHC_ADDR_02:
+       case LOWPAN_IPHC_SAM_10:
                /* TODO */
-       case LOWPAN_IPHC_ADDR_03:
+       case LOWPAN_IPHC_SAM_11:
                /* TODO */
-               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+               netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
+                           address_mode);
                return -EINVAL;
        default:
-               pr_debug("Invalid sam value: 0x%x\n", sam);
+               pr_debug("Invalid sam value: 0x%x\n", address_mode);
                return -EINVAL;
        }
 
@@ -175,11 +295,11 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
  */
 static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
                                             struct in6_addr *ipaddr,
-                                            const u8 dam)
+                                            u8 address_mode)
 {
        bool fail;
 
-       switch (dam) {
+       switch (address_mode) {
        case LOWPAN_IPHC_DAM_00:
                /* 00:  128 bits.  The full address
                 * is carried in-line.
@@ -211,7 +331,7 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
                fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
                break;
        default:
-               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+               pr_debug("DAM value has a wrong value: 0x%x\n", address_mode);
                return -EINVAL;
        }
 
@@ -226,77 +346,142 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
        return 0;
 }
 
-/* TTL uncompression values */
-static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
-
-int
-lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
-                        const u8 *saddr, const u8 saddr_type,
-                        const u8 saddr_len, const u8 *daddr,
-                        const u8 daddr_type, const u8 daddr_len,
-                        u8 iphc0, u8 iphc1)
+/* get the ecn values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
 {
-       struct ipv6hdr hdr = {};
-       u8 tmp, num_context = 0;
-       int err;
+       /* get the two higher bits which is ecn */
+       u8 ecn = tf[0] & 0xc0;
 
-       raw_dump_table(__func__, "raw skb data dump uncompressed",
-                      skb->data, skb->len);
+       /* ECN takes 0x30 in hdr->flow_lbl[0] */
+       hdr->flow_lbl[0] |= (ecn >> 2);
+}
 
-       /* another if the CID flag is set */
-       if (iphc1 & LOWPAN_IPHC_CID) {
-               pr_debug("CID flag is set, increase header with one\n");
-               if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
-                       return -EINVAL;
-       }
+/* get the dscp values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_dscp(struct ipv6hdr *hdr, const u8 *tf)
+{
+       /* DSCP is at place after ECN */
+       u8 dscp = tf[0] & 0x3f;
 
-       hdr.version = 6;
+       /* The four highest bits need to be set at hdr->priority */
+       hdr->priority |= ((dscp & 0x3c) >> 2);
+       /* The two lower bits is part of hdr->flow_lbl[0] */
+       hdr->flow_lbl[0] |= ((dscp & 0x03) << 6);
+}
 
-       /* Traffic Class and Flow Label */
-       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
-       /* Traffic Class and FLow Label carried in-line
-        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+/* get the flow label values from iphc tf format and set it to ipv6hdr */
+static inline void lowpan_iphc_tf_set_lbl(struct ipv6hdr *hdr, const u8 *lbl)
+{
+       /* flow label is always some array started with lower nibble of
+        * flow_lbl[0] and followed with two bytes afterwards. Inside inline
+        * data the flow_lbl position can be different, which will be handled
+        * by lbl pointer. E.g. case "01" vs "00" the traffic class is 8 bit
+        * shifted, the different lbl pointer will handle that.
+        *
+        * The flow label will started at lower nibble of flow_lbl[0], the
+        * higher nibbles are part of DSCP + ECN.
         */
-       case 0: /* 00b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       hdr->flow_lbl[0] |= lbl[0] & 0x0f;
+       memcpy(&hdr->flow_lbl[1], &lbl[1], 2);
+}
+
+/* lowpan_iphc_tf_decompress - decompress the traffic class.
+ *     This function will return zero on success, a value lower than zero if
+ *     failed.
+ */
+static int lowpan_iphc_tf_decompress(struct sk_buff *skb, struct ipv6hdr *hdr,
+                                    u8 val)
+{
+       u8 tf[4];
+
+       /* Traffic Class and Flow Label */
+       switch (val) {
+       case LOWPAN_IPHC_TF_00:
+               /* ECN + DSCP + 4-bit Pad + Flow Label (4 bytes) */
+               if (lowpan_fetch_skb(skb, tf, 4))
                        return -EINVAL;
 
-               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
-               skb_pull(skb, 3);
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
-                                       (hdr.flow_lbl[0] & 0x0f);
+               /*                      1                   2                   3
+                *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                * |ECN|   DSCP    |  rsv  |             Flow Label                |
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_dscp(hdr, tf);
+               lowpan_iphc_tf_set_lbl(hdr, &tf[1]);
                break;
-       /* Traffic class carried in-line
-        * ECN + DSCP (1 byte), Flow Label is elided
-        */
-       case 2: /* 10b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       case LOWPAN_IPHC_TF_01:
+               /* ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided. */
+               if (lowpan_fetch_skb(skb, tf, 3))
                        return -EINVAL;
 
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+               /*                     1                   2
+                * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                * |ECN|rsv|             Flow Label                |
+                * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_lbl(hdr, &tf[0]);
                break;
-       /* Flow Label carried in-line
-        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
-        */
-       case 1: /* 01b */
-               if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+       case LOWPAN_IPHC_TF_10:
+               /* ECN + DSCP (1 byte), Flow Label is elided. */
+               if (lowpan_fetch_skb(skb, tf, 1))
                        return -EINVAL;
 
-               hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
-               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
-               skb_pull(skb, 2);
+               /*  0 1 2 3 4 5 6 7
+                * +-+-+-+-+-+-+-+-+
+                * |ECN|   DSCP    |
+                * +-+-+-+-+-+-+-+-+
+                */
+               lowpan_iphc_tf_set_ecn(hdr, tf);
+               lowpan_iphc_tf_set_dscp(hdr, tf);
                break;
-       /* Traffic Class and Flow Label are elided */
-       case 3: /* 11b */
+       case LOWPAN_IPHC_TF_11:
+               /* Traffic Class and Flow Label are elided */
                break;
        default:
-               break;
+               WARN_ON_ONCE(1);
+               return -EINVAL;
        }
 
+       return 0;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = {
+       [LOWPAN_IPHC_HLIM_01] = 1,
+       [LOWPAN_IPHC_HLIM_10] = 64,
+       [LOWPAN_IPHC_HLIM_11] = 255,
+};
+
+int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
+                            const void *daddr, const void *saddr)
+{
+       struct ipv6hdr hdr = {};
+       u8 iphc0, iphc1;
+       int err;
+
+       raw_dump_table(__func__, "raw skb data dump uncompressed",
+                      skb->data, skb->len);
+
+       if (lowpan_fetch_skb(skb, &iphc0, sizeof(iphc0)) ||
+           lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
+               return -EINVAL;
+
+       /* another if the CID flag is set */
+       if (iphc1 & LOWPAN_IPHC_CID)
+               return -ENOTSUPP;
+
+       hdr.version = 6;
+
+       err = lowpan_iphc_tf_decompress(skb, &hdr,
+                                       iphc0 & LOWPAN_IPHC_TF_MASK);
+       if (err < 0)
+               return err;
+
        /* Next Header */
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+       if (!(iphc0 & LOWPAN_IPHC_NH)) {
                /* Next header is carried inline */
                if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
                        return -EINVAL;
@@ -306,35 +491,30 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
        }
 
        /* Hop Limit */
-       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) {
-               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+       if ((iphc0 & LOWPAN_IPHC_HLIM_MASK) != LOWPAN_IPHC_HLIM_00) {
+               hdr.hop_limit = lowpan_ttl_values[iphc0 & LOWPAN_IPHC_HLIM_MASK];
        } else {
                if (lowpan_fetch_skb(skb, &hdr.hop_limit,
                                     sizeof(hdr.hop_limit)))
                        return -EINVAL;
        }
 
-       /* Extract SAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
-
        if (iphc1 & LOWPAN_IPHC_SAC) {
                /* Source address context based uncompression */
                pr_debug("SAC bit is set. Handle context based source address.\n");
-               err = uncompress_context_based_src_addr(skb, &hdr.saddr, tmp);
+               err = uncompress_context_based_src_addr(skb, &hdr.saddr,
+                                                       iphc1 & LOWPAN_IPHC_SAM_MASK);
        } else {
                /* Source address uncompression */
                pr_debug("source address stateless compression\n");
-               err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
-                                     saddr_type, saddr_len);
+               err = uncompress_addr(skb, dev, &hdr.saddr,
+                                     iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
        }
 
        /* Check on error of previous branch */
        if (err)
                return -EINVAL;
 
-       /* Extract DAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
-
        /* check for Multicast Compression */
        if (iphc1 & LOWPAN_IPHC_M) {
                if (iphc1 & LOWPAN_IPHC_DAC) {
@@ -342,22 +522,22 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
                        /* TODO: implement this */
                } else {
                        err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
-                                                               tmp);
+                                                               iphc1 & LOWPAN_IPHC_DAM_MASK);
 
                        if (err)
                                return -EINVAL;
                }
        } else {
-               err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
-                                     daddr_type, daddr_len);
+               err = uncompress_addr(skb, dev, &hdr.daddr,
+                                     iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
                pr_debug("dest: stateless compression mode %d dest %pI6c\n",
-                        tmp, &hdr.daddr);
+                        iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
                if (err)
                        return -EINVAL;
        }
 
        /* Next header data uncompression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
+       if (iphc0 & LOWPAN_IPHC_NH) {
                err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
                if (err < 0)
                        return err;
@@ -367,7 +547,18 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
                        return err;
        }
 
-       hdr.payload_len = htons(skb->len);
+       switch (lowpan_priv(dev)->lltype) {
+       case LOWPAN_LLTYPE_IEEE802154:
+               if (lowpan_802154_cb(skb)->d_size)
+                       hdr.payload_len = htons(lowpan_802154_cb(skb)->d_size -
+                                               sizeof(struct ipv6hdr));
+               else
+                       hdr.payload_len = htons(skb->len);
+               break;
+       default:
+               hdr.payload_len = htons(skb->len);
+               break;
+       }
 
        pr_debug("skb headroom size = %d, data length = %d\n",
                 skb_headroom(skb), skb->len);
@@ -387,42 +578,176 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(lowpan_header_decompress);
 
-static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
-                                 const struct in6_addr *ipaddr,
-                                 const unsigned char *lladdr)
+static const u8 lowpan_iphc_dam_to_sam_value[] = {
+       [LOWPAN_IPHC_DAM_00] = LOWPAN_IPHC_SAM_00,
+       [LOWPAN_IPHC_DAM_01] = LOWPAN_IPHC_SAM_01,
+       [LOWPAN_IPHC_DAM_10] = LOWPAN_IPHC_SAM_10,
+       [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
+};
+
+static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
+                                 const unsigned char *lladdr, bool sam)
 {
-       u8 val = 0;
+       u8 dam = LOWPAN_IPHC_DAM_00;
 
        if (is_addr_mac_addr_based(ipaddr, lladdr)) {
-               val = 3; /* 0-bits */
+               dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
                pr_debug("address compression 0 bits\n");
        } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
                /* compress IID to 16 bits xxxx::XXXX */
                lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
-               val = 2; /* 16-bits */
+               dam = LOWPAN_IPHC_DAM_10; /* 16-bits */
                raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
                                *hc_ptr - 2, 2);
        } else {
                /* do not compress IID => xxxx::IID */
                lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
-               val = 1; /* 64-bits */
+               dam = LOWPAN_IPHC_DAM_01; /* 64-bits */
                raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
                                *hc_ptr - 8, 8);
        }
 
-       return rol8(val, shift);
+       if (sam)
+               return lowpan_iphc_dam_to_sam_value[dam];
+       else
+               return dam;
+}
+
+/* lowpan_iphc_get_tc - get the ECN + DCSP fields in hc format */
+static inline u8 lowpan_iphc_get_tc(const struct ipv6hdr *hdr)
+{
+       u8 dscp, ecn;
+
+       /* hdr->priority contains the higher bits of dscp, lower are part of
+        * flow_lbl[0]. Note ECN, DCSP is swapped in ipv6 hdr.
+        */
+       dscp = (hdr->priority << 2) | ((hdr->flow_lbl[0] & 0xc0) >> 6);
+       /* ECN is at the two lower bits from first nibble of flow_lbl[0] */
+       ecn = (hdr->flow_lbl[0] & 0x30);
+       /* for pretty debug output, also shift ecn to get the ecn value */
+       pr_debug("ecn 0x%02x dscp 0x%02x\n", ecn >> 4, dscp);
+       /* ECN is at 0x30 now, shift it to have ECN + DCSP */
+       return (ecn << 2) | dscp;
+}
+
+/* lowpan_iphc_is_flow_lbl_zero - check if flow label is zero */
+static inline bool lowpan_iphc_is_flow_lbl_zero(const struct ipv6hdr *hdr)
+{
+       return ((!(hdr->flow_lbl[0] & 0x0f)) &&
+               !hdr->flow_lbl[1] && !hdr->flow_lbl[2]);
+}
+
+/* lowpan_iphc_tf_compress - compress the traffic class which is set by
+ *     ipv6hdr. Return the corresponding format identifier which is used.
+ */
+static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
+{
+       /* get ecn dscp data in a byteformat as: ECN(hi) + DSCP(lo) */
+       u8 tc = lowpan_iphc_get_tc(hdr), tf[4], val;
+
+       /* printout the traffic class in hc format */
+       pr_debug("tc 0x%02x\n", tc);
+
+       if (lowpan_iphc_is_flow_lbl_zero(hdr)) {
+               if (!tc) {
+                       /* 11:  Traffic Class and Flow Label are elided. */
+                       val = LOWPAN_IPHC_TF_11;
+               } else {
+                       /* 10:  ECN + DSCP (1 byte), Flow Label is elided.
+                        *
+                        *  0 1 2 3 4 5 6 7
+                        * +-+-+-+-+-+-+-+-+
+                        * |ECN|   DSCP    |
+                        * +-+-+-+-+-+-+-+-+
+                        */
+                       lowpan_push_hc_data(hc_ptr, &tc, sizeof(tc));
+                       val = LOWPAN_IPHC_TF_10;
+               }
+       } else {
+               /* check if dscp is zero, it's after the first two bit */
+               if (!(tc & 0x3f)) {
+                       /* 01:  ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+                        *
+                        *                     1                   2
+                        * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |ECN|rsv|             Flow Label                |
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        */
+                       memcpy(&tf[0], &hdr->flow_lbl[0], 3);
+                       /* zero the highest 4-bits, contains DCSP + ECN */
+                       tf[0] &= ~0xf0;
+                       /* set ECN */
+                       tf[0] |= (tc & 0xc0);
+
+                       lowpan_push_hc_data(hc_ptr, tf, 3);
+                       val = LOWPAN_IPHC_TF_01;
+               } else {
+                       /* 00:  ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+                        *
+                        *                      1                   2                   3
+                        *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |ECN|   DSCP    |  rsv  |             Flow Label                |
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        */
+                       memcpy(&tf[0], &tc, sizeof(tc));
+                       /* highest nibble of flow_lbl[0] is part of DSCP + ECN
+                        * which will be the 4-bit pad and will be filled with
+                        * zeros afterwards.
+                        */
+                       memcpy(&tf[1], &hdr->flow_lbl[0], 3);
+                       /* zero the 4-bit pad, which is reserved */
+                       tf[1] &= ~0xf0;
+
+                       lowpan_push_hc_data(hc_ptr, tf, 4);
+                       val = LOWPAN_IPHC_TF_00;
+               }
+       }
+
+       return val;
 }
 
-int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
-                          unsigned short type, const void *_daddr,
-                          const void *_saddr, unsigned int len)
+static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
+                                         const struct in6_addr *ipaddr)
 {
-       u8 tmp, iphc0, iphc1, *hc_ptr;
+       u8 val;
+
+       if (lowpan_is_mcast_addr_compressable8(ipaddr)) {
+               pr_debug("compressed to 1 octet\n");
+               /* use last byte */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[15], 1);
+               val = LOWPAN_IPHC_DAM_11;
+       } else if (lowpan_is_mcast_addr_compressable32(ipaddr)) {
+               pr_debug("compressed to 4 octets\n");
+               /* second byte + the last three */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[1], 1);
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[13], 3);
+               val = LOWPAN_IPHC_DAM_10;
+       } else if (lowpan_is_mcast_addr_compressable48(ipaddr)) {
+               pr_debug("compressed to 6 octets\n");
+               /* second byte + the last five */
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[1], 1);
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[11], 5);
+               val = LOWPAN_IPHC_DAM_01;
+       } else {
+               pr_debug("using full address\n");
+               lowpan_push_hc_data(hc_ptr, ipaddr->s6_addr, 16);
+               val = LOWPAN_IPHC_DAM_00;
+       }
+
+       return val;
+}
+
+int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
+                          const void *daddr, const void *saddr)
+{
+       u8 iphc0, iphc1, *hc_ptr;
        struct ipv6hdr *hdr;
-       u8 head[100] = {};
+       u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
        int ret, addr_type;
 
-       if (type != ETH_P_IPV6)
+       if (skb->protocol != htons(ETH_P_IPV6))
                return -EINVAL;
 
        hdr = ipv6_hdr(skb);
@@ -446,63 +771,26 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
 
        /* TODO: context lookup */
 
-       raw_dump_inline(__func__, "saddr",
-                       (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
-       raw_dump_inline(__func__, "daddr",
-                       (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+       raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
+       raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
 
        raw_dump_table(__func__, "sending raw skb network uncompressed packet",
                       skb->data, skb->len);
 
-       /* Traffic class, flow label
-        * If flow label is 0, compress it. If traffic class is 0, compress it
-        * We have to process both in the same time as the offset of traffic
-        * class depends on the presence of version and flow label
-        */
-
-       /* hc format of TC is ECN | DSCP , original one is DSCP | ECN */
-       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
-       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
-
-       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
-           (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
-               /* flow label can be compressed */
-               iphc0 |= LOWPAN_IPHC_FL_C;
-               if ((hdr->priority == 0) &&
-                   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress (elide) all */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-               } else {
-                       /* compress only the flow label */
-                       *hc_ptr = tmp;
-                       hc_ptr += 1;
-               }
-       } else {
-               /* Flow label cannot be compressed */
-               if ((hdr->priority == 0) &&
-                   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress only traffic class */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-                       *hc_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
-                       memcpy(hc_ptr + 1, &hdr->flow_lbl[1], 2);
-                       hc_ptr += 3;
-               } else {
-                       /* compress nothing */
-                       memcpy(hc_ptr, hdr, 4);
-                       /* replace the top byte with new ECN | DSCP format */
-                       *hc_ptr = tmp;
-                       hc_ptr += 4;
-               }
-       }
+       /* Traffic Class, Flow Label compression */
+       iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
 
        /* NOTE: payload length is always compressed */
 
        /* Check if we provide the nhc format for nexthdr and compression
         * functionality. If not nexthdr is handled inline and not compressed.
         */
-       ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr, &iphc0);
-       if (ret < 0)
-               return ret;
+       ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr);
+       if (ret == -ENOENT)
+               lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
+                                   sizeof(hdr->nexthdr));
+       else
+               iphc0 |= LOWPAN_IPHC_NH;
 
        /* Hop limit
         * if 1:   compress, encoding is 01
@@ -512,13 +800,13 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
         */
        switch (hdr->hop_limit) {
        case 1:
-               iphc0 |= LOWPAN_IPHC_TTL_1;
+               iphc0 |= LOWPAN_IPHC_HLIM_01;
                break;
        case 64:
-               iphc0 |= LOWPAN_IPHC_TTL_64;
+               iphc0 |= LOWPAN_IPHC_HLIM_10;
                break;
        case 255:
-               iphc0 |= LOWPAN_IPHC_TTL_255;
+               iphc0 |= LOWPAN_IPHC_HLIM_11;
                break;
        default:
                lowpan_push_hc_data(&hc_ptr, &hdr->hop_limit,
@@ -532,9 +820,8 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
                iphc1 |= LOWPAN_IPHC_SAC;
        } else {
                if (addr_type & IPV6_ADDR_LINKLOCAL) {
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr,
-                                                        LOWPAN_IPHC_SAM_BIT,
-                                                        &hdr->saddr, _saddr);
+                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
+                                                        saddr, true);
                        pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
                                 &hdr->saddr, iphc1);
                } else {
@@ -548,38 +835,12 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
        if (addr_type & IPV6_ADDR_MULTICAST) {
                pr_debug("destination address is multicast: ");
                iphc1 |= LOWPAN_IPHC_M;
-               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
-                       pr_debug("compressed to 1 octet\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_11;
-                       /* use last byte */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[15], 1);
-               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
-                       pr_debug("compressed to 4 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_10;
-                       /* second byte + the last three */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[1], 1);
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[13], 3);
-               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
-                       pr_debug("compressed to 6 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_01;
-                       /* second byte + the last five */
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[1], 1);
-                       lowpan_push_hc_data(&hc_ptr,
-                                           &hdr->daddr.s6_addr[11], 5);
-               } else {
-                       pr_debug("using full address\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_00;
-                       lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
-               }
+               iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
        } else {
                if (addr_type & IPV6_ADDR_LINKLOCAL) {
                        /* TODO: context lookup */
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr,
-                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
+                                                        daddr, false);
                        pr_debug("dest address unicast link-local %pI6c "
                                 "iphc1 0x%02x\n", &hdr->daddr, iphc1);
                } else {
@@ -589,7 +850,7 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
        }
 
        /* next header compression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
+       if (iphc0 & LOWPAN_IPHC_NH) {
                ret = lowpan_nhc_do_compression(skb, hdr, &hc_ptr);
                if (ret < 0)
                        return ret;
@@ -610,19 +871,3 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
        return 0;
 }
 EXPORT_SYMBOL_GPL(lowpan_header_compress);
-
-static int __init lowpan_module_init(void)
-{
-       request_module_nowait("nhc_dest");
-       request_module_nowait("nhc_fragment");
-       request_module_nowait("nhc_hop");
-       request_module_nowait("nhc_ipv6");
-       request_module_nowait("nhc_mobility");
-       request_module_nowait("nhc_routing");
-       request_module_nowait("nhc_udp");
-
-       return 0;
-}
-module_init(lowpan_module_init);
-
-MODULE_LICENSE("GPL");