Merge branch '6lowpan'
Alexander Aring says: ==================== 6lowpan: address uncompression fixes The current implementation to uncompress addresses in a 6lowpan header is completely broken. This patch series fixes the parsing of addresses in a 6lowpan header. It contains a major rewrite of the uncompress address function to parse the address in a correct way. Tested with the ravenusbstick(contiki 6LoWPAN stack) and beaglebone (linux 6LoWPAN Stack) on the other side. The linux side contains all possible addresses for the uncompression cases. Then I type a ping6 for each case and lookup in wireshark and dmesg the correct reconstruction. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
15ec80f5d4
@ -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)
|
||||
{
|
||||
memcpy(&ipaddr->s6_addr[8], lladdr, IEEE802154_ADDR_LEN);
|
||||
/* second bit-flip (Universe/Local) is done according RFC2464 */
|
||||
ipaddr->s6_addr[8] ^= 0x02;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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,
|
||||
u8 const *prefix, u8 pref_post_count, unsigned char *lladdr)
|
||||
lowpan_uncompress_addr(struct sk_buff *skb,
|
||||
struct in6_addr *ipaddr,
|
||||
const u8 address_mode,
|
||||
const struct ieee802154_addr *lladdr)
|
||||
{
|
||||
u8 prefcount = pref_post_count >> 4;
|
||||
u8 postcount = pref_post_count & 0x0f;
|
||||
bool fail;
|
||||
|
||||
/* 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)
|
||||
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;
|
||||
|
||||
/* no IID based configuration if no prefix and no data */
|
||||
lowpan_uip_ds6_set_addr_iid(ipaddr, lladdr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("Invalid address mode value: 0x%x\n", address_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("uncompressing %d + %d => ", prefcount, postcount);
|
||||
lowpan_raw_dump_inline(NULL, NULL, ipaddr->s6_addr, 16);
|
||||
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 address function for source context
|
||||
* based address(non-multicast).
|
||||
*/
|
||||
static int
|
||||
lowpan_uncompress_context_based_src_addr(struct sk_buff *skb,
|
||||
struct in6_addr *ipaddr,
|
||||
const u8 sam)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -193,10 +193,12 @@
|
||||
/* Values of fields within the IPHC encoding second byte */
|
||||
#define LOWPAN_IPHC_CID 0x80
|
||||
|
||||
#define LOWPAN_IPHC_ADDR_00 0x00
|
||||
#define LOWPAN_IPHC_ADDR_01 0x01
|
||||
#define LOWPAN_IPHC_ADDR_02 0x02
|
||||
#define LOWPAN_IPHC_ADDR_03 0x03
|
||||
|
||||
#define LOWPAN_IPHC_SAC 0x40
|
||||
#define LOWPAN_IPHC_SAM_00 0x00
|
||||
#define LOWPAN_IPHC_SAM_01 0x10
|
||||
#define LOWPAN_IPHC_SAM_10 0x20
|
||||
#define LOWPAN_IPHC_SAM 0x30
|
||||
|
||||
#define LOWPAN_IPHC_SAM_BIT 4
|
||||
@ -230,4 +232,16 @@
|
||||
dest = 16 bit inline */
|
||||
#define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */
|
||||
|
||||
static inline bool lowpan_fetch_skb(struct sk_buff *skb,
|
||||
void *data, const unsigned int len)
|
||||
{
|
||||
if (unlikely(!pskb_may_pull(skb, len)))
|
||||
return true;
|
||||
|
||||
skb_copy_from_linear_data(skb, data, len);
|
||||
skb_pull(skb, len);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* __6LOWPAN_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user