Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15ec80f5 authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 4cffa13d 65d892c8
Loading
Loading
Loading
Loading
+187 −99
Original line number Diff line number Diff line
@@ -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 */
	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;
	}

/*
 * 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).
	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_addr(struct sk_buff *skb, struct in6_addr *ipaddr,
	u8 const *prefix, u8 pref_post_count, unsigned char *lladdr)
lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
		struct in6_addr *ipaddr,
		const u8 dam)
{
	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)
	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;
	}

		/* no IID based configuration if no prefix and no data */
		lowpan_uip_ds6_set_addr_iid(ipaddr, lladdr);
	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 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
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;

	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, lowpan_llprefix,
				lowpan_unc_llconf[tmp], skb->data);
		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;
	}
+17 −3
Original line number Diff line number Diff line
@@ -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__ */