diff options
Diffstat (limited to 'net/ieee802154')
| -rw-r--r-- | net/ieee802154/6lowpan.c | 1520 | ||||
| -rw-r--r-- | net/ieee802154/6lowpan.h | 247 | ||||
| -rw-r--r-- | net/ieee802154/6lowpan_iphc.c | 801 | ||||
| -rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 683 | ||||
| -rw-r--r-- | net/ieee802154/Kconfig | 9 | ||||
| -rw-r--r-- | net/ieee802154/Makefile | 7 | ||||
| -rw-r--r-- | net/ieee802154/af802154.h | 5 | ||||
| -rw-r--r-- | net/ieee802154/af_ieee802154.c | 22 | ||||
| -rw-r--r-- | net/ieee802154/dgram.c | 161 | ||||
| -rw-r--r-- | net/ieee802154/header_ops.c | 325 | ||||
| -rw-r--r-- | net/ieee802154/ieee802154.h | 41 | ||||
| -rw-r--r-- | net/ieee802154/netlink.c | 66 | ||||
| -rw-r--r-- | net/ieee802154/nl-mac.c | 1084 | ||||
| -rw-r--r-- | net/ieee802154/nl-phy.c | 43 | ||||
| -rw-r--r-- | net/ieee802154/nl_policy.c | 26 | ||||
| -rw-r--r-- | net/ieee802154/raw.c | 18 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.c | 585 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.h | 41 | ||||
| -rw-r--r-- | net/ieee802154/wpan-class.c | 4 | 
19 files changed, 3707 insertions, 1981 deletions
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c deleted file mode 100644 index c85e71e0c7f..00000000000 --- a/net/ieee802154/6lowpan.c +++ /dev/null @@ -1,1520 +0,0 @@ -/* - * Copyright 2011, Siemens AG - * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -/* - * Based on patches from Jon Smirl <jonsmirl@gmail.com> - * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* Jon's code is based on 6lowpan implementation for Contiki which is: - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - *    notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - *    notice, this list of conditions and the following disclaimer in the - *    documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - *    may be used to endorse or promote products derived from this software - *    without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <linux/bitops.h> -#include <linux/if_arp.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/netdevice.h> -#include <net/af_ieee802154.h> -#include <net/ieee802154.h> -#include <net/ieee802154_netdev.h> -#include <net/ipv6.h> - -#include "6lowpan.h" - -/* TTL uncompression values */ -static const u8 lowpan_ttl_values[] = {0, 1, 64, 255}; - -static LIST_HEAD(lowpan_devices); - -/* private device info */ -struct lowpan_dev_info { -	struct net_device	*real_dev; /* real WPAN device ptr */ -	struct mutex		dev_list_mtx; /* mutex for list ops */ -	unsigned short		fragment_tag; -}; - -struct lowpan_dev_record { -	struct net_device *ldev; -	struct list_head list; -}; - -struct lowpan_fragment { -	struct sk_buff		*skb;		/* skb to be assembled */ -	u16			length;		/* length to be assemled */ -	u32			bytes_rcv;	/* bytes received */ -	u16			tag;		/* current fragment tag */ -	struct timer_list	timer;		/* assembling timer */ -	struct list_head	list;		/* fragments list */ -}; - -static LIST_HEAD(lowpan_fragments); -static DEFINE_SPINLOCK(flist_lock); - -static inline struct -lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) -{ -	return netdev_priv(dev); -} - -static inline void lowpan_address_flip(u8 *src, u8 *dest) -{ -	int i; -	for (i = 0; i < IEEE802154_ADDR_LEN; i++) -		(dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i]; -} - -/* list of all 6lowpan devices, uses for package delivering */ -/* print data in line */ -static inline void lowpan_raw_dump_inline(const char *caller, char *msg, -				   unsigned char *buf, int len) -{ -#ifdef DEBUG -	if (msg) -		pr_debug("(%s) %s: ", caller, msg); -	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, -		       16, 1, buf, len, false); -#endif /* DEBUG */ -} - -/* - * print data in a table format: - * - * addr: xx xx xx xx xx xx - * addr: xx xx xx xx xx xx - * ... - */ -static inline void lowpan_raw_dump_table(const char *caller, char *msg, -				   unsigned char *buf, int len) -{ -#ifdef DEBUG -	if (msg) -		pr_debug("(%s) %s:\n", caller, msg); -	print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, -		       16, 1, buf, len, false); -#endif /* DEBUG */ -} - -static u8 -lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift, const struct in6_addr *ipaddr, -		 const unsigned char *lladdr) -{ -	u8 val = 0; - -	if (is_addr_mac_addr_based(ipaddr, lladdr)) -		val = 3; /* 0-bits */ -	else if (lowpan_is_iid_16_bit_compressable(ipaddr)) { -		/* compress IID to 16 bits xxxx::XXXX */ -		memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2); -		*hc06_ptr += 2; -		val = 2; /* 16-bits */ -	} else { -		/* do not compress IID => xxxx::IID */ -		memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8); -		*hc06_ptr += 8; -		val = 1; /* 64-bits */ -	} - -	return rol8(val, shift); -} - -/* - * 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) -{ -	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 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; -} - -static void -lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) -{ -	struct udphdr *uh = udp_hdr(skb); - -	if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) == -				LOWPAN_NHC_UDP_4BIT_PORT) && -	    ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) == -				LOWPAN_NHC_UDP_4BIT_PORT)) { -		pr_debug("UDP header: both ports compression to 4 bits\n"); -		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_11; -		**(hc06_ptr + 1) = /* subtraction is faster */ -		   (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) + -		       ((uh->source & LOWPAN_NHC_UDP_4BIT_PORT) << 4)); -		*hc06_ptr += 2; -	} else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) == -			LOWPAN_NHC_UDP_8BIT_PORT) { -		pr_debug("UDP header: remove 8 bits of dest\n"); -		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_01; -		memcpy(*hc06_ptr + 1, &uh->source, 2); -		**(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT); -		*hc06_ptr += 4; -	} else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) == -			LOWPAN_NHC_UDP_8BIT_PORT) { -		pr_debug("UDP header: remove 8 bits of source\n"); -		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_10; -		memcpy(*hc06_ptr + 1, &uh->dest, 2); -		**(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT); -		*hc06_ptr += 4; -	} else { -		pr_debug("UDP header: can't compress\n"); -		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_00; -		memcpy(*hc06_ptr + 1, &uh->source, 2); -		memcpy(*hc06_ptr + 3, &uh->dest, 2); -		*hc06_ptr += 5; -	} - -	/* checksum is always inline */ -	memcpy(*hc06_ptr, &uh->check, 2); -	*hc06_ptr += 2; - -	/* skip the UDP header */ -	skb_pull(skb, sizeof(struct udphdr)); -} - -static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) -{ -	if (unlikely(!pskb_may_pull(skb, 1))) -		return -EINVAL; - -	*val = skb->data[0]; -	skb_pull(skb, 1); - -	return 0; -} - -static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) -{ -	if (unlikely(!pskb_may_pull(skb, 2))) -		return -EINVAL; - -	*val = (skb->data[0] << 8) | skb->data[1]; -	skb_pull(skb, 2); - -	return 0; -} - -static int -lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh) -{ -	u8 tmp; - -	if (!uh) -		goto err; - -	if (lowpan_fetch_skb_u8(skb, &tmp)) -		goto err; - -	if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { -		pr_debug("UDP header uncompression\n"); -		switch (tmp & LOWPAN_NHC_UDP_CS_P_11) { -		case LOWPAN_NHC_UDP_CS_P_00: -			memcpy(&uh->source, &skb->data[0], 2); -			memcpy(&uh->dest, &skb->data[2], 2); -			skb_pull(skb, 4); -			break; -		case LOWPAN_NHC_UDP_CS_P_01: -			memcpy(&uh->source, &skb->data[0], 2); -			uh->dest = -			   skb->data[2] + LOWPAN_NHC_UDP_8BIT_PORT; -			skb_pull(skb, 3); -			break; -		case LOWPAN_NHC_UDP_CS_P_10: -			uh->source = skb->data[0] + LOWPAN_NHC_UDP_8BIT_PORT; -			memcpy(&uh->dest, &skb->data[1], 2); -			skb_pull(skb, 3); -			break; -		case LOWPAN_NHC_UDP_CS_P_11: -			uh->source = -			   LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] >> 4); -			uh->dest = -			   LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] & 0x0f); -			skb_pull(skb, 1); -			break; -		default: -			pr_debug("ERROR: unknown UDP format\n"); -			goto err; -			break; -		} - -		pr_debug("uncompressed UDP ports: src = %d, dst = %d\n", -			 uh->source, uh->dest); - -		/* copy checksum */ -		memcpy(&uh->check, &skb->data[0], 2); -		skb_pull(skb, 2); - -		/* -		 * UDP lenght needs to be infered from the lower layers -		 * here, we obtain the hint from the remaining size of the -		 * frame -		 */ -		uh->len = htons(skb->len + sizeof(struct udphdr)); -		pr_debug("uncompressed UDP length: src = %d", uh->len); -	} else { -		pr_debug("ERROR: unsupported NH format\n"); -		goto err; -	} - -	return 0; -err: -	return -EINVAL; -} - -static int lowpan_header_create(struct sk_buff *skb, -			   struct net_device *dev, -			   unsigned short type, const void *_daddr, -			   const void *_saddr, unsigned int len) -{ -	u8 tmp, iphc0, iphc1, *hc06_ptr; -	struct ipv6hdr *hdr; -	const u8 *saddr = _saddr; -	const u8 *daddr = _daddr; -	u8 head[100]; -	struct ieee802154_addr sa, da; - -	/* TODO: -	 * if this package isn't ipv6 one, where should it be routed? -	 */ -	if (type != ETH_P_IPV6) -		return 0; - -	hdr = ipv6_hdr(skb); -	hc06_ptr = head + 2; - -	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n" -		 "\tnexthdr = 0x%02x\n\thop_lim = %d\n", hdr->version, -		 ntohs(hdr->payload_len), hdr->nexthdr, hdr->hop_limit); - -	lowpan_raw_dump_table(__func__, "raw skb network header dump", -		skb_network_header(skb), sizeof(struct ipv6hdr)); - -	if (!saddr) -		saddr = dev->dev_addr; - -	lowpan_raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); - -	/* -	 * As we copy some bit-length fields, in the IPHC encoding bytes, -	 * we sometimes use |= -	 * If the field is 0, and the current bit value in memory is 1, -	 * this does not work. We therefore reset the IPHC encoding here -	 */ -	iphc0 = LOWPAN_DISPATCH_IPHC; -	iphc1 = 0; - -	/* TODO: context lookup */ - -	lowpan_raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); - -	/* -	 * 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 -	 */ - -	/* hc06 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 */ -			*hc06_ptr = tmp; -			hc06_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; -			*hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F); -			memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2); -			hc06_ptr += 3; -		} else { -			/* compress nothing */ -			memcpy(hc06_ptr, &hdr, 4); -			/* replace the top byte with new ECN | DSCP format */ -			*hc06_ptr = tmp; -			hc06_ptr += 4; -		} -	} - -	/* NOTE: payload length is always compressed */ - -	/* Next Header is compress if UDP */ -	if (hdr->nexthdr == UIP_PROTO_UDP) -		iphc0 |= LOWPAN_IPHC_NH_C; - -	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { -		*hc06_ptr = hdr->nexthdr; -		hc06_ptr += 1; -	} - -	/* -	 * Hop limit -	 * if 1:   compress, encoding is 01 -	 * if 64:  compress, encoding is 10 -	 * if 255: compress, encoding is 11 -	 * else do not compress -	 */ -	switch (hdr->hop_limit) { -	case 1: -		iphc0 |= LOWPAN_IPHC_TTL_1; -		break; -	case 64: -		iphc0 |= LOWPAN_IPHC_TTL_64; -		break; -	case 255: -		iphc0 |= LOWPAN_IPHC_TTL_255; -		break; -	default: -		*hc06_ptr = hdr->hop_limit; -		hc06_ptr += 1; -		break; -	} - -	/* source address compression */ -	if (is_addr_unspecified(&hdr->saddr)) { -		pr_debug("source address is unspecified, setting SAC\n"); -		iphc1 |= LOWPAN_IPHC_SAC; -	/* TODO: context lookup */ -	} else if (is_addr_link_local(&hdr->saddr)) { -		pr_debug("source address is link-local\n"); -		iphc1 |= lowpan_compress_addr_64(&hc06_ptr, -				LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr); -	} else { -		pr_debug("send the full source address\n"); -		memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16); -		hc06_ptr += 16; -	} - -	/* destination address compression */ -	if (is_addr_mcast(&hdr->daddr)) { -		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 */ -			*hc06_ptr = hdr->daddr.s6_addr[15]; -			hc06_ptr += 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 */ -			*hc06_ptr = hdr->daddr.s6_addr[1]; -			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3); -			hc06_ptr += 4; -		} 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 */ -			*hc06_ptr = hdr->daddr.s6_addr[1]; -			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5); -			hc06_ptr += 6; -		} else { -			pr_debug("using full address\n"); -			iphc1 |= LOWPAN_IPHC_DAM_00; -			memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16); -			hc06_ptr += 16; -		} -	} else { -		/* TODO: context lookup */ -		if (is_addr_link_local(&hdr->daddr)) { -			pr_debug("dest address is unicast and link-local\n"); -			iphc1 |= lowpan_compress_addr_64(&hc06_ptr, -				LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr); -		} else { -			pr_debug("dest address is unicast: using full one\n"); -			memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16); -			hc06_ptr += 16; -		} -	} - -	/* UDP header compression */ -	if (hdr->nexthdr == UIP_PROTO_UDP) -		lowpan_compress_udp_header(&hc06_ptr, skb); - -	head[0] = iphc0; -	head[1] = iphc1; - -	skb_pull(skb, sizeof(struct ipv6hdr)); -	memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head); - -	lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, -				skb->len); - -	/* -	 * NOTE1: I'm still unsure about the fact that compression and WPAN -	 * header are created here and not later in the xmit. So wait for -	 * an opinion of net maintainers. -	 */ -	/* -	 * NOTE2: to be absolutely correct, we must derive PANid information -	 * from MAC subif of the 'dev' and 'real_dev' network devices, but -	 * this isn't implemented in mainline yet, so currently we assign 0xff -	 */ -	{ -		mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; -		mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); - -		/* prepare wpan address data */ -		sa.addr_type = IEEE802154_ADDR_LONG; -		sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - -		memcpy(&(sa.hwaddr), saddr, 8); -		/* intra-PAN communications */ -		da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - -		/* -		 * if the destination address is the broadcast address, use the -		 * corresponding short address -		 */ -		if (lowpan_is_addr_broadcast(daddr)) { -			da.addr_type = IEEE802154_ADDR_SHORT; -			da.short_addr = IEEE802154_ADDR_BROADCAST; -		} else { -			da.addr_type = IEEE802154_ADDR_LONG; -			memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN); - -			/* request acknowledgment */ -			mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; -		} - -		return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, -				type, (void *)&da, (void *)&sa, skb->len); -	} -} - -static int lowpan_give_skb_to_devices(struct sk_buff *skb) -{ -	struct lowpan_dev_record *entry; -	struct sk_buff *skb_cp; -	int stat = NET_RX_SUCCESS; - -	rcu_read_lock(); -	list_for_each_entry_rcu(entry, &lowpan_devices, list) -		if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { -			skb_cp = skb_copy(skb, GFP_ATOMIC); -			if (!skb_cp) { -				stat = -ENOMEM; -				break; -			} - -			skb_cp->dev = entry->ldev; -			stat = netif_rx(skb_cp); -		} -	rcu_read_unlock(); - -	return stat; -} - -static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr) -{ -	struct sk_buff *new; -	int stat = NET_RX_SUCCESS; - -	new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), -								GFP_ATOMIC); -	kfree_skb(skb); - -	if (!new) -		return -ENOMEM; - -	skb_push(new, sizeof(struct ipv6hdr)); -	skb_reset_network_header(new); -	skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); - -	new->protocol = htons(ETH_P_IPV6); -	new->pkt_type = PACKET_HOST; - -	stat = lowpan_give_skb_to_devices(new); - -	kfree_skb(new); - -	return stat; -} - -static void lowpan_fragment_timer_expired(unsigned long entry_addr) -{ -	struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr; - -	pr_debug("timer expired for frame with tag %d\n", entry->tag); - -	list_del(&entry->list); -	dev_kfree_skb(entry->skb); -	kfree(entry); -} - -static struct lowpan_fragment * -lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag) -{ -	struct lowpan_fragment *frame; - -	frame = kzalloc(sizeof(struct lowpan_fragment), -			GFP_ATOMIC); -	if (!frame) -		goto frame_err; - -	INIT_LIST_HEAD(&frame->list); - -	frame->length = len; -	frame->tag = tag; - -	/* allocate buffer for frame assembling */ -	frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length + -					       sizeof(struct ipv6hdr)); - -	if (!frame->skb) -		goto skb_err; - -	frame->skb->priority = skb->priority; -	frame->skb->dev = skb->dev; - -	/* reserve headroom for uncompressed ipv6 header */ -	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; -	frame->timer.data = (unsigned long)frame; -	frame->timer.function = lowpan_fragment_timer_expired; - -	add_timer(&frame->timer); - -	list_add_tail(&frame->list, &lowpan_fragments); - -	return frame; - -skb_err: -	kfree(frame); -frame_err: -	return NULL; -} - -static int -lowpan_process_data(struct sk_buff *skb) -{ -	struct ipv6hdr hdr = {}; -	u8 tmp, iphc0, iphc1, num_context = 0; -	const struct ieee802154_addr *_saddr, *_daddr; -	int err; - -	lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, -				skb->len); -	/* at least two bytes will be used for the encoding */ -	if (skb->len < 2) -		goto drop; - -	if (lowpan_fetch_skb_u8(skb, &iphc0)) -		goto drop; - -	/* fragments assembling */ -	switch (iphc0 & LOWPAN_DISPATCH_MASK) { -	case LOWPAN_DISPATCH_FRAG1: -	case LOWPAN_DISPATCH_FRAGN: -	{ -		struct lowpan_fragment *frame; -		/* slen stores the rightmost 8 bits of the 11 bits length */ -		u8 slen, offset = 0; -		u16 len, tag; -		bool found = false; - -		if (lowpan_fetch_skb_u8(skb, &slen) || /* frame length */ -		    lowpan_fetch_skb_u16(skb, &tag))  /* fragment tag */ -			goto drop; - -		/* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */ -		len = ((iphc0 & 7) << 8) | slen; - -		if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) { -			pr_debug("%s received a FRAG1 packet (tag: %d, " -				 "size of the entire IP packet: %d)", -				 __func__, tag, len); -		} else { /* FRAGN */ -			if (lowpan_fetch_skb_u8(skb, &offset)) -				goto unlock_and_drop; -			pr_debug("%s received a FRAGN packet (tag: %d, " -				 "size of the entire IP packet: %d, " -				 "offset: %d)", __func__, tag, len, offset * 8); -		} - -		/* -		 * check if frame assembling with the same tag is -		 * already in progress -		 */ -		spin_lock_bh(&flist_lock); - -		list_for_each_entry(frame, &lowpan_fragments, list) -			if (frame->tag == tag) { -				found = true; -				break; -			} - -		/* alloc new frame structure */ -		if (!found) { -			pr_debug("%s first fragment received for tag %d, " -				 "begin packet reassembly", __func__, tag); -			frame = lowpan_alloc_new_frame(skb, len, tag); -			if (!frame) -				goto unlock_and_drop; -		} - -		/* if payload fits buffer, copy it */ -		if (likely((offset * 8 + skb->len) <= frame->length)) -			skb_copy_to_linear_data_offset(frame->skb, offset * 8, -							skb->data, skb->len); -		else -			goto unlock_and_drop; - -		frame->bytes_rcv += skb->len; - -		/* frame assembling complete */ -		if ((frame->bytes_rcv == frame->length) && -		     frame->timer.expires > jiffies) { -			/* if timer haven't expired - first of all delete it */ -			del_timer_sync(&frame->timer); -			list_del(&frame->list); -			spin_unlock_bh(&flist_lock); - -			pr_debug("%s successfully reassembled fragment " -				 "(tag %d)", __func__, tag); - -			dev_kfree_skb(skb); -			skb = frame->skb; -			kfree(frame); - -			if (lowpan_fetch_skb_u8(skb, &iphc0)) -				goto drop; - -			break; -		} -		spin_unlock_bh(&flist_lock); - -		return kfree_skb(skb), 0; -	} -	default: -		break; -	} - -	if (lowpan_fetch_skb_u8(skb, &iphc1)) -		goto drop; - -	_saddr = &mac_cb(skb)->sa; -	_daddr = &mac_cb(skb)->da; - -	pr_debug("iphc0 = %02x, iphc1 = %02x\n", iphc0, iphc1); - -	/* 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_u8(skb, &num_context)) -			goto drop; -	} - -	hdr.version = 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) -	 */ -	case 0: /* 00b */ -		if (lowpan_fetch_skb_u8(skb, &tmp)) -			goto drop; - -		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); -		break; -	/* -	 * Traffic class carried in-line -	 * ECN + DSCP (1 byte), Flow Label is elided -	 */ -	case 1: /* 10b */ -		if (lowpan_fetch_skb_u8(skb, &tmp)) -			goto drop; - -		hdr.priority = ((tmp >> 2) & 0x0f); -		hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30); -		break; -	/* -	 * Flow Label carried in-line -	 * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided -	 */ -	case 2: /* 01b */ -		if (lowpan_fetch_skb_u8(skb, &tmp)) -			goto drop; - -		hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30); -		memcpy(&hdr.flow_lbl[1], &skb->data[0], 2); -		skb_pull(skb, 2); -		break; -	/* Traffic Class and Flow Label are elided */ -	case 3: /* 11b */ -		break; -	default: -		break; -	} - -	/* Next Header */ -	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { -		/* Next header is carried inline */ -		if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr))) -			goto drop; - -		pr_debug("NH flag is set, next header carried inline: %02x\n", -			 hdr.nexthdr); -	} - -	/* Hop Limit */ -	if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) -		hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03]; -	else { -		if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit))) -			goto drop; -	} - -	/* 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, tmp, _saddr); -	} - -	/* Check on error of previous branch */ -	if (err) -		goto drop; - -	/* 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) { -			pr_debug("dest: context-based mcast compression\n"); -			/* TODO: implement this */ -		} else { -			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, tmp, _daddr); -		if (err) -			goto drop; -	} - -	/* UDP data uncompression */ -	if (iphc0 & LOWPAN_IPHC_NH_C) { -		struct udphdr uh; -		struct sk_buff *new; -		if (lowpan_uncompress_udp_header(skb, &uh)) -			goto drop; - -		/* -		 * replace the compressed UDP head by the uncompressed UDP -		 * header -		 */ -		new = skb_copy_expand(skb, sizeof(struct udphdr), -				      skb_tailroom(skb), GFP_ATOMIC); -		kfree_skb(skb); - -		if (!new) -			return -ENOMEM; - -		skb = new; - -		skb_push(skb, sizeof(struct udphdr)); -		skb_reset_transport_header(skb); -		skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr)); - -		lowpan_raw_dump_table(__func__, "raw UDP header dump", -				      (u8 *)&uh, sizeof(uh)); - -		hdr.nexthdr = UIP_PROTO_UDP; -	} - -	/* Not fragmented package */ -	hdr.payload_len = htons(skb->len); - -	pr_debug("skb headroom size = %d, data length = %d\n", -		 skb_headroom(skb), skb->len); - -	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t" -		 "nexthdr = 0x%02x\n\thop_lim = %d\n", hdr.version, -		 ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit); - -	lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, -							sizeof(hdr)); -	return lowpan_skb_deliver(skb, &hdr); - -unlock_and_drop: -	spin_unlock_bh(&flist_lock); -drop: -	kfree_skb(skb); -	return -EINVAL; -} - -static int lowpan_set_address(struct net_device *dev, void *p) -{ -	struct sockaddr *sa = p; - -	if (netif_running(dev)) -		return -EBUSY; - -	/* TODO: validate addr */ -	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); - -	return 0; -} - -static int lowpan_get_mac_header_length(struct sk_buff *skb) -{ -	/* -	 * Currently long addressing mode is supported only, so the overall -	 * header size is 21: -	 * FC SeqNum DPAN DA  SA  Sec -	 * 2  +  1  +  2 + 8 + 8 + 0  = 21 -	 */ -	return 21; -} - -static int -lowpan_fragment_xmit(struct sk_buff *skb, u8 *head, -			int mlen, int plen, int offset, int type) -{ -	struct sk_buff *frag; -	int hlen, ret; - -	hlen = (type == LOWPAN_DISPATCH_FRAG1) ? -			LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE; - -	lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen); - -	frag = dev_alloc_skb(hlen + mlen + plen + IEEE802154_MFR_SIZE); -	if (!frag) -		return -ENOMEM; - -	frag->priority = skb->priority; -	frag->dev = skb->dev; - -	/* copy header, MFR and payload */ -	memcpy(skb_put(frag, mlen), skb->data, mlen); -	memcpy(skb_put(frag, hlen), head, hlen); - -	if (plen) -		skb_copy_from_linear_data_offset(skb, offset + mlen, -					skb_put(frag, plen), plen); - -	lowpan_raw_dump_table(__func__, " raw fragment dump", frag->data, -								frag->len); - -	ret = dev_queue_xmit(frag); - -	return ret; -} - -static int -lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev) -{ -	int  err, header_length, payload_length, tag, offset = 0; -	u8 head[5]; - -	header_length = lowpan_get_mac_header_length(skb); -	payload_length = skb->len - header_length; -	tag = lowpan_dev_info(dev)->fragment_tag++; - -	/* first fragment header */ -	head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7); -	head[1] = payload_length & 0xff; -	head[2] = tag >> 8; -	head[3] = tag & 0xff; - -	err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE, -				   0, LOWPAN_DISPATCH_FRAG1); - -	if (err) { -		pr_debug("%s unable to send FRAG1 packet (tag: %d)", -			 __func__, tag); -		goto exit; -	} - -	offset = LOWPAN_FRAG_SIZE; - -	/* next fragment header */ -	head[0] &= ~LOWPAN_DISPATCH_FRAG1; -	head[0] |= LOWPAN_DISPATCH_FRAGN; - -	while ((payload_length - offset > 0) && (err >= 0)) { -		int len = LOWPAN_FRAG_SIZE; - -		head[4] = offset / 8; - -		if (payload_length - offset < len) -			len = payload_length - offset; - -		err = lowpan_fragment_xmit(skb, head, header_length, -					   len, offset, LOWPAN_DISPATCH_FRAGN); -		if (err) { -			pr_debug("%s unable to send a subsequent FRAGN packet " -				 "(tag: %d, offset: %d", __func__, tag, offset); -			goto exit; -		} - -		offset += len; -	} - -exit: -	return err; -} - -static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) -{ -	int err = -1; - -	pr_debug("package xmit\n"); - -	skb->dev = lowpan_dev_info(dev)->real_dev; -	if (skb->dev == NULL) { -		pr_debug("ERROR: no real wpan device found\n"); -		goto error; -	} - -	/* Send directly if less than the MTU minus the 2 checksum bytes. */ -	if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) { -		err = dev_queue_xmit(skb); -		goto out; -	} - -	pr_debug("frame is too big, fragmentation is needed\n"); -	err = lowpan_skb_fragmentation(skb, dev); -error: -	dev_kfree_skb(skb); -out: -	if (err) -		pr_debug("ERROR: xmit failed\n"); - -	return (err < 0) ? NET_XMIT_DROP : err; -} - -static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) -{ -	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; -	return ieee802154_mlme_ops(real_dev)->get_phy(real_dev); -} - -static u16 lowpan_get_pan_id(const struct net_device *dev) -{ -	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; -	return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); -} - -static u16 lowpan_get_short_addr(const struct net_device *dev) -{ -	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; -	return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); -} - -static u8 lowpan_get_dsn(const struct net_device *dev) -{ -	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; -	return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); -} - -static struct header_ops lowpan_header_ops = { -	.create	= lowpan_header_create, -}; - -static const struct net_device_ops lowpan_netdev_ops = { -	.ndo_start_xmit		= lowpan_xmit, -	.ndo_set_mac_address	= lowpan_set_address, -}; - -static struct ieee802154_mlme_ops lowpan_mlme = { -	.get_pan_id = lowpan_get_pan_id, -	.get_phy = lowpan_get_phy, -	.get_short_addr = lowpan_get_short_addr, -	.get_dsn = lowpan_get_dsn, -}; - -static void lowpan_setup(struct net_device *dev) -{ -	dev->addr_len		= IEEE802154_ADDR_LEN; -	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); -	dev->type		= ARPHRD_IEEE802154; -	/* Frame Control + Sequence Number + Address fields + Security Header */ -	dev->hard_header_len	= 2 + 1 + 20 + 14; -	dev->needed_tailroom	= 2; /* FCS */ -	dev->mtu		= 1281; -	dev->tx_queue_len	= 0; -	dev->flags		= IFF_BROADCAST | IFF_MULTICAST; -	dev->watchdog_timeo	= 0; - -	dev->netdev_ops		= &lowpan_netdev_ops; -	dev->header_ops		= &lowpan_header_ops; -	dev->ml_priv		= &lowpan_mlme; -	dev->destructor		= free_netdev; -} - -static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) -{ -	if (tb[IFLA_ADDRESS]) { -		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) -			return -EINVAL; -	} -	return 0; -} - -static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, -	struct packet_type *pt, struct net_device *orig_dev) -{ -	struct sk_buff *local_skb; - -	if (!netif_running(dev)) -		goto drop; - -	if (dev->type != ARPHRD_IEEE802154) -		goto drop; - -	/* check that it's our buffer */ -	if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { -		/* Copy the packet so that the IPv6 header is -		 * properly aligned. -		 */ -		local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1, -					    skb_tailroom(skb), GFP_ATOMIC); -		if (!local_skb) -			goto drop; - -		local_skb->protocol = htons(ETH_P_IPV6); -		local_skb->pkt_type = PACKET_HOST; - -		/* Pull off the 1-byte of 6lowpan header. */ -		skb_pull(local_skb, 1); -		skb_reset_network_header(local_skb); -		skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); - -		lowpan_give_skb_to_devices(local_skb); - -		kfree_skb(local_skb); -		kfree_skb(skb); -	} else { -		switch (skb->data[0] & 0xe0) { -		case LOWPAN_DISPATCH_IPHC:	/* ipv6 datagram */ -		case LOWPAN_DISPATCH_FRAG1:	/* first fragment header */ -		case LOWPAN_DISPATCH_FRAGN:	/* next fragments headers */ -			local_skb = skb_clone(skb, GFP_ATOMIC); -			if (!local_skb) -				goto drop; -			lowpan_process_data(local_skb); - -			kfree_skb(skb); -			break; -		default: -			break; -		} -	} - -	return NET_RX_SUCCESS; - -drop: -	kfree_skb(skb); -	return NET_RX_DROP; -} - -static int lowpan_newlink(struct net *src_net, struct net_device *dev, -			  struct nlattr *tb[], struct nlattr *data[]) -{ -	struct net_device *real_dev; -	struct lowpan_dev_record *entry; - -	pr_debug("adding new link\n"); - -	if (!tb[IFLA_LINK]) -		return -EINVAL; -	/* find and hold real wpan device */ -	real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); -	if (!real_dev) -		return -ENODEV; - -	lowpan_dev_info(dev)->real_dev = real_dev; -	lowpan_dev_info(dev)->fragment_tag = 0; -	mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); - -	entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL); -	if (!entry) { -		dev_put(real_dev); -		lowpan_dev_info(dev)->real_dev = NULL; -		return -ENOMEM; -	} - -	entry->ldev = dev; - -	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); -	INIT_LIST_HEAD(&entry->list); -	list_add_tail(&entry->list, &lowpan_devices); -	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - -	register_netdevice(dev); - -	return 0; -} - -static void lowpan_dellink(struct net_device *dev, struct list_head *head) -{ -	struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); -	struct net_device *real_dev = lowpan_dev->real_dev; -	struct lowpan_dev_record *entry, *tmp; - -	ASSERT_RTNL(); - -	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); -	list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { -		if (entry->ldev == dev) { -			list_del(&entry->list); -			kfree(entry); -		} -	} -	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - -	mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); - -	unregister_netdevice_queue(dev, head); - -	dev_put(real_dev); -} - -static struct rtnl_link_ops lowpan_link_ops __read_mostly = { -	.kind		= "lowpan", -	.priv_size	= sizeof(struct lowpan_dev_info), -	.setup		= lowpan_setup, -	.newlink	= lowpan_newlink, -	.dellink	= lowpan_dellink, -	.validate	= lowpan_validate, -}; - -static inline int __init lowpan_netlink_init(void) -{ -	return rtnl_link_register(&lowpan_link_ops); -} - -static inline void lowpan_netlink_fini(void) -{ -	rtnl_link_unregister(&lowpan_link_ops); -} - -static int lowpan_device_event(struct notifier_block *unused, -			       unsigned long event, void *ptr) -{ -	struct net_device *dev = netdev_notifier_info_to_dev(ptr); -	LIST_HEAD(del_list); -	struct lowpan_dev_record *entry, *tmp; - -	if (dev->type != ARPHRD_IEEE802154) -		goto out; - -	if (event == NETDEV_UNREGISTER) { -		list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { -			if (lowpan_dev_info(entry->ldev)->real_dev == dev) -				lowpan_dellink(entry->ldev, &del_list); -		} - -		unregister_netdevice_many(&del_list); -	} - -out: -	return NOTIFY_DONE; -} - -static struct notifier_block lowpan_dev_notifier = { -	.notifier_call = lowpan_device_event, -}; - -static struct packet_type lowpan_packet_type = { -	.type = __constant_htons(ETH_P_IEEE802154), -	.func = lowpan_rcv, -}; - -static int __init lowpan_init_module(void) -{ -	int err = 0; - -	err = lowpan_netlink_init(); -	if (err < 0) -		goto out; - -	dev_add_pack(&lowpan_packet_type); - -	err = register_netdevice_notifier(&lowpan_dev_notifier); -	if (err < 0) { -		dev_remove_pack(&lowpan_packet_type); -		lowpan_netlink_fini(); -	} -out: -	return err; -} - -static void __exit lowpan_cleanup_module(void) -{ -	struct lowpan_fragment *frame, *tframe; - -	lowpan_netlink_fini(); - -	dev_remove_pack(&lowpan_packet_type); - -	unregister_netdevice_notifier(&lowpan_dev_notifier); - -	/* Now 6lowpan packet_type is removed, so no new fragments are -	 * expected on RX, therefore that's the time to clean incomplete -	 * fragments. -	 */ -	spin_lock_bh(&flist_lock); -	list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { -		del_timer_sync(&frame->timer); -		list_del(&frame->list); -		dev_kfree_skb(frame->skb); -		kfree(frame); -	} -	spin_unlock_bh(&flist_lock); -} - -module_init(lowpan_init_module); -module_exit(lowpan_cleanup_module); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("lowpan"); diff --git a/net/ieee802154/6lowpan.h b/net/ieee802154/6lowpan.h deleted file mode 100644 index 2869c0526da..00000000000 --- a/net/ieee802154/6lowpan.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2011, Siemens AG - * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -/* - * Based on patches from Jon Smirl <jonsmirl@gmail.com> - * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* Jon's code is based on 6lowpan implementation for Contiki which is: - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - *    notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - *    notice, this list of conditions and the following disclaimer in the - *    documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - *    may be used to endorse or promote products derived from this software - *    without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef __6LOWPAN_H__ -#define __6LOWPAN_H__ - -#define UIP_802154_SHORTADDR_LEN	2  /* compressed ipv6 address length */ -#define UIP_IPH_LEN			40 /* ipv6 fixed header size */ -#define UIP_PROTO_UDP			17 /* ipv6 next header value for UDP */ -#define UIP_FRAGH_LEN			8  /* ipv6 fragment header size */ - -/* - * 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])) - -/* ipv6 address is unspecified */ -#define is_addr_unspecified(a)		\ -	((((a)->s6_addr32[0]) == 0) &&	\ -	 (((a)->s6_addr32[1]) == 0) &&	\ -	 (((a)->s6_addr32[2]) == 0) &&	\ -	 (((a)->s6_addr32[3]) == 0)) - -/* compare ipv6 addresses prefixes */ -#define ipaddr_prefixcmp(addr1, addr2, length) \ -	(memcmp(addr1, addr2, length >> 3) == 0) - -/* local link, i.e. FE80::/10 */ -#define is_addr_link_local(a) (((a)->s6_addr16[0]) == htons(0xFE80)) - -/* - * check whether we can compress the IID to 16 bits, - * it's possible for unicast adresses 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)) - -/* multicast address */ -#define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF) - -/* check whether the 112-bit gid of the multicast address is mappable to: */ - -/* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */ -#define lowpan_is_mcast_addr_compressable(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_addr16[6]) == 0) &&		\ -	 (((a)->s6_addr[14])  == 0) &&		\ -	 ((((a)->s6_addr[15]) == 1) || (((a)->s6_addr[15]) == 2))) - -/* 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)) - -#define lowpan_is_addr_broadcast(a)	\ -	((((a)[0]) == 0xFF) &&	\ -	 (((a)[1]) == 0xFF) &&	\ -	 (((a)[2]) == 0xFF) &&	\ -	 (((a)[3]) == 0xFF) &&	\ -	 (((a)[4]) == 0xFF) &&	\ -	 (((a)[5]) == 0xFF) &&	\ -	 (((a)[6]) == 0xFF) &&	\ -	 (((a)[7]) == 0xFF)) - -#define LOWPAN_DISPATCH_IPV6	0x41 /* 01000001 = 65 */ -#define LOWPAN_DISPATCH_HC1	0x42 /* 01000010 = 66 */ -#define LOWPAN_DISPATCH_IPHC	0x60 /* 011xxxxx = ... */ -#define LOWPAN_DISPATCH_FRAG1	0xc0 /* 11000xxx */ -#define LOWPAN_DISPATCH_FRAGN	0xe0 /* 11100xxx */ - -#define LOWPAN_DISPATCH_MASK	0xf8 /* 11111000 */ - -#define LOWPAN_FRAG_TIMEOUT	(HZ * 60)	/* time-out 60 sec */ - -#define LOWPAN_FRAG1_HEAD_SIZE	0x4 -#define LOWPAN_FRAGN_HEAD_SIZE	0x5 - -/* - * According IEEE802.15.4 standard: - *   - MTU is 127 octets - *   - maximum MHR size is 37 octets - *   - MFR size is 2 octets - * - * so minimal payload size that we may guarantee is: - *   MTU - MHR - MFR = 88 octets - */ -#define LOWPAN_FRAG_SIZE	88 - -/* - * Values of fields within the IPHC encoding first byte - * (C stands for compressed and I for inline) - */ -#define LOWPAN_IPHC_TF		0x18 - -#define LOWPAN_IPHC_FL_C	0x10 -#define LOWPAN_IPHC_TC_C	0x08 -#define LOWPAN_IPHC_NH_C	0x04 -#define LOWPAN_IPHC_TTL_1	0x01 -#define LOWPAN_IPHC_TTL_64	0x02 -#define LOWPAN_IPHC_TTL_255	0x03 -#define LOWPAN_IPHC_TTL_I	0x00 - - -/* 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		0x30 - -#define LOWPAN_IPHC_SAM_BIT	4 - -#define LOWPAN_IPHC_M		0x08 -#define LOWPAN_IPHC_DAC		0x04 -#define LOWPAN_IPHC_DAM_00	0x00 -#define LOWPAN_IPHC_DAM_01	0x01 -#define LOWPAN_IPHC_DAM_10	0x02 -#define LOWPAN_IPHC_DAM_11	0x03 - -#define LOWPAN_IPHC_DAM_BIT	0 -/* - * LOWPAN_UDP encoding (works together with IPHC) - */ -#define LOWPAN_NHC_UDP_MASK		0xF8 -#define LOWPAN_NHC_UDP_ID		0xF0 -#define LOWPAN_NHC_UDP_CHECKSUMC	0x04 -#define LOWPAN_NHC_UDP_CHECKSUMI	0x00 - -#define LOWPAN_NHC_UDP_4BIT_PORT	0xF0B0 -#define LOWPAN_NHC_UDP_4BIT_MASK	0xFFF0 -#define LOWPAN_NHC_UDP_8BIT_PORT	0xF000 -#define LOWPAN_NHC_UDP_8BIT_MASK	0xFF00 - -/* values for port compression, _with checksum_ ie bit 5 set to 0 */ -#define LOWPAN_NHC_UDP_CS_P_00	0xF0 /* all inline */ -#define LOWPAN_NHC_UDP_CS_P_01	0xF1 /* source 16bit inline, -					dest = 0xF0 + 8 bit inline */ -#define LOWPAN_NHC_UDP_CS_P_10	0xF2 /* source = 0xF0 + 8bit inline, -					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__ */ diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c new file mode 100644 index 00000000000..211b5686d71 --- /dev/null +++ b/net/ieee802154/6lowpan_iphc.c @@ -0,0 +1,801 @@ +/* + * Copyright 2011, Siemens AG + * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +/* + * Based on patches from Jon Smirl <jonsmirl@gmail.com> + * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Jon's code is based on 6lowpan implementation for Contiki which is: + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#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> + +/* + * Uncompress address function for source and + * destination address(non-multicast). + * + * address_mode is sam value 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) +{ +	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 (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)); +			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; +	} + +	raw_dump_inline(NULL, "Reconstructed ipv6 addr is", +			ipaddr->s6_addr, 16); + +	return 0; +} + +/* + * Uncompress address function for source context + * based address(non-multicast). + */ +static int 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; +	} + +	raw_dump_inline(NULL, +			"Reconstructed context based ipv6 src addr is", +			ipaddr->s6_addr, 16); + +	return 0; +} + +static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr, +		struct net_device *dev, skb_delivery_cb deliver_skb) +{ +	struct sk_buff *new; +	int stat; + +	new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), +								GFP_ATOMIC); +	kfree_skb(skb); + +	if (!new) +		return -ENOMEM; + +	skb_push(new, sizeof(struct ipv6hdr)); +	skb_reset_network_header(new); +	skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); + +	new->protocol = htons(ETH_P_IPV6); +	new->pkt_type = PACKET_HOST; +	new->dev = dev; + +	raw_dump_table(__func__, "raw skb data dump before receiving", +			new->data, new->len); + +	stat = deliver_skb(new, dev); + +	kfree_skb(new); + +	return stat; +} + +/* 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; +	} + +	raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is", +				ipaddr->s6_addr, 16); + +	return 0; +} + +static int +uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh) +{ +	bool fail; +	u8 tmp = 0, val = 0; + +	if (!uh) +		goto err; + +	fail = lowpan_fetch_skb(skb, &tmp, 1); + +	if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { +		pr_debug("UDP header uncompression\n"); +		switch (tmp & LOWPAN_NHC_UDP_CS_P_11) { +		case LOWPAN_NHC_UDP_CS_P_00: +			fail |= lowpan_fetch_skb(skb, &uh->source, 2); +			fail |= lowpan_fetch_skb(skb, &uh->dest, 2); +			break; +		case LOWPAN_NHC_UDP_CS_P_01: +			fail |= lowpan_fetch_skb(skb, &uh->source, 2); +			fail |= lowpan_fetch_skb(skb, &val, 1); +			uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT); +			break; +		case LOWPAN_NHC_UDP_CS_P_10: +			fail |= lowpan_fetch_skb(skb, &val, 1); +			uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT); +			fail |= lowpan_fetch_skb(skb, &uh->dest, 2); +			break; +		case LOWPAN_NHC_UDP_CS_P_11: +			fail |= lowpan_fetch_skb(skb, &val, 1); +			uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT + +					   (val >> 4)); +			uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + +					 (val & 0x0f)); +			break; +		default: +			pr_debug("ERROR: unknown UDP format\n"); +			goto err; +			break; +		} + +		pr_debug("uncompressed UDP ports: src = %d, dst = %d\n", +			 ntohs(uh->source), ntohs(uh->dest)); + +		/* checksum */ +		if (tmp & LOWPAN_NHC_UDP_CS_C) { +			pr_debug_ratelimited("checksum elided currently not supported\n"); +			goto err; +		} else { +			fail |= lowpan_fetch_skb(skb, &uh->check, 2); +		} + +		/* +		 * UDP lenght needs to be infered from the lower layers +		 * here, we obtain the hint from the remaining size of the +		 * frame +		 */ +		uh->len = htons(skb->len + sizeof(struct udphdr)); +		pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len)); +	} else { +		pr_debug("ERROR: unsupported NH format\n"); +		goto err; +	} + +	if (fail) +		goto err; + +	return 0; +err: +	return -EINVAL; +} + +/* TTL uncompression values */ +static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 }; + +int lowpan_process_data(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, skb_delivery_cb deliver_skb) +{ +	struct ipv6hdr hdr = {}; +	u8 tmp, num_context = 0; +	int err; + +	raw_dump_table(__func__, "raw skb data dump uncompressed", +				skb->data, skb->len); + +	/* 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_u8(skb, &num_context)) +			goto drop; +	} + +	hdr.version = 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) +	 */ +	case 0: /* 00b */ +		if (lowpan_fetch_skb_u8(skb, &tmp)) +			goto drop; + +		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); +		break; +	/* +	 * Traffic class carried in-line +	 * ECN + DSCP (1 byte), Flow Label is elided +	 */ +	case 2: /* 10b */ +		if (lowpan_fetch_skb_u8(skb, &tmp)) +			goto drop; + +		hdr.priority = ((tmp >> 2) & 0x0f); +		hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30); +		break; +	/* +	 * Flow Label carried in-line +	 * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided +	 */ +	case 1: /* 01b */ +		if (lowpan_fetch_skb_u8(skb, &tmp)) +			goto drop; + +		hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30); +		memcpy(&hdr.flow_lbl[1], &skb->data[0], 2); +		skb_pull(skb, 2); +		break; +	/* Traffic Class and Flow Label are elided */ +	case 3: /* 11b */ +		break; +	default: +		break; +	} + +	/* Next Header */ +	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { +		/* Next header is carried inline */ +		if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr))) +			goto drop; + +		pr_debug("NH flag is set, next header carried inline: %02x\n", +			 hdr.nexthdr); +	} + +	/* Hop Limit */ +	if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) +		hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03]; +	else { +		if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit))) +			goto drop; +	} + +	/* 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); +	} else { +		/* Source address uncompression */ +		pr_debug("source address stateless compression\n"); +		err = uncompress_addr(skb, &hdr.saddr, tmp, saddr, +					saddr_type, saddr_len); +	} + +	/* Check on error of previous branch */ +	if (err) +		goto drop; + +	/* 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) { +			pr_debug("dest: context-based mcast compression\n"); +			/* TODO: implement this */ +		} else { +			err = lowpan_uncompress_multicast_daddr( +						skb, &hdr.daddr, tmp); +			if (err) +				goto drop; +		} +	} else { +		err = uncompress_addr(skb, &hdr.daddr, tmp, daddr, +					daddr_type, daddr_len); +		pr_debug("dest: stateless compression mode %d dest %pI6c\n", +			tmp, &hdr.daddr); +		if (err) +			goto drop; +	} + +	/* UDP data uncompression */ +	if (iphc0 & LOWPAN_IPHC_NH_C) { +		struct udphdr uh; +		struct sk_buff *new; +		if (uncompress_udp_header(skb, &uh)) +			goto drop; + +		/* +		 * replace the compressed UDP head by the uncompressed UDP +		 * header +		 */ +		new = skb_copy_expand(skb, sizeof(struct udphdr), +				      skb_tailroom(skb), GFP_ATOMIC); +		kfree_skb(skb); + +		if (!new) +			return -ENOMEM; + +		skb = new; + +		skb_push(skb, sizeof(struct udphdr)); +		skb_reset_transport_header(skb); +		skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr)); + +		raw_dump_table(__func__, "raw UDP header dump", +				      (u8 *)&uh, sizeof(uh)); + +		hdr.nexthdr = UIP_PROTO_UDP; +	} + +	hdr.payload_len = htons(skb->len); + +	pr_debug("skb headroom size = %d, data length = %d\n", +		 skb_headroom(skb), skb->len); + +	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t" +		 "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n", +		hdr.version, ntohs(hdr.payload_len), hdr.nexthdr, +		hdr.hop_limit, &hdr.daddr); + +	raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, +							sizeof(hdr)); + +	return skb_deliver(skb, &hdr, dev, deliver_skb); + +drop: +	kfree_skb(skb); +	return -EINVAL; +} +EXPORT_SYMBOL_GPL(lowpan_process_data); + +static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift, +				const struct in6_addr *ipaddr, +				const unsigned char *lladdr) +{ +	u8 val = 0; + +	if (is_addr_mac_addr_based(ipaddr, lladdr)) { +		val = 3; /* 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 */ +		memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2); +		*hc06_ptr += 2; +		val = 2; /* 16-bits */ +		raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)", +			*hc06_ptr - 2, 2); +	} else { +		/* do not compress IID => xxxx::IID */ +		memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8); +		*hc06_ptr += 8; +		val = 1; /* 64-bits */ +		raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)", +			*hc06_ptr - 8, 8); +	} + +	return rol8(val, shift); +} + +static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) +{ +	struct udphdr *uh = udp_hdr(skb); +	u8 tmp; + +	if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) == +	     LOWPAN_NHC_UDP_4BIT_PORT) && +	    ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) == +	     LOWPAN_NHC_UDP_4BIT_PORT)) { +		pr_debug("UDP header: both ports compression to 4 bits\n"); +		/* compression value */ +		tmp = LOWPAN_NHC_UDP_CS_P_11; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +		/* source and destination port */ +		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT + +		      ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4); +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +	} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) == +			LOWPAN_NHC_UDP_8BIT_PORT) { +		pr_debug("UDP header: remove 8 bits of dest\n"); +		/* compression value */ +		tmp = LOWPAN_NHC_UDP_CS_P_01; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +		/* source port */ +		lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source)); +		/* destination port */ +		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +	} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) == +			LOWPAN_NHC_UDP_8BIT_PORT) { +		pr_debug("UDP header: remove 8 bits of source\n"); +		/* compression value */ +		tmp = LOWPAN_NHC_UDP_CS_P_10; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +		/* source port */ +		tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +		/* destination port */ +		lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest)); +	} else { +		pr_debug("UDP header: can't compress\n"); +		/* compression value */ +		tmp = LOWPAN_NHC_UDP_CS_P_00; +		lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp)); +		/* source port */ +		lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source)); +		/* destination port */ +		lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest)); +	} + +	/* checksum is always inline */ +	lowpan_push_hc_data(hc06_ptr, &uh->check, sizeof(uh->check)); + +	/* skip the UDP header */ +	skb_pull(skb, sizeof(struct udphdr)); +} + +int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, +			unsigned short type, const void *_daddr, +			const void *_saddr, unsigned int len) +{ +	u8 tmp, iphc0, iphc1, *hc06_ptr; +	struct ipv6hdr *hdr; +	u8 head[100] = {}; + +	if (type != ETH_P_IPV6) +		return -EINVAL; + +	hdr = ipv6_hdr(skb); +	hc06_ptr = head + 2; + +	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n" +		 "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n", +		hdr->version, ntohs(hdr->payload_len), hdr->nexthdr, +		hdr->hop_limit, &hdr->daddr); + +	raw_dump_table(__func__, "raw skb network header dump", +		skb_network_header(skb), sizeof(struct ipv6hdr)); + +	/* +	 * As we copy some bit-length fields, in the IPHC encoding bytes, +	 * we sometimes use |= +	 * If the field is 0, and the current bit value in memory is 1, +	 * this does not work. We therefore reset the IPHC encoding here +	 */ +	iphc0 = LOWPAN_DISPATCH_IPHC; +	iphc1 = 0; + +	/* 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_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 +	 */ + +	/* hc06 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 */ +			*hc06_ptr = tmp; +			hc06_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; +			*hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F); +			memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2); +			hc06_ptr += 3; +		} else { +			/* compress nothing */ +			memcpy(hc06_ptr, hdr, 4); +			/* replace the top byte with new ECN | DSCP format */ +			*hc06_ptr = tmp; +			hc06_ptr += 4; +		} +	} + +	/* NOTE: payload length is always compressed */ + +	/* Next Header is compress if UDP */ +	if (hdr->nexthdr == UIP_PROTO_UDP) +		iphc0 |= LOWPAN_IPHC_NH_C; + +	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { +		*hc06_ptr = hdr->nexthdr; +		hc06_ptr += 1; +	} + +	/* +	 * Hop limit +	 * if 1:   compress, encoding is 01 +	 * if 64:  compress, encoding is 10 +	 * if 255: compress, encoding is 11 +	 * else do not compress +	 */ +	switch (hdr->hop_limit) { +	case 1: +		iphc0 |= LOWPAN_IPHC_TTL_1; +		break; +	case 64: +		iphc0 |= LOWPAN_IPHC_TTL_64; +		break; +	case 255: +		iphc0 |= LOWPAN_IPHC_TTL_255; +		break; +	default: +		*hc06_ptr = hdr->hop_limit; +		hc06_ptr += 1; +		break; +	} + +	/* source address compression */ +	if (is_addr_unspecified(&hdr->saddr)) { +		pr_debug("source address is unspecified, setting SAC\n"); +		iphc1 |= LOWPAN_IPHC_SAC; +	/* TODO: context lookup */ +	} else if (is_addr_link_local(&hdr->saddr)) { +		iphc1 |= lowpan_compress_addr_64(&hc06_ptr, +				LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr); +		pr_debug("source address unicast link-local %pI6c " +			"iphc1 0x%02x\n", &hdr->saddr, iphc1); +	} else { +		pr_debug("send the full source address\n"); +		memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16); +		hc06_ptr += 16; +	} + +	/* destination address compression */ +	if (is_addr_mcast(&hdr->daddr)) { +		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 */ +			*hc06_ptr = hdr->daddr.s6_addr[15]; +			hc06_ptr += 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 */ +			*hc06_ptr = hdr->daddr.s6_addr[1]; +			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3); +			hc06_ptr += 4; +		} 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 */ +			*hc06_ptr = hdr->daddr.s6_addr[1]; +			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5); +			hc06_ptr += 6; +		} else { +			pr_debug("using full address\n"); +			iphc1 |= LOWPAN_IPHC_DAM_00; +			memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16); +			hc06_ptr += 16; +		} +	} else { +		/* TODO: context lookup */ +		if (is_addr_link_local(&hdr->daddr)) { +			iphc1 |= lowpan_compress_addr_64(&hc06_ptr, +				LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr); +			pr_debug("dest address unicast link-local %pI6c " +				"iphc1 0x%02x\n", &hdr->daddr, iphc1); +		} else { +			pr_debug("dest address unicast %pI6c\n", &hdr->daddr); +			memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16); +			hc06_ptr += 16; +		} +	} + +	/* UDP header compression */ +	if (hdr->nexthdr == UIP_PROTO_UDP) +		compress_udp_header(&hc06_ptr, skb); + +	head[0] = iphc0; +	head[1] = iphc1; + +	skb_pull(skb, sizeof(struct ipv6hdr)); +	skb_reset_transport_header(skb); +	memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head); +	skb_reset_network_header(skb); + +	pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len); + +	raw_dump_table(__func__, "raw skb data dump compressed", +				skb->data, skb->len); +	return 0; +} +EXPORT_SYMBOL_GPL(lowpan_header_compress); + +MODULE_LICENSE("GPL"); diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c new file mode 100644 index 00000000000..fe6bd7a7108 --- /dev/null +++ b/net/ieee802154/6lowpan_rtnl.c @@ -0,0 +1,683 @@ +/* Copyright 2011, Siemens AG + * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +/* Based on patches from Jon Smirl <jonsmirl@gmail.com> + * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +/* Jon's code is based on 6lowpan implementation for Contiki which is: + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <linux/bitops.h> +#include <linux/if_arp.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/netdevice.h> +#include <net/af_ieee802154.h> +#include <net/ieee802154.h> +#include <net/ieee802154_netdev.h> +#include <net/6lowpan.h> +#include <net/ipv6.h> + +#include "reassembly.h" + +static LIST_HEAD(lowpan_devices); + +/* private device info */ +struct lowpan_dev_info { +	struct net_device	*real_dev; /* real WPAN device ptr */ +	struct mutex		dev_list_mtx; /* mutex for list ops */ +	__be16			fragment_tag; +}; + +struct lowpan_dev_record { +	struct net_device *ldev; +	struct list_head list; +}; + +static inline struct +lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) +{ +	return netdev_priv(dev); +} + +static inline void lowpan_address_flip(u8 *src, u8 *dest) +{ +	int i; +	for (i = 0; i < IEEE802154_ADDR_LEN; i++) +		(dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i]; +} + +static int lowpan_header_create(struct sk_buff *skb, +			   struct net_device *dev, +			   unsigned short type, const void *_daddr, +			   const void *_saddr, unsigned int len) +{ +	const u8 *saddr = _saddr; +	const u8 *daddr = _daddr; +	struct ieee802154_addr sa, da; +	struct ieee802154_mac_cb *cb = mac_cb_init(skb); + +	/* TODO: +	 * if this package isn't ipv6 one, where should it be routed? +	 */ +	if (type != ETH_P_IPV6) +		return 0; + +	if (!saddr) +		saddr = dev->dev_addr; + +	raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); +	raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); + +	lowpan_header_compress(skb, dev, type, daddr, saddr, len); + +	/* NOTE1: I'm still unsure about the fact that compression and WPAN +	 * header are created here and not later in the xmit. So wait for +	 * an opinion of net maintainers. +	 */ +	/* NOTE2: to be absolutely correct, we must derive PANid information +	 * from MAC subif of the 'dev' and 'real_dev' network devices, but +	 * this isn't implemented in mainline yet, so currently we assign 0xff +	 */ +	cb->type = IEEE802154_FC_TYPE_DATA; + +	/* prepare wpan address data */ +	sa.mode = IEEE802154_ADDR_LONG; +	sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); +	sa.extended_addr = ieee802154_devaddr_from_raw(saddr); + +	/* intra-PAN communications */ +	da.pan_id = sa.pan_id; + +	/* if the destination address is the broadcast address, use the +	 * corresponding short address +	 */ +	if (lowpan_is_addr_broadcast(daddr)) { +		da.mode = IEEE802154_ADDR_SHORT; +		da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); +	} else { +		da.mode = IEEE802154_ADDR_LONG; +		da.extended_addr = ieee802154_devaddr_from_raw(daddr); +	} + +	cb->ackreq = !lowpan_is_addr_broadcast(daddr); + +	return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, +			type, (void *)&da, (void *)&sa, 0); +} + +static int lowpan_give_skb_to_devices(struct sk_buff *skb, +					struct net_device *dev) +{ +	struct lowpan_dev_record *entry; +	struct sk_buff *skb_cp; +	int stat = NET_RX_SUCCESS; + +	rcu_read_lock(); +	list_for_each_entry_rcu(entry, &lowpan_devices, list) +		if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { +			skb_cp = skb_copy(skb, GFP_ATOMIC); +			if (!skb_cp) { +				stat = -ENOMEM; +				break; +			} + +			skb_cp->dev = entry->ldev; +			stat = netif_rx(skb_cp); +		} +	rcu_read_unlock(); + +	return stat; +} + +static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +{ +	u8 iphc0, iphc1; +	struct ieee802154_addr_sa sa, da; +	void *sap, *dap; + +	raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); +	/* at least two bytes will be used for the encoding */ +	if (skb->len < 2) +		goto drop; + +	if (lowpan_fetch_skb_u8(skb, &iphc0)) +		goto drop; + +	if (lowpan_fetch_skb_u8(skb, &iphc1)) +		goto drop; + +	ieee802154_addr_to_sa(&sa, &hdr->source); +	ieee802154_addr_to_sa(&da, &hdr->dest); + +	if (sa.addr_type == IEEE802154_ADDR_SHORT) +		sap = &sa.short_addr; +	else +		sap = &sa.hwaddr; + +	if (da.addr_type == IEEE802154_ADDR_SHORT) +		dap = &da.short_addr; +	else +		dap = &da.hwaddr; + +	return lowpan_process_data(skb, skb->dev, sap, sa.addr_type, +				   IEEE802154_ADDR_LEN, dap, da.addr_type, +				   IEEE802154_ADDR_LEN, iphc0, iphc1, +				   lowpan_give_skb_to_devices); + +drop: +	kfree_skb(skb); +	return -EINVAL; +} + +static int lowpan_set_address(struct net_device *dev, void *p) +{ +	struct sockaddr *sa = p; + +	if (netif_running(dev)) +		return -EBUSY; + +	/* TODO: validate addr */ +	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + +	return 0; +} + +static struct sk_buff* +lowpan_alloc_frag(struct sk_buff *skb, int size, +		  const struct ieee802154_hdr *master_hdr) +{ +	struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; +	struct sk_buff *frag; +	int rc; + +	frag = alloc_skb(real_dev->hard_header_len + +			 real_dev->needed_tailroom + size, +			 GFP_ATOMIC); + +	if (likely(frag)) { +		frag->dev = real_dev; +		frag->priority = skb->priority; +		skb_reserve(frag, real_dev->hard_header_len); +		skb_reset_network_header(frag); +		*mac_cb(frag) = *mac_cb(skb); + +		rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, +				     &master_hdr->source, size); +		if (rc < 0) { +			kfree_skb(frag); +			return ERR_PTR(-rc); +		} +	} else { +		frag = ERR_PTR(ENOMEM); +	} + +	return frag; +} + +static int +lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, +		     u8 *frag_hdr, int frag_hdrlen, +		     int offset, int len) +{ +	struct sk_buff *frag; + +	raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen); + +	frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr); +	if (IS_ERR(frag)) +		return -PTR_ERR(frag); + +	memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen); +	memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len); + +	raw_dump_table(__func__, " fragment dump", frag->data, frag->len); + +	return dev_queue_xmit(frag); +} + +static int +lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, +		       const struct ieee802154_hdr *wpan_hdr) +{ +	u16 dgram_size, dgram_offset; +	__be16 frag_tag; +	u8 frag_hdr[5]; +	int frag_cap, frag_len, payload_cap, rc; +	int skb_unprocessed, skb_offset; + +	dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - +		     skb->mac_len; +	frag_tag = lowpan_dev_info(dev)->fragment_tag++; + +	frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); +	frag_hdr[1] = dgram_size & 0xff; +	memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag)); + +	payload_cap = ieee802154_max_payload(wpan_hdr); + +	frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE - +			      skb_network_header_len(skb), 8); + +	skb_offset = skb_network_header_len(skb); +	skb_unprocessed = skb->len - skb->mac_len - skb_offset; + +	rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, +				  LOWPAN_FRAG1_HEAD_SIZE, 0, +				  frag_len + skb_network_header_len(skb)); +	if (rc) { +		pr_debug("%s unable to send FRAG1 packet (tag: %d)", +			 __func__, frag_tag); +		goto err; +	} + +	frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1; +	frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN; +	frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8); + +	do { +		dgram_offset += frag_len; +		skb_offset += frag_len; +		skb_unprocessed -= frag_len; +		frag_len = min(frag_cap, skb_unprocessed); + +		frag_hdr[4] = dgram_offset >> 3; + +		rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr, +					  LOWPAN_FRAGN_HEAD_SIZE, skb_offset, +					  frag_len); +		if (rc) { +			pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", +				 __func__, frag_tag, skb_offset); +			goto err; +		} +	} while (skb_unprocessed > frag_cap); + +	consume_skb(skb); +	return NET_XMIT_SUCCESS; + +err: +	kfree_skb(skb); +	return rc; +} + +static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct ieee802154_hdr wpan_hdr; +	int max_single; + +	pr_debug("package xmit\n"); + +	if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) { +		kfree_skb(skb); +		return NET_XMIT_DROP; +	} + +	max_single = ieee802154_max_payload(&wpan_hdr); + +	if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { +		skb->dev = lowpan_dev_info(dev)->real_dev; +		return dev_queue_xmit(skb); +	} else { +		netdev_tx_t rc; + +		pr_debug("frame is too big, fragmentation is needed\n"); +		rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); + +		return rc < 0 ? NET_XMIT_DROP : rc; +	} +} + +static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) +{ +	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; +	return ieee802154_mlme_ops(real_dev)->get_phy(real_dev); +} + +static __le16 lowpan_get_pan_id(const struct net_device *dev) +{ +	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; +	return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); +} + +static __le16 lowpan_get_short_addr(const struct net_device *dev) +{ +	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; +	return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); +} + +static u8 lowpan_get_dsn(const struct net_device *dev) +{ +	struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; +	return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); +} + +static struct header_ops lowpan_header_ops = { +	.create	= lowpan_header_create, +}; + +static struct lock_class_key lowpan_tx_busylock; +static struct lock_class_key lowpan_netdev_xmit_lock_key; + +static void lowpan_set_lockdep_class_one(struct net_device *dev, +					 struct netdev_queue *txq, +					 void *_unused) +{ +	lockdep_set_class(&txq->_xmit_lock, +			  &lowpan_netdev_xmit_lock_key); +} + + +static int lowpan_dev_init(struct net_device *dev) +{ +	netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); +	dev->qdisc_tx_busylock = &lowpan_tx_busylock; +	return 0; +} + +static const struct net_device_ops lowpan_netdev_ops = { +	.ndo_init		= lowpan_dev_init, +	.ndo_start_xmit		= lowpan_xmit, +	.ndo_set_mac_address	= lowpan_set_address, +}; + +static struct ieee802154_mlme_ops lowpan_mlme = { +	.get_pan_id = lowpan_get_pan_id, +	.get_phy = lowpan_get_phy, +	.get_short_addr = lowpan_get_short_addr, +	.get_dsn = lowpan_get_dsn, +}; + +static void lowpan_setup(struct net_device *dev) +{ +	dev->addr_len		= IEEE802154_ADDR_LEN; +	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); +	dev->type		= ARPHRD_IEEE802154; +	/* Frame Control + Sequence Number + Address fields + Security Header */ +	dev->hard_header_len	= 2 + 1 + 20 + 14; +	dev->needed_tailroom	= 2; /* FCS */ +	dev->mtu		= 1281; +	dev->tx_queue_len	= 0; +	dev->flags		= IFF_BROADCAST | IFF_MULTICAST; +	dev->watchdog_timeo	= 0; + +	dev->netdev_ops		= &lowpan_netdev_ops; +	dev->header_ops		= &lowpan_header_ops; +	dev->ml_priv		= &lowpan_mlme; +	dev->destructor		= free_netdev; +} + +static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) +{ +	if (tb[IFLA_ADDRESS]) { +		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) +			return -EINVAL; +	} +	return 0; +} + +static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, +	struct packet_type *pt, struct net_device *orig_dev) +{ +	struct ieee802154_hdr hdr; +	int ret; + +	skb = skb_share_check(skb, GFP_ATOMIC); +	if (!skb) +		goto drop; + +	if (!netif_running(dev)) +		goto drop_skb; + +	if (dev->type != ARPHRD_IEEE802154) +		goto drop_skb; + +	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) +		goto drop_skb; + +	/* check that it's our buffer */ +	if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { +		skb->protocol = htons(ETH_P_IPV6); +		skb->pkt_type = PACKET_HOST; + +		/* Pull off the 1-byte of 6lowpan header. */ +		skb_pull(skb, 1); + +		ret = lowpan_give_skb_to_devices(skb, NULL); +		if (ret == NET_RX_DROP) +			goto drop; +	} else { +		switch (skb->data[0] & 0xe0) { +		case LOWPAN_DISPATCH_IPHC:	/* ipv6 datagram */ +			ret = process_data(skb, &hdr); +			if (ret == NET_RX_DROP) +				goto drop; +			break; +		case LOWPAN_DISPATCH_FRAG1:	/* first fragment header */ +			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); +			if (ret == 1) { +				ret = process_data(skb, &hdr); +				if (ret == NET_RX_DROP) +					goto drop; +			} +			break; +		case LOWPAN_DISPATCH_FRAGN:	/* next fragments headers */ +			ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); +			if (ret == 1) { +				ret = process_data(skb, &hdr); +				if (ret == NET_RX_DROP) +					goto drop; +			} +			break; +		default: +			break; +		} +	} + +	return NET_RX_SUCCESS; +drop_skb: +	kfree_skb(skb); +drop: +	return NET_RX_DROP; +} + +static int lowpan_newlink(struct net *src_net, struct net_device *dev, +			  struct nlattr *tb[], struct nlattr *data[]) +{ +	struct net_device *real_dev; +	struct lowpan_dev_record *entry; + +	pr_debug("adding new link\n"); + +	if (!tb[IFLA_LINK]) +		return -EINVAL; +	/* find and hold real wpan device */ +	real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); +	if (!real_dev) +		return -ENODEV; +	if (real_dev->type != ARPHRD_IEEE802154) { +		dev_put(real_dev); +		return -EINVAL; +	} + +	lowpan_dev_info(dev)->real_dev = real_dev; +	mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); + +	entry = kzalloc(sizeof(*entry), GFP_KERNEL); +	if (!entry) { +		dev_put(real_dev); +		lowpan_dev_info(dev)->real_dev = NULL; +		return -ENOMEM; +	} + +	entry->ldev = dev; + +	/* Set the lowpan harware address to the wpan hardware address. */ +	memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); + +	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); +	INIT_LIST_HEAD(&entry->list); +	list_add_tail(&entry->list, &lowpan_devices); +	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + +	register_netdevice(dev); + +	return 0; +} + +static void lowpan_dellink(struct net_device *dev, struct list_head *head) +{ +	struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); +	struct net_device *real_dev = lowpan_dev->real_dev; +	struct lowpan_dev_record *entry, *tmp; + +	ASSERT_RTNL(); + +	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); +	list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { +		if (entry->ldev == dev) { +			list_del(&entry->list); +			kfree(entry); +		} +	} +	mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); + +	mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); + +	unregister_netdevice_queue(dev, head); + +	dev_put(real_dev); +} + +static struct rtnl_link_ops lowpan_link_ops __read_mostly = { +	.kind		= "lowpan", +	.priv_size	= sizeof(struct lowpan_dev_info), +	.setup		= lowpan_setup, +	.newlink	= lowpan_newlink, +	.dellink	= lowpan_dellink, +	.validate	= lowpan_validate, +}; + +static inline int __init lowpan_netlink_init(void) +{ +	return rtnl_link_register(&lowpan_link_ops); +} + +static inline void lowpan_netlink_fini(void) +{ +	rtnl_link_unregister(&lowpan_link_ops); +} + +static int lowpan_device_event(struct notifier_block *unused, +			       unsigned long event, void *ptr) +{ +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); +	LIST_HEAD(del_list); +	struct lowpan_dev_record *entry, *tmp; + +	if (dev->type != ARPHRD_IEEE802154) +		goto out; + +	if (event == NETDEV_UNREGISTER) { +		list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { +			if (lowpan_dev_info(entry->ldev)->real_dev == dev) +				lowpan_dellink(entry->ldev, &del_list); +		} + +		unregister_netdevice_many(&del_list); +	} + +out: +	return NOTIFY_DONE; +} + +static struct notifier_block lowpan_dev_notifier = { +	.notifier_call = lowpan_device_event, +}; + +static struct packet_type lowpan_packet_type = { +	.type = htons(ETH_P_IEEE802154), +	.func = lowpan_rcv, +}; + +static int __init lowpan_init_module(void) +{ +	int err = 0; + +	err = lowpan_net_frag_init(); +	if (err < 0) +		goto out; + +	err = lowpan_netlink_init(); +	if (err < 0) +		goto out_frag; + +	dev_add_pack(&lowpan_packet_type); + +	err = register_netdevice_notifier(&lowpan_dev_notifier); +	if (err < 0) +		goto out_pack; + +	return 0; + +out_pack: +	dev_remove_pack(&lowpan_packet_type); +	lowpan_netlink_fini(); +out_frag: +	lowpan_net_frag_exit(); +out: +	return err; +} + +static void __exit lowpan_cleanup_module(void) +{ +	lowpan_netlink_fini(); + +	dev_remove_pack(&lowpan_packet_type); + +	lowpan_net_frag_exit(); + +	unregister_netdevice_notifier(&lowpan_dev_notifier); +} + +module_init(lowpan_init_module); +module_exit(lowpan_cleanup_module); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("lowpan"); diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index b2e06df0076..8af1330b313 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -13,5 +13,12 @@ config IEEE802154  config IEEE802154_6LOWPAN  	tristate "6lowpan support over IEEE 802.15.4"  	depends on IEEE802154 && IPV6 +	select 6LOWPAN_IPHC  	---help--- -	IPv6 compression over IEEE 802.15.4. +	  IPv6 compression over IEEE 802.15.4. + +config 6LOWPAN_IPHC +	tristate +	---help--- +	  6lowpan compression code which is shared between IEEE 802.15.4 and Bluetooth +	  stacks. diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index d7716d64c6b..bf1b51497a4 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,5 +1,10 @@  obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o  obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o +obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o -ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o +6lowpan-y := 6lowpan_rtnl.o reassembly.o +ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \ +                header_ops.o  af_802154-y := af_ieee802154.o raw.o dgram.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h index b1ec5253752..8330a09bfc9 100644 --- a/net/ieee802154/af802154.h +++ b/net/ieee802154/af802154.h @@ -25,12 +25,13 @@  #define AF802154_H  struct sk_buff; -struct net_devce; +struct net_device; +struct ieee802154_addr;  extern struct proto ieee802154_raw_prot;  extern struct proto ieee802154_dgram_prot;  void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb);  int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb);  struct net_device *ieee802154_get_dev(struct net *net, -		struct ieee802154_addr *addr); +				      const struct ieee802154_addr *addr);  #endif diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 40e606f3788..351d9a94ec2 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -43,25 +43,27 @@  /*   * Utility function for families   */ -struct net_device *ieee802154_get_dev(struct net *net, -		struct ieee802154_addr *addr) +struct net_device* +ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)  {  	struct net_device *dev = NULL;  	struct net_device *tmp; -	u16 pan_id, short_addr; +	__le16 pan_id, short_addr; +	u8 hwaddr[IEEE802154_ADDR_LEN]; -	switch (addr->addr_type) { +	switch (addr->mode) {  	case IEEE802154_ADDR_LONG: +		ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr);  		rcu_read_lock(); -		dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, addr->hwaddr); +		dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr);  		if (dev)  			dev_hold(dev);  		rcu_read_unlock();  		break;  	case IEEE802154_ADDR_SHORT: -		if (addr->pan_id == 0xffff || -		    addr->short_addr == IEEE802154_ADDR_UNDEF || -		    addr->short_addr == 0xffff) +		if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) || +		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || +		    addr->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST))  			break;  		rtnl_lock(); @@ -86,7 +88,7 @@ struct net_device *ieee802154_get_dev(struct net *net,  		break;  	default:  		pr_warning("Unsupported ieee802154 address type: %d\n", -				addr->addr_type); +				addr->mode);  		break;  	} @@ -326,7 +328,7 @@ drop:  static struct packet_type ieee802154_packet_type = { -	.type = __constant_htons(ETH_P_IEEE802154), +	.type = htons(ETH_P_IEEE802154),  	.func = ieee802154_rcv,  }; diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 581a59504bd..4f0ed878019 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -21,6 +21,7 @@   * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>   */ +#include <linux/capability.h>  #include <linux/net.h>  #include <linux/module.h>  #include <linux/if_arp.h> @@ -45,7 +46,12 @@ struct dgram_sock {  	struct ieee802154_addr dst_addr;  	unsigned int bound:1; +	unsigned int connected:1;  	unsigned int want_ack:1; +	unsigned int secen:1; +	unsigned int secen_override:1; +	unsigned int seclevel:3; +	unsigned int seclevel_override:1;  };  static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -73,10 +79,7 @@ static int dgram_init(struct sock *sk)  {  	struct dgram_sock *ro = dgram_sk(sk); -	ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; -	ro->dst_addr.pan_id = 0xffff;  	ro->want_ack = 1; -	memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));  	return 0;  } @@ -88,6 +91,7 @@ static void dgram_close(struct sock *sk, long timeout)  static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)  {  	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; +	struct ieee802154_addr haddr;  	struct dgram_sock *ro = dgram_sk(sk);  	int err = -EINVAL;  	struct net_device *dev; @@ -102,7 +106,8 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)  	if (addr->family != AF_IEEE802154)  		goto out; -	dev = ieee802154_get_dev(sock_net(sk), &addr->addr); +	ieee802154_addr_from_sa(&haddr, &addr->addr); +	dev = ieee802154_get_dev(sock_net(sk), &haddr);  	if (!dev) {  		err = -ENODEV;  		goto out; @@ -113,7 +118,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)  		goto out_put;  	} -	memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr)); +	ro->src_addr = haddr;  	ro->bound = 1;  	err = 0; @@ -149,8 +154,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)  			 * of this packet since that is all  			 * that will be read.  			 */ -			/* FIXME: parse the header for more correct value */ -			amount = skb->len - (3+8+8); +			amount = skb->len - ieee802154_hdr_length(skb);  		}  		spin_unlock_bh(&sk->sk_receive_queue.lock);  		return put_user(amount, (int __user *)arg); @@ -181,7 +185,8 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,  		goto out;  	} -	memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr)); +	ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); +	ro->connected = 1;  out:  	release_sock(sk); @@ -193,10 +198,7 @@ static int dgram_disconnect(struct sock *sk, int flags)  	struct dgram_sock *ro = dgram_sk(sk);  	lock_sock(sk); - -	ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; -	memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); - +	ro->connected = 0;  	release_sock(sk);  	return 0; @@ -208,7 +210,9 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  	struct net_device *dev;  	unsigned int mtu;  	struct sk_buff *skb; +	struct ieee802154_mac_cb *cb;  	struct dgram_sock *ro = dgram_sk(sk); +	struct ieee802154_addr dst_addr;  	int hlen, tlen;  	int err; @@ -217,6 +221,11 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  		return -EOPNOTSUPP;  	} +	if (!ro->connected && !msg->msg_name) +		return -EDESTADDRREQ; +	else if (ro->connected && msg->msg_name) +		return -EISCONN; +  	if (!ro->bound)  		dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);  	else @@ -232,7 +241,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  	if (size > mtu) {  		pr_debug("size = %Zu, mtu = %u\n", size, mtu); -		err = -EINVAL; +		err = -EMSGSIZE;  		goto out_dev;  	} @@ -248,18 +257,28 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  	skb_reset_network_header(skb); -	mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; -	if (ro->want_ack) -		mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; +	cb = mac_cb_init(skb); +	cb->type = IEEE802154_FC_TYPE_DATA; +	cb->ackreq = ro->want_ack; + +	if (msg->msg_name) { +		DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name); + +		ieee802154_addr_from_sa(&dst_addr, &daddr->addr); +	} else { +		dst_addr = ro->dst_addr; +	} -	mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); -	err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, -			ro->bound ? &ro->src_addr : NULL, size); +	cb->secen = ro->secen; +	cb->secen_override = ro->secen_override; +	cb->seclevel = ro->seclevel; +	cb->seclevel_override = ro->seclevel_override; + +	err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, +			      ro->bound ? &ro->src_addr : NULL, size);  	if (err < 0)  		goto out_skb; -	skb_reset_mac_header(skb); -  	err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);  	if (err < 0)  		goto out_skb; @@ -291,9 +310,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,  	size_t copied = 0;  	int err = -EOPNOTSUPP;  	struct sk_buff *skb; -	struct sockaddr_ieee802154 *saddr; - -	saddr = (struct sockaddr_ieee802154 *)msg->msg_name; +	DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name);  	skb = skb_recv_datagram(sk, flags, noblock, &err);  	if (!skb) @@ -314,10 +331,9 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,  	if (saddr) {  		saddr->family = AF_IEEE802154; -		saddr->addr = mac_cb(skb)->sa; -	} -	if (addr_len) +		ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source);  		*addr_len = sizeof(*saddr); +	}  	if (flags & MSG_TRUNC)  		copied = skb->len; @@ -331,6 +347,10 @@ out:  static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)  { +	skb = skb_share_check(skb, GFP_ATOMIC); +	if (!skb) +		return NET_RX_DROP; +  	if (sock_queue_rcv_skb(sk, skb) < 0) {  		kfree_skb(skb);  		return NET_RX_DROP; @@ -339,40 +359,43 @@ static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb)  	return NET_RX_SUCCESS;  } -static inline int ieee802154_match_sock(u8 *hw_addr, u16 pan_id, -		u16 short_addr, struct dgram_sock *ro) +static inline bool +ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, +		      struct dgram_sock *ro)  {  	if (!ro->bound) -		return 1; +		return true; -	if (ro->src_addr.addr_type == IEEE802154_ADDR_LONG && -	    !memcmp(ro->src_addr.hwaddr, hw_addr, IEEE802154_ADDR_LEN)) -		return 1; +	if (ro->src_addr.mode == IEEE802154_ADDR_LONG && +	    hw_addr == ro->src_addr.extended_addr) +		return true; -	if (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT && -		     pan_id == ro->src_addr.pan_id && -		     short_addr == ro->src_addr.short_addr) -		return 1; +	if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && +	    pan_id == ro->src_addr.pan_id && +	    short_addr == ro->src_addr.short_addr) +		return true; -	return 0; +	return false;  }  int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)  {  	struct sock *sk, *prev = NULL;  	int ret = NET_RX_SUCCESS; -	u16 pan_id, short_addr; +	__le16 pan_id, short_addr; +	__le64 hw_addr;  	/* Data frame processing */  	BUG_ON(dev->type != ARPHRD_IEEE802154);  	pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);  	short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); +	hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr);  	read_lock(&dgram_lock);  	sk_for_each(sk, &dgram_head) { -		if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr, -					dgram_sk(sk))) { +		if (ieee802154_match_sock(hw_addr, pan_id, short_addr, +					  dgram_sk(sk))) {  			if (prev) {  				struct sk_buff *clone;  				clone = skb_clone(skb, GFP_ATOMIC); @@ -414,6 +437,20 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,  	case WPAN_WANTACK:  		val = ro->want_ack;  		break; +	case WPAN_SECURITY: +		if (!ro->secen_override) +			val = WPAN_SECURITY_DEFAULT; +		else if (ro->secen) +			val = WPAN_SECURITY_ON; +		else +			val = WPAN_SECURITY_OFF; +		break; +	case WPAN_SECURITY_LEVEL: +		if (!ro->seclevel_override) +			val = WPAN_SECURITY_LEVEL_DEFAULT; +		else +			val = ro->seclevel; +		break;  	default:  		return -ENOPROTOOPT;  	} @@ -429,6 +466,7 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,  		    char __user *optval, unsigned int optlen)  {  	struct dgram_sock *ro = dgram_sk(sk); +	struct net *net = sock_net(sk);  	int val;  	int err = 0; @@ -444,6 +482,47 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,  	case WPAN_WANTACK:  		ro->want_ack = !!val;  		break; +	case WPAN_SECURITY: +		if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && +		    !ns_capable(net->user_ns, CAP_NET_RAW)) { +			err = -EPERM; +			break; +		} + +		switch (val) { +		case WPAN_SECURITY_DEFAULT: +			ro->secen_override = 0; +			break; +		case WPAN_SECURITY_ON: +			ro->secen_override = 1; +			ro->secen = 1; +			break; +		case WPAN_SECURITY_OFF: +			ro->secen_override = 1; +			ro->secen = 0; +			break; +		default: +			err = -EINVAL; +			break; +		} +		break; +	case WPAN_SECURITY_LEVEL: +		if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && +		    !ns_capable(net->user_ns, CAP_NET_RAW)) { +			err = -EPERM; +			break; +		} + +		if (val < WPAN_SECURITY_LEVEL_DEFAULT || +		    val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { +			err = -EINVAL; +		} else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { +			ro->seclevel_override = 0; +		} else { +			ro->seclevel_override = 1; +			ro->seclevel = val; +		} +		break;  	default:  		err = -ENOPROTOOPT;  		break; diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c new file mode 100644 index 00000000000..c09294e39ca --- /dev/null +++ b/net/ieee802154/header_ops.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2014 Fraunhofer ITWM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * Written by: + * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> + */ + +#include <net/mac802154.h> +#include <net/ieee802154.h> +#include <net/ieee802154_netdev.h> + +static int +ieee802154_hdr_push_addr(u8 *buf, const struct ieee802154_addr *addr, +			 bool omit_pan) +{ +	int pos = 0; + +	if (addr->mode == IEEE802154_ADDR_NONE) +		return 0; + +	if (!omit_pan) { +		memcpy(buf + pos, &addr->pan_id, 2); +		pos += 2; +	} + +	switch (addr->mode) { +	case IEEE802154_ADDR_SHORT: +		memcpy(buf + pos, &addr->short_addr, 2); +		pos += 2; +		break; + +	case IEEE802154_ADDR_LONG: +		memcpy(buf + pos, &addr->extended_addr, IEEE802154_ADDR_LEN); +		pos += IEEE802154_ADDR_LEN; +		break; + +	default: +		return -EINVAL; +	} + +	return pos; +} + +static int +ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr) +{ +	int pos = 5; + +	memcpy(buf, hdr, 1); +	memcpy(buf + 1, &hdr->frame_counter, 4); + +	switch (hdr->key_id_mode) { +	case IEEE802154_SCF_KEY_IMPLICIT: +		return pos; + +	case IEEE802154_SCF_KEY_INDEX: +		break; + +	case IEEE802154_SCF_KEY_SHORT_INDEX: +		memcpy(buf + pos, &hdr->short_src, 4); +		pos += 4; +		break; + +	case IEEE802154_SCF_KEY_HW_INDEX: +		memcpy(buf + pos, &hdr->extended_src, IEEE802154_ADDR_LEN); +		pos += IEEE802154_ADDR_LEN; +		break; +	} + +	buf[pos++] = hdr->key_id; + +	return pos; +} + +int +ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +{ +	u8 buf[MAC802154_FRAME_HARD_HEADER_LEN]; +	int pos = 2; +	int rc; +	struct ieee802154_hdr_fc fc = hdr->fc; + +	buf[pos++] = hdr->seq; + +	fc.dest_addr_mode = hdr->dest.mode; + +	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false); +	if (rc < 0) +		return -EINVAL; +	pos += rc; + +	fc.source_addr_mode = hdr->source.mode; + +	if (hdr->source.pan_id == hdr->dest.pan_id && +	    hdr->dest.mode != IEEE802154_ADDR_NONE) +		fc.intra_pan = true; + +	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan); +	if (rc < 0) +		return -EINVAL; +	pos += rc; + +	if (fc.security_enabled) { +		fc.version = 1; + +		rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec); +		if (rc < 0) +			return -EINVAL; + +		pos += rc; +	} + +	memcpy(buf, &fc, 2); + +	memcpy(skb_push(skb, pos), buf, pos); + +	return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_push); + +static int +ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, +			struct ieee802154_addr *addr) +{ +	int pos = 0; + +	addr->mode = mode; + +	if (mode == IEEE802154_ADDR_NONE) +		return 0; + +	if (!omit_pan) { +		memcpy(&addr->pan_id, buf + pos, 2); +		pos += 2; +	} + +	if (mode == IEEE802154_ADDR_SHORT) { +		memcpy(&addr->short_addr, buf + pos, 2); +		return pos + 2; +	} else { +		memcpy(&addr->extended_addr, buf + pos, IEEE802154_ADDR_LEN); +		return pos + IEEE802154_ADDR_LEN; +	} +} + +static int ieee802154_hdr_addr_len(int mode, bool omit_pan) +{ +	int pan_len = omit_pan ? 0 : 2; + +	switch (mode) { +	case IEEE802154_ADDR_NONE: return 0; +	case IEEE802154_ADDR_SHORT: return 2 + pan_len; +	case IEEE802154_ADDR_LONG: return IEEE802154_ADDR_LEN + pan_len; +	default: return -EINVAL; +	} +} + +static int +ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr) +{ +	int pos = 5; + +	memcpy(hdr, buf, 1); +	memcpy(&hdr->frame_counter, buf + 1, 4); + +	switch (hdr->key_id_mode) { +	case IEEE802154_SCF_KEY_IMPLICIT: +		return pos; + +	case IEEE802154_SCF_KEY_INDEX: +		break; + +	case IEEE802154_SCF_KEY_SHORT_INDEX: +		memcpy(&hdr->short_src, buf + pos, 4); +		pos += 4; +		break; + +	case IEEE802154_SCF_KEY_HW_INDEX: +		memcpy(&hdr->extended_src, buf + pos, IEEE802154_ADDR_LEN); +		pos += IEEE802154_ADDR_LEN; +		break; +	} + +	hdr->key_id = buf[pos++]; + +	return pos; +} + +static int ieee802154_sechdr_lengths[4] = { +	[IEEE802154_SCF_KEY_IMPLICIT] = 5, +	[IEEE802154_SCF_KEY_INDEX] = 6, +	[IEEE802154_SCF_KEY_SHORT_INDEX] = 10, +	[IEEE802154_SCF_KEY_HW_INDEX] = 14, +}; + +static int ieee802154_hdr_sechdr_len(u8 sc) +{ +	return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)]; +} + +static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr) +{ +	int dlen, slen; + +	dlen = ieee802154_hdr_addr_len(hdr->fc.dest_addr_mode, false); +	slen = ieee802154_hdr_addr_len(hdr->fc.source_addr_mode, +				       hdr->fc.intra_pan); + +	if (slen < 0 || dlen < 0) +		return -EINVAL; + +	return 3 + dlen + slen + hdr->fc.security_enabled; +} + +static int +ieee802154_hdr_get_addrs(const u8 *buf, struct ieee802154_hdr *hdr) +{ +	int pos = 0; + +	pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.dest_addr_mode, +				       false, &hdr->dest); +	pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.source_addr_mode, +				       hdr->fc.intra_pan, &hdr->source); + +	if (hdr->fc.intra_pan) +		hdr->source.pan_id = hdr->dest.pan_id; + +	return pos; +} + +int +ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ +	int pos = 3, rc; + +	if (!pskb_may_pull(skb, 3)) +		return -EINVAL; + +	memcpy(hdr, skb->data, 3); + +	rc = ieee802154_hdr_minlen(hdr); +	if (rc < 0 || !pskb_may_pull(skb, rc)) +		return -EINVAL; + +	pos += ieee802154_hdr_get_addrs(skb->data + pos, hdr); + +	if (hdr->fc.security_enabled) { +		int want = pos + ieee802154_hdr_sechdr_len(skb->data[pos]); + +		if (!pskb_may_pull(skb, want)) +			return -EINVAL; + +		pos += ieee802154_hdr_get_sechdr(skb->data + pos, &hdr->sec); +	} + +	skb_pull(skb, pos); +	return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_pull); + +int +ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ +	const u8 *buf = skb_mac_header(skb); +	int pos = 3, rc; + +	if (buf + 3 > skb_tail_pointer(skb)) +		return -EINVAL; + +	memcpy(hdr, buf, 3); + +	rc = ieee802154_hdr_minlen(hdr); +	if (rc < 0 || buf + rc > skb_tail_pointer(skb)) +		return -EINVAL; + +	pos += ieee802154_hdr_get_addrs(buf + pos, hdr); +	return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs); + +int +ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ +	const u8 *buf = skb_mac_header(skb); +	int pos; + +	pos = ieee802154_hdr_peek_addrs(skb, hdr); +	if (pos < 0) +		return -EINVAL; + +	if (hdr->fc.security_enabled) { +		u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos)); +		int want = pos + ieee802154_sechdr_lengths[key_id_mode]; + +		if (buf + want > skb_tail_pointer(skb)) +			return -EINVAL; + +		pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec); +	} + +	return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_peek); + +int ieee802154_max_payload(const struct ieee802154_hdr *hdr) +{ +	int hlen = ieee802154_hdr_minlen(hdr); + +	if (hdr->fc.security_enabled) { +		hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1; +		hlen += ieee802154_sechdr_authtag_len(&hdr->sec); +	} + +	return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE; +} +EXPORT_SYMBOL_GPL(ieee802154_max_payload); diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index aadec428e6e..8b83a231299 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -47,7 +47,44 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,  int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info);  extern struct genl_family nl802154_family; -int nl802154_mac_register(void); -int nl802154_phy_register(void); + +/* genetlink ops/groups */ +int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info); +int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb); +int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info); +int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info); + +enum ieee802154_mcgrp_ids { +	IEEE802154_COORD_MCGRP, +	IEEE802154_BEACON_MCGRP, +}; + +int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info); +int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info); +int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info); +int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info); +int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info); +int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info); +int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb); +int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info); + +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_keys(struct sk_buff *skb, +			       struct netlink_callback *cb); +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devs(struct sk_buff *skb, +			       struct netlink_callback *cb); +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, +				  struct netlink_callback *cb); +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, +				    struct netlink_callback *cb);  #endif diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 7e49bbcc696..26efcf4fd2f 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -70,7 +70,7 @@ int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group)  	if (genlmsg_end(msg, hdr) < 0)  		goto out; -	return genlmsg_multicast(msg, 0, group, GFP_ATOMIC); +	return genlmsg_multicast(&nl802154_family, msg, 0, group, GFP_ATOMIC);  out:  	nlmsg_free(msg);  	return -ENOBUFS; @@ -109,31 +109,57 @@ out:  	return -ENOBUFS;  } -int __init ieee802154_nl_init(void) -{ -	int rc; - -	rc = genl_register_family(&nl802154_family); -	if (rc) -		goto fail; - -	rc = nl802154_mac_register(); -	if (rc) -		goto fail; +static const struct genl_ops ieee8021154_ops[] = { +	/* see nl-phy.c */ +	IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy, +			ieee802154_dump_phy), +	IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface), +	IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface), +	/* see nl-mac.c */ +	IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), +	IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), +	IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), +	IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), +	IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), +	IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, +			ieee802154_dump_iface), +	IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), +	IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), +	IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), +	IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, +			ieee802154_llsec_dump_keys), +	IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), +	IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), +	IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, +			ieee802154_llsec_dump_devs), +	IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), +	IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), +	IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, +			ieee802154_llsec_dump_devkeys), +	IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), +	IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), +	IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, +			ieee802154_llsec_dump_seclevels), +	IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, +		      ieee802154_llsec_add_seclevel), +	IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, +		      ieee802154_llsec_del_seclevel), +}; -	rc = nl802154_phy_register(); -	if (rc) -		goto fail; +static const struct genl_multicast_group ieee802154_mcgrps[] = { +	[IEEE802154_COORD_MCGRP] = { .name = IEEE802154_MCAST_COORD_NAME, }, +	[IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, }, +}; -	return 0; -fail: -	genl_unregister_family(&nl802154_family); -	return rc; +int __init ieee802154_nl_init(void) +{ +	return genl_register_family_with_ops_groups(&nl802154_family, +						    ieee8021154_ops, +						    ieee802154_mcgrps);  }  void __exit ieee802154_nl_exit(void)  {  	genl_unregister_family(&nl802154_family);  } - diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index b0bdd8c51e9..a3281b8bfd5 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -39,13 +39,25 @@  #include "ieee802154.h" -static struct genl_multicast_group ieee802154_coord_mcgrp = { -	.name		= IEEE802154_MCAST_COORD_NAME, -}; +static int nla_put_hwaddr(struct sk_buff *msg, int type, __le64 hwaddr) +{ +	return nla_put_u64(msg, type, swab64((__force u64)hwaddr)); +} -static struct genl_multicast_group ieee802154_beacon_mcgrp = { -	.name		= IEEE802154_MCAST_BEACON_NAME, -}; +static __le64 nla_get_hwaddr(const struct nlattr *nla) +{ +	return ieee802154_devaddr_from_raw(nla_data(nla)); +} + +static int nla_put_shortaddr(struct sk_buff *msg, int type, __le16 addr) +{ +	return nla_put_u16(msg, type, le16_to_cpu(addr)); +} + +static __le16 nla_get_shortaddr(const struct nlattr *nla) +{ +	return cpu_to_le16(nla_get_u16(nla)); +}  int ieee802154_nl_assoc_indic(struct net_device *dev,  		struct ieee802154_addr *addr, u8 cap) @@ -54,7 +66,7 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,  	pr_debug("%s\n", __func__); -	if (addr->addr_type != IEEE802154_ADDR_LONG) { +	if (addr->mode != IEEE802154_ADDR_LONG) {  		pr_err("%s: received non-long source address!\n", __func__);  		return -EINVAL;  	} @@ -67,12 +79,12 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,  	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||  	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,  		    dev->dev_addr) || -	    nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, -		    addr->hwaddr) || +	    nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, +			   addr->extended_addr) ||  	    nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -80,7 +92,7 @@ nla_put_failure:  }  EXPORT_SYMBOL(ieee802154_nl_assoc_indic); -int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, +int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,  		u8 status)  {  	struct sk_buff *msg; @@ -95,10 +107,10 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,  	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||  	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,  		    dev->dev_addr) || -	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || +	    nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||  	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -122,18 +134,18 @@ int ieee802154_nl_disassoc_indic(struct net_device *dev,  	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,  		    dev->dev_addr))  		goto nla_put_failure; -	if (addr->addr_type == IEEE802154_ADDR_LONG) { -		if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, -			    addr->hwaddr)) +	if (addr->mode == IEEE802154_ADDR_LONG) { +		if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, +				   addr->extended_addr))  			goto nla_put_failure;  	} else { -		if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, -				addr->short_addr)) +		if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, +				      addr->short_addr))  			goto nla_put_failure;  	}  	if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -157,7 +169,7 @@ int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)  		    dev->dev_addr) ||  	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -165,8 +177,8 @@ nla_put_failure:  }  EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); -int ieee802154_nl_beacon_indic(struct net_device *dev, -		u16 panid, u16 coord_addr) +int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid, +			       __le16 coord_addr)  {  	struct sk_buff *msg; @@ -180,10 +192,11 @@ int ieee802154_nl_beacon_indic(struct net_device *dev,  	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||  	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,  		    dev->dev_addr) || -	    nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) || -	    nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid)) +	    nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, +			      coord_addr) || +	    nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -214,7 +227,7 @@ int ieee802154_nl_scan_confirm(struct net_device *dev,  	    (edl &&  	     nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -238,7 +251,7 @@ int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)  		    dev->dev_addr) ||  	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))  		goto nla_put_failure; -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -251,6 +264,8 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,  {  	void *hdr;  	struct wpan_phy *phy; +	struct ieee802154_mlme_ops *ops; +	__le16 short_addr, pan_id;  	pr_debug("%s\n", __func__); @@ -259,19 +274,45 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,  	if (!hdr)  		goto out; -	phy = ieee802154_mlme_ops(dev)->get_phy(dev); +	ops = ieee802154_mlme_ops(dev); +	phy = ops->get_phy(dev);  	BUG_ON(!phy); +	short_addr = ops->get_short_addr(dev); +	pan_id = ops->get_pan_id(dev); +  	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||  	    nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||  	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||  	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,  		    dev->dev_addr) || -	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, -			ieee802154_mlme_ops(dev)->get_short_addr(dev)) || -	    nla_put_u16(msg, IEEE802154_ATTR_PAN_ID, -			ieee802154_mlme_ops(dev)->get_pan_id(dev))) +	    nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || +	    nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id))  		goto nla_put_failure; + +	if (ops->get_mac_params) { +		struct ieee802154_mac_params params; + +		ops->get_mac_params(dev, ¶ms); + +		if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, +			       params.transmit_power) || +		    nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) || +		    nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, +			       params.cca_mode) || +		    nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, +				params.cca_ed_level) || +		    nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, +			       params.csma_retries) || +		    nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE, +			       params.min_be) || +		    nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE, +			       params.max_be) || +		    nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES, +			       params.frame_retries)) +			goto nla_put_failure; +	} +  	wpan_phy_put(phy);  	return genlmsg_end(msg, hdr); @@ -309,8 +350,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)  	return dev;  } -static int ieee802154_associate_req(struct sk_buff *skb, -		struct genl_info *info) +int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)  {  	struct net_device *dev;  	struct ieee802154_addr addr; @@ -331,16 +371,16 @@ static int ieee802154_associate_req(struct sk_buff *skb,  		goto out;  	if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { -		addr.addr_type = IEEE802154_ADDR_LONG; -		nla_memcpy(addr.hwaddr, -				info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], -				IEEE802154_ADDR_LEN); +		addr.mode = IEEE802154_ADDR_LONG; +		addr.extended_addr = nla_get_hwaddr( +				info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]);  	} else { -		addr.addr_type = IEEE802154_ADDR_SHORT; -		addr.short_addr = nla_get_u16( +		addr.mode = IEEE802154_ADDR_SHORT; +		addr.short_addr = nla_get_shortaddr(  				info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);  	} -	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); +	addr.pan_id = nla_get_shortaddr( +			info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);  	if (info->attrs[IEEE802154_ATTR_PAGE])  		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); @@ -357,8 +397,7 @@ out:  	return ret;  } -static int ieee802154_associate_resp(struct sk_buff *skb, -		struct genl_info *info) +int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)  {  	struct net_device *dev;  	struct ieee802154_addr addr; @@ -375,14 +414,13 @@ static int ieee802154_associate_resp(struct sk_buff *skb,  	if (!ieee802154_mlme_ops(dev)->assoc_resp)  		goto out; -	addr.addr_type = IEEE802154_ADDR_LONG; -	nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], -			IEEE802154_ADDR_LEN); +	addr.mode = IEEE802154_ADDR_LONG; +	addr.extended_addr = nla_get_hwaddr( +			info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);  	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); -  	ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, -		nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), +		nla_get_shortaddr(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),  		nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));  out: @@ -390,8 +428,7 @@ out:  	return ret;  } -static int ieee802154_disassociate_req(struct sk_buff *skb, -		struct genl_info *info) +int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)  {  	struct net_device *dev;  	struct ieee802154_addr addr; @@ -409,13 +446,12 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,  		goto out;  	if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { -		addr.addr_type = IEEE802154_ADDR_LONG; -		nla_memcpy(addr.hwaddr, -				info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], -				IEEE802154_ADDR_LEN); +		addr.mode = IEEE802154_ADDR_LONG; +		addr.extended_addr = nla_get_hwaddr( +				info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);  	} else { -		addr.addr_type = IEEE802154_ADDR_SHORT; -		addr.short_addr = nla_get_u16( +		addr.mode = IEEE802154_ADDR_SHORT; +		addr.short_addr = nla_get_shortaddr(  				info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);  	}  	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); @@ -433,7 +469,7 @@ out:   * PAN_coordinator, battery_life_extension = 0,   * coord_realignment = 0, security_enable = 0  */ -static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) +int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  {  	struct net_device *dev;  	struct ieee802154_addr addr; @@ -460,10 +496,11 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  	if (!ieee802154_mlme_ops(dev)->start_req)  		goto out; -	addr.addr_type = IEEE802154_ADDR_SHORT; -	addr.short_addr = nla_get_u16( +	addr.mode = IEEE802154_ADDR_SHORT; +	addr.short_addr = nla_get_shortaddr(  			info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); -	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); +	addr.pan_id = nla_get_shortaddr( +			info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);  	channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);  	bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); @@ -478,7 +515,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  		page = 0; -	if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { +	if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {  		ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);  		dev_put(dev);  		return -EINVAL; @@ -492,7 +529,7 @@ out:  	return ret;  } -static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) +int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)  {  	struct net_device *dev;  	int ret = -EOPNOTSUPP; @@ -530,8 +567,7 @@ out:  	return ret;  } -static int ieee802154_list_iface(struct sk_buff *skb, -	struct genl_info *info) +int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)  {  	/* Request for interface name, index, type, IEEE address,  	   PAN Id, short address */ @@ -565,8 +601,7 @@ out_dev:  } -static int ieee802154_dump_iface(struct sk_buff *skb, -	struct netlink_callback *cb) +int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)  {  	struct net *net = sock_net(skb->sk);  	struct net_device *dev; @@ -591,40 +626,901 @@ cont:  	return skb->len;  } -static struct genl_ops ieee802154_coordinator_ops[] = { -	IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), -	IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), -	IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), -	IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), -	IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), -	IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, -							ieee802154_dump_iface), +int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) +{ +	struct net_device *dev = NULL; +	struct ieee802154_mlme_ops *ops; +	struct ieee802154_mac_params params; +	struct wpan_phy *phy; +	int rc = -EINVAL; + +	pr_debug("%s\n", __func__); + +	dev = ieee802154_nl_get_dev(info); +	if (!dev) +		return -ENODEV; + +	ops = ieee802154_mlme_ops(dev); + +	if (!ops->get_mac_params || !ops->set_mac_params) { +		rc = -EOPNOTSUPP; +		goto out; +	} + +	if (netif_running(dev)) { +		rc = -EBUSY; +		goto out; +	} + +	if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] && +	    !info->attrs[IEEE802154_ATTR_CCA_MODE] && +	    !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] && +	    !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] && +	    !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] && +	    !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] && +	    !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) +		goto out; + +	phy = ops->get_phy(dev); + +	if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || +	    (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) || +	    (!phy->set_cca_ed_level && +	     info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) || +	    (!phy->set_csma_params && +	     (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] || +	      info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] || +	      info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) || +	    (!phy->set_frame_retries && +	     info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) { +		rc = -EOPNOTSUPP; +		goto out_phy; +	} + +	ops->get_mac_params(dev, ¶ms); + +	if (info->attrs[IEEE802154_ATTR_TXPOWER]) +		params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); + +	if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) +		params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]); + +	if (info->attrs[IEEE802154_ATTR_CCA_MODE]) +		params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); + +	if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) +		params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]); + +	if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES]) +		params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]); + +	if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]) +		params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]); + +	if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) +		params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]); + +	if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) +		params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); + +	rc = ops->set_mac_params(dev, ¶ms); + +	wpan_phy_put(phy); +	dev_put(dev); +	return rc; + +out_phy: +	wpan_phy_put(phy); +out: +	dev_put(dev); +	return rc; +} + + + +static int +ieee802154_llsec_parse_key_id(struct genl_info *info, +			      struct ieee802154_llsec_key_id *desc) +{ +	memset(desc, 0, sizeof(*desc)); + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) +		return -EINVAL; + +	desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]); + +	if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { +		if (!info->attrs[IEEE802154_ATTR_PAN_ID] && +		    !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] || +		      info->attrs[IEEE802154_ATTR_HW_ADDR])) +			return -EINVAL; + +		desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); + +		if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) { +			desc->device_addr.mode = IEEE802154_ADDR_SHORT; +			desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); +		} else { +			desc->device_addr.mode = IEEE802154_ADDR_LONG; +			desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); +		} +	} + +	if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]) +		return -EINVAL; + +	if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]) +		return -EINVAL; + +	if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]) +		return -EINVAL; + +	if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT) +		desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]); + +	switch (desc->mode) { +	case IEEE802154_SCF_KEY_SHORT_INDEX: +	{ +		u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]); +		desc->short_source = cpu_to_le32(source); +		break; +	} +	case IEEE802154_SCF_KEY_HW_INDEX: +		desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]); +		break; +	} + +	return 0; +} + +static int +ieee802154_llsec_fill_key_id(struct sk_buff *msg, +			     const struct ieee802154_llsec_key_id *desc) +{ +	if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode)) +		return -EMSGSIZE; + +	if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { +		if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, +				      desc->device_addr.pan_id)) +			return -EMSGSIZE; + +		if (desc->device_addr.mode == IEEE802154_ADDR_SHORT && +		    nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, +				      desc->device_addr.short_addr)) +			return -EMSGSIZE; + +		if (desc->device_addr.mode == IEEE802154_ADDR_LONG && +		    nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, +				   desc->device_addr.extended_addr)) +			return -EMSGSIZE; +	} + +	if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id)) +		return -EMSGSIZE; + +	if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && +	    nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT, +			le32_to_cpu(desc->short_source))) +		return -EMSGSIZE; + +	if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && +	    nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED, +			   desc->extended_source)) +		return -EMSGSIZE; + +	return 0; +} + +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info) +{ +	struct sk_buff *msg; +	struct net_device *dev = NULL; +	int rc = -ENOBUFS; +	struct ieee802154_mlme_ops *ops; +	void *hdr; +	struct ieee802154_llsec_params params; + +	pr_debug("%s\n", __func__); + +	dev = ieee802154_nl_get_dev(info); +	if (!dev) +		return -ENODEV; + +	ops = ieee802154_mlme_ops(dev); +	if (!ops->llsec) { +		rc = -EOPNOTSUPP; +		goto out_dev; +	} + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!msg) +		goto out_dev; + +	hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0, +		IEEE802154_LLSEC_GETPARAMS); +	if (!hdr) +		goto out_free; + +	rc = ops->llsec->get_params(dev, ¶ms); +	if (rc < 0) +		goto out_free; + +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) || +	    nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, +			be32_to_cpu(params.frame_counter)) || +	    ieee802154_llsec_fill_key_id(msg, ¶ms.out_key)) +		goto out_free; + +	dev_put(dev); + +	return ieee802154_nl_reply(msg, info); +out_free: +	nlmsg_free(msg); +out_dev: +	dev_put(dev); +	return rc; +} + +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info) +{ +	struct net_device *dev = NULL; +	int rc = -EINVAL; +	struct ieee802154_mlme_ops *ops; +	struct ieee802154_llsec_params params; +	int changed = 0; + +	pr_debug("%s\n", __func__); + +	dev = ieee802154_nl_get_dev(info); +	if (!dev) +		return -ENODEV; + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] && +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] && +	    !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) +		goto out; + +	ops = ieee802154_mlme_ops(dev); +	if (!ops->llsec) { +		rc = -EOPNOTSUPP; +		goto out; +	} + +	if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] && +	    nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7) +		goto out; + +	if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) { +		params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]); +		changed |= IEEE802154_LLSEC_PARAM_ENABLED; +	} + +	if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) { +		if (ieee802154_llsec_parse_key_id(info, ¶ms.out_key)) +			goto out; + +		changed |= IEEE802154_LLSEC_PARAM_OUT_KEY; +	} + +	if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) { +		params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]); +		changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL; +	} + +	if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) { +		u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + +		params.frame_counter = cpu_to_be32(fc); +		changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER; +	} + +	rc = ops->llsec->set_params(dev, ¶ms, changed); + +	dev_put(dev); + +	return rc; +out: +	dev_put(dev); +	return rc; +} + + + +struct llsec_dump_data { +	struct sk_buff *skb; +	int s_idx, s_idx2; +	int portid; +	int nlmsg_seq; +	struct net_device *dev; +	struct ieee802154_mlme_ops *ops; +	struct ieee802154_llsec_table *table;  }; -/* - * No need to unregister as family unregistration will do it. - */ -int nl802154_mac_register(void) +static int +ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb, +			    int (*step)(struct llsec_dump_data*))  { -	int i; +	struct net *net = sock_net(skb->sk); +	struct net_device *dev; +	struct llsec_dump_data data; +	int idx = 0; +	int first_dev = cb->args[0];  	int rc; -	rc = genl_register_mc_group(&nl802154_family, -			&ieee802154_coord_mcgrp); -	if (rc) -		return rc; - -	rc = genl_register_mc_group(&nl802154_family, -			&ieee802154_beacon_mcgrp); -	if (rc) -		return rc; - -	for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { -		rc = genl_register_ops(&nl802154_family, -				&ieee802154_coordinator_ops[i]); -		if (rc) -			return rc; +	for_each_netdev(net, dev) { +		if (idx < first_dev || dev->type != ARPHRD_IEEE802154) +			goto skip; + +		data.ops = ieee802154_mlme_ops(dev); +		if (!data.ops->llsec) +			goto skip; + +		data.skb = skb; +		data.s_idx = cb->args[1]; +		data.s_idx2 = cb->args[2]; +		data.dev = dev; +		data.portid = NETLINK_CB(cb->skb).portid; +		data.nlmsg_seq = cb->nlh->nlmsg_seq; + +		data.ops->llsec->lock_table(dev); +		data.ops->llsec->get_table(data.dev, &data.table); +		rc = step(&data); +		data.ops->llsec->unlock_table(dev); + +		if (rc < 0) +			break; + +skip: +		idx++; +	} +	cb->args[0] = idx; + +	return skb->len; +} + +static int +ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info, +			   int (*fn)(struct net_device*, struct genl_info*)) +{ +	struct net_device *dev = NULL; +	int rc = -EINVAL; + +	dev = ieee802154_nl_get_dev(info); +	if (!dev) +		return -ENODEV; + +	if (!ieee802154_mlme_ops(dev)->llsec) +		rc = -EOPNOTSUPP; +	else +		rc = fn(dev, info); + +	dev_put(dev); +	return rc; +} + + + +static int +ieee802154_llsec_parse_key(struct genl_info *info, +			   struct ieee802154_llsec_key *key) +{ +	u8 frames; +	u32 commands[256 / 32]; + +	memset(key, 0, sizeof(*key)); + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] || +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES]) +		return -EINVAL; + +	frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]); +	if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) && +	    !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) +		return -EINVAL; + +	if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) { +		nla_memcpy(commands, +			   info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS], +			   256 / 8); + +		if (commands[0] || commands[1] || commands[2] || commands[3] || +		    commands[4] || commands[5] || commands[6] || +		    commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1)) +			return -EINVAL; + +		key->cmd_frame_ids = commands[7];  	} +	key->frame_types = frames; + +	nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES], +		   IEEE802154_LLSEC_KEY_SIZE); +  	return 0;  } + +static int llsec_add_key(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_key key; +	struct ieee802154_llsec_key_id id; + +	if (ieee802154_llsec_parse_key(info, &key) || +	    ieee802154_llsec_parse_key_id(info, &id)) +		return -EINVAL; + +	return ops->llsec->add_key(dev, &id, &key); +} + +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info) +{ +	if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != +	    (NLM_F_CREATE | NLM_F_EXCL)) +		return -EINVAL; + +	return ieee802154_nl_llsec_change(skb, info, llsec_add_key); +} + +static int llsec_remove_key(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_key_id id; + +	if (ieee802154_llsec_parse_key_id(info, &id)) +		return -EINVAL; + +	return ops->llsec->del_key(dev, &id); +} + +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info) +{ +	return ieee802154_nl_llsec_change(skb, info, llsec_remove_key); +} + +static int +ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq, +		       const struct ieee802154_llsec_key_entry *key, +		       const struct net_device *dev) +{ +	void *hdr; +	u32 commands[256 / 32]; + +	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, +			  IEEE802154_LLSEC_LIST_KEY); +	if (!hdr) +		goto out; + +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    ieee802154_llsec_fill_key_id(msg, &key->id) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES, +		       key->key->frame_types)) +		goto nla_put_failure; + +	if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) { +		memset(commands, 0, sizeof(commands)); +		commands[7] = key->key->cmd_frame_ids; +		if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, +			    sizeof(commands), commands)) +			goto nla_put_failure; +	} + +	if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES, +		    IEEE802154_LLSEC_KEY_SIZE, key->key->key)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); +	return 0; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +out: +	return -EMSGSIZE; +} + +static int llsec_iter_keys(struct llsec_dump_data *data) +{ +	struct ieee802154_llsec_key_entry *pos; +	int rc = 0, idx = 0; + +	list_for_each_entry(pos, &data->table->keys, list) { +		if (idx++ < data->s_idx) +			continue; + +		if (ieee802154_nl_fill_key(data->skb, data->portid, +					   data->nlmsg_seq, pos, data->dev)) { +			rc = -EMSGSIZE; +			break; +		} + +		data->s_idx++; +	} + +	return rc; +} + +int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb) +{ +	return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys); +} + + + +static int +llsec_parse_dev(struct genl_info *info, +		struct ieee802154_llsec_device *dev) +{ +	memset(dev, 0, sizeof(*dev)); + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || +	    !info->attrs[IEEE802154_ATTR_HW_ADDR] || +	    !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] || +	    !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] || +	    (!!info->attrs[IEEE802154_ATTR_PAN_ID] != +	     !!info->attrs[IEEE802154_ATTR_SHORT_ADDR])) +		return -EINVAL; + +	if (info->attrs[IEEE802154_ATTR_PAN_ID]) { +		dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); +		dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); +	} else { +		dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF); +	} + +	dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); +	dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); +	dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); +	dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]); + +	if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX) +		return -EINVAL; + +	return 0; +} + +static int llsec_add_dev(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_device desc; + +	if (llsec_parse_dev(info, &desc)) +		return -EINVAL; + +	return ops->llsec->add_dev(dev, &desc); +} + +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info) +{ +	if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != +	    (NLM_F_CREATE | NLM_F_EXCL)) +		return -EINVAL; + +	return ieee802154_nl_llsec_change(skb, info, llsec_add_dev); +} + +static int llsec_del_dev(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	__le64 devaddr; + +	if (!info->attrs[IEEE802154_ATTR_HW_ADDR]) +		return -EINVAL; + +	devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + +	return ops->llsec->del_dev(dev, devaddr); +} + +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info) +{ +	return ieee802154_nl_llsec_change(skb, info, llsec_del_dev); +} + +static int +ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq, +		       const struct ieee802154_llsec_device *desc, +		       const struct net_device *dev) +{ +	void *hdr; + +	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, +			  IEEE802154_LLSEC_LIST_DEV); +	if (!hdr) +		goto out; + +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) || +	    nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, +			      desc->short_addr) || +	    nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) || +	    nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, +			desc->frame_counter) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, +		       desc->seclevel_exempt) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); +	return 0; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +out: +	return -EMSGSIZE; +} + +static int llsec_iter_devs(struct llsec_dump_data *data) +{ +	struct ieee802154_llsec_device *pos; +	int rc = 0, idx = 0; + +	list_for_each_entry(pos, &data->table->devices, list) { +		if (idx++ < data->s_idx) +			continue; + +		if (ieee802154_nl_fill_dev(data->skb, data->portid, +					   data->nlmsg_seq, pos, data->dev)) { +			rc = -EMSGSIZE; +			break; +		} + +		data->s_idx++; +	} + +	return rc; +} + +int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb) +{ +	return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs); +} + + + +static int llsec_add_devkey(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_device_key key; +	__le64 devaddr; + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || +	    !info->attrs[IEEE802154_ATTR_HW_ADDR] || +	    ieee802154_llsec_parse_key_id(info, &key.key_id)) +		return -EINVAL; + +	devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); +	key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + +	return ops->llsec->add_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info) +{ +	if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != +	    (NLM_F_CREATE | NLM_F_EXCL)) +		return -EINVAL; + +	return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey); +} + +static int llsec_del_devkey(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_device_key key; +	__le64 devaddr; + +	if (!info->attrs[IEEE802154_ATTR_HW_ADDR] || +	    ieee802154_llsec_parse_key_id(info, &key.key_id)) +		return -EINVAL; + +	devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + +	return ops->llsec->del_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info) +{ +	return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey); +} + +static int +ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq, +			  __le64 devaddr, +			  const struct ieee802154_llsec_device_key *devkey, +			  const struct net_device *dev) +{ +	void *hdr; + +	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, +			  IEEE802154_LLSEC_LIST_DEVKEY); +	if (!hdr) +		goto out; + +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) || +	    nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, +			devkey->frame_counter) || +	    ieee802154_llsec_fill_key_id(msg, &devkey->key_id)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); +	return 0; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +out: +	return -EMSGSIZE; +} + +static int llsec_iter_devkeys(struct llsec_dump_data *data) +{ +	struct ieee802154_llsec_device *dpos; +	struct ieee802154_llsec_device_key *kpos; +	int rc = 0, idx = 0, idx2; + +	list_for_each_entry(dpos, &data->table->devices, list) { +		if (idx++ < data->s_idx) +			continue; + +		idx2 = 0; + +		list_for_each_entry(kpos, &dpos->keys, list) { +			if (idx2++ < data->s_idx2) +				continue; + +			if (ieee802154_nl_fill_devkey(data->skb, data->portid, +						      data->nlmsg_seq, +						      dpos->hwaddr, kpos, +						      data->dev)) { +				return rc = -EMSGSIZE; +			} + +			data->s_idx2++; +		} + +		data->s_idx++; +	} + +	return rc; +} + +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, +				  struct netlink_callback *cb) +{ +	return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys); +} + + + +static int +llsec_parse_seclevel(struct genl_info *info, +		     struct ieee802154_llsec_seclevel *sl) +{ +	memset(sl, 0, sizeof(*sl)); + +	if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] || +	    !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] || +	    !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]) +		return -EINVAL; + +	sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]); +	if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) { +		if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]) +			return -EINVAL; + +		sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]); +	} + +	sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]); +	sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); + +	return 0; +} + +static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_seclevel sl; + +	if (llsec_parse_seclevel(info, &sl)) +		return -EINVAL; + +	return ops->llsec->add_seclevel(dev, &sl); +} + +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info) +{ +	if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != +	    (NLM_F_CREATE | NLM_F_EXCL)) +		return -EINVAL; + +	return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel); +} + +static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info) +{ +	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); +	struct ieee802154_llsec_seclevel sl; + +	if (llsec_parse_seclevel(info, &sl)) +		return -EINVAL; + +	return ops->llsec->del_seclevel(dev, &sl); +} + +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info) +{ +	return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel); +} + +static int +ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq, +			    const struct ieee802154_llsec_seclevel *sl, +			    const struct net_device *dev) +{ +	void *hdr; + +	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, +			  IEEE802154_LLSEC_LIST_SECLEVEL); +	if (!hdr) +		goto out; + +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) || +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, +		       sl->device_override)) +		goto nla_put_failure; + +	if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD && +	    nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID, +		       sl->cmd_frame_id)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); +	return 0; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +out: +	return -EMSGSIZE; +} + +static int llsec_iter_seclevels(struct llsec_dump_data *data) +{ +	struct ieee802154_llsec_seclevel *pos; +	int rc = 0, idx = 0; + +	list_for_each_entry(pos, &data->table->security_levels, list) { +		if (idx++ < data->s_idx) +			continue; + +		if (ieee802154_nl_fill_seclevel(data->skb, data->portid, +						data->nlmsg_seq, pos, +						data->dev)) { +			rc = -EMSGSIZE; +			break; +		} + +		data->s_idx++; +	} + +	return rc; +} + +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, +				    struct netlink_callback *cb) +{ +	return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels); +} diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 22b1a7058fd..89b265aea15 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -77,8 +77,7 @@ out:  	return -EMSGSIZE;  } -static int ieee802154_list_phy(struct sk_buff *skb, -	struct genl_info *info) +int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)  {  	/* Request for interface name, index, type, IEEE address,  	   PAN Id, short address */ @@ -151,8 +150,7 @@ static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)  	return 0;  } -static int ieee802154_dump_phy(struct sk_buff *skb, -	struct netlink_callback *cb) +int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)  {  	struct dump_phy_data data = {  		.cb = cb, @@ -170,8 +168,7 @@ static int ieee802154_dump_phy(struct sk_buff *skb,  	return skb->len;  } -static int ieee802154_add_iface(struct sk_buff *skb, -		struct genl_info *info) +int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)  {  	struct sk_buff *msg;  	struct wpan_phy *phy; @@ -224,8 +221,10 @@ static int ieee802154_add_iface(struct sk_buff *skb,  	if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {  		type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]); -		if (type >= __IEEE802154_DEV_MAX) -			return -EINVAL; +		if (type >= __IEEE802154_DEV_MAX) { +			rc = -EINVAL; +			goto nla_put_failure; +		}  	}  	dev = phy->add_iface(phy, devname, type); @@ -273,8 +272,7 @@ out_dev:  	return rc;  } -static int ieee802154_del_iface(struct sk_buff *skb, -		struct genl_info *info) +int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)  {  	struct sk_buff *msg;  	struct wpan_phy *phy; @@ -356,28 +354,3 @@ out_dev:  	return rc;  } - -static struct genl_ops ieee802154_phy_ops[] = { -	IEEE802154_DUMP(IEEE802154_LIST_PHY, ieee802154_list_phy, -							ieee802154_dump_phy), -	IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface), -	IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface), -}; - -/* - * No need to unregister as family unregistration will do it. - */ -int nl802154_phy_register(void) -{ -	int i; -	int rc; - -	for (i = 0; i < ARRAY_SIZE(ieee802154_phy_ops); i++) { -		rc = genl_register_ops(&nl802154_family, -				&ieee802154_phy_ops[i]); -		if (rc) -			return rc; -	} - -	return 0; -} diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 6adda4d46f9..3a703ab8834 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -52,5 +52,31 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {  	[IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },  	[IEEE802154_ATTR_ED_LIST] = { .len = 27 },  	[IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, }, + +	[IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, }, +	[IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, }, +	[IEEE802154_ATTR_CSMA_RETRIES] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_CSMA_MIN_BE] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, }, + +	[IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, }, + +	[IEEE802154_ATTR_LLSEC_ENABLED] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_SECLEVEL] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_KEY_MODE] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT] = { .type = NLA_U32, }, +	[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED] = { .type = NLA_HW_ADDR, }, +	[IEEE802154_ATTR_LLSEC_KEY_ID] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] = { .type = NLA_U32 }, +	[IEEE802154_ATTR_LLSEC_KEY_BYTES] = { .len = 16, }, +	[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS] = { .len = 258 / 8 }, +	[IEEE802154_ATTR_LLSEC_FRAME_TYPE] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_SECLEVELS] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] = { .type = NLA_U8, }, +	[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] = { .type = NLA_U8, },  }; diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index 41f538b8e59..74d54fae33d 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -28,6 +28,7 @@  #include <linux/slab.h>  #include <net/sock.h>  #include <net/af_ieee802154.h> +#include <net/ieee802154_netdev.h>  #include "af802154.h" @@ -55,21 +56,24 @@ static void raw_close(struct sock *sk, long timeout)  	sk_common_release(sk);  } -static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len) +static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len)  { -	struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; +	struct ieee802154_addr addr; +	struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr;  	int err = 0;  	struct net_device *dev = NULL; -	if (len < sizeof(*addr)) +	if (len < sizeof(*uaddr))  		return -EINVAL; -	if (addr->family != AF_IEEE802154) +	uaddr = (struct sockaddr_ieee802154 *)_uaddr; +	if (uaddr->family != AF_IEEE802154)  		return -EINVAL;  	lock_sock(sk); -	dev = ieee802154_get_dev(sock_net(sk), &addr->addr); +	ieee802154_addr_from_sa(&addr, &uaddr->addr); +	dev = ieee802154_get_dev(sock_net(sk), &addr);  	if (!dev) {  		err = -ENODEV;  		goto out; @@ -209,6 +213,10 @@ out:  static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)  { +	skb = skb_share_check(skb, GFP_ATOMIC); +	if (!skb) +		return NET_RX_DROP; +  	if (sock_queue_rcv_skb(sk, skb) < 0) {  		kfree_skb(skb);  		return NET_RX_DROP; diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c new file mode 100644 index 00000000000..6f1428c4870 --- /dev/null +++ b/net/ieee802154/reassembly.c @@ -0,0 +1,585 @@ +/*	6LoWPAN fragment reassembly + * + * + *	Authors: + *	Alexander Aring		<aar@pengutronix.de> + * + *	Based on: net/ipv6/reassembly.c + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; either version + *	2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "6LoWPAN: " fmt + +#include <linux/net.h> +#include <linux/list.h> +#include <linux/netdevice.h> +#include <linux/random.h> +#include <linux/jhash.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/export.h> + +#include <net/ieee802154_netdev.h> +#include <net/6lowpan.h> +#include <net/ipv6.h> +#include <net/inet_frag.h> + +#include "reassembly.h" + +struct lowpan_frag_info { +	__be16 d_tag; +	u16 d_size; +	u8 d_offset; +}; + +static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb) +{ +	return (struct lowpan_frag_info *)skb->cb; +} + +static struct inet_frags lowpan_frags; + +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, +			     struct sk_buff *prev, struct net_device *dev); + +static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, +				     const struct ieee802154_addr *saddr, +				     const struct ieee802154_addr *daddr) +{ +	u32 c; + +	net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd)); +	c = jhash_3words(ieee802154_addr_hash(saddr), +			 ieee802154_addr_hash(daddr), +			 (__force u32)(tag + (d_size << 16)), +			 lowpan_frags.rnd); + +	return c & (INETFRAGS_HASHSZ - 1); +} + +static unsigned int lowpan_hashfn(struct inet_frag_queue *q) +{ +	struct lowpan_frag_queue *fq; + +	fq = container_of(q, struct lowpan_frag_queue, q); +	return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); +} + +static bool lowpan_frag_match(struct inet_frag_queue *q, void *a) +{ +	struct lowpan_frag_queue *fq; +	struct lowpan_create_arg *arg = a; + +	fq = container_of(q, struct lowpan_frag_queue, q); +	return	fq->tag == arg->tag && fq->d_size == arg->d_size && +		ieee802154_addr_equal(&fq->saddr, arg->src) && +		ieee802154_addr_equal(&fq->daddr, arg->dst); +} + +static void lowpan_frag_init(struct inet_frag_queue *q, void *a) +{ +	struct lowpan_frag_queue *fq; +	struct lowpan_create_arg *arg = a; + +	fq = container_of(q, struct lowpan_frag_queue, q); + +	fq->tag = arg->tag; +	fq->d_size = arg->d_size; +	fq->saddr = *arg->src; +	fq->daddr = *arg->dst; +} + +static void lowpan_frag_expire(unsigned long data) +{ +	struct frag_queue *fq; +	struct net *net; + +	fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); +	net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); + +	spin_lock(&fq->q.lock); + +	if (fq->q.last_in & INET_FRAG_COMPLETE) +		goto out; + +	inet_frag_kill(&fq->q, &lowpan_frags); +out: +	spin_unlock(&fq->q.lock); +	inet_frag_put(&fq->q, &lowpan_frags); +} + +static inline struct lowpan_frag_queue * +fq_find(struct net *net, const struct lowpan_frag_info *frag_info, +	const struct ieee802154_addr *src, +	const struct ieee802154_addr *dst) +{ +	struct inet_frag_queue *q; +	struct lowpan_create_arg arg; +	unsigned int hash; +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); + +	arg.tag = frag_info->d_tag; +	arg.d_size = frag_info->d_size; +	arg.src = src; +	arg.dst = dst; + +	read_lock(&lowpan_frags.lock); +	hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); + +	q = inet_frag_find(&ieee802154_lowpan->frags, +			   &lowpan_frags, &arg, hash); +	if (IS_ERR_OR_NULL(q)) { +		inet_frag_maybe_warn_overflow(q, pr_fmt()); +		return NULL; +	} +	return container_of(q, struct lowpan_frag_queue, q); +} + +static int lowpan_frag_queue(struct lowpan_frag_queue *fq, +			     struct sk_buff *skb, const u8 frag_type) +{ +	struct sk_buff *prev, *next; +	struct net_device *dev; +	int end, offset; + +	if (fq->q.last_in & INET_FRAG_COMPLETE) +		goto err; + +	offset = lowpan_cb(skb)->d_offset << 3; +	end = lowpan_cb(skb)->d_size; + +	/* Is this the final fragment? */ +	if (offset + skb->len == end) { +		/* If we already have some bits beyond end +		 * or have different end, the segment is corrupted. +		 */ +		if (end < fq->q.len || +		    ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) +			goto err; +		fq->q.last_in |= INET_FRAG_LAST_IN; +		fq->q.len = end; +	} else { +		if (end > fq->q.len) { +			/* Some bits beyond end -> corruption. */ +			if (fq->q.last_in & INET_FRAG_LAST_IN) +				goto err; +			fq->q.len = end; +		} +	} + +	/* Find out which fragments are in front and at the back of us +	 * in the chain of fragments so far.  We must know where to put +	 * this fragment, right? +	 */ +	prev = fq->q.fragments_tail; +	if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) { +		next = NULL; +		goto found; +	} +	prev = NULL; +	for (next = fq->q.fragments; next != NULL; next = next->next) { +		if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset) +			break;	/* bingo! */ +		prev = next; +	} + +found: +	/* Insert this fragment in the chain of fragments. */ +	skb->next = next; +	if (!next) +		fq->q.fragments_tail = skb; +	if (prev) +		prev->next = skb; +	else +		fq->q.fragments = skb; + +	dev = skb->dev; +	if (dev) +		skb->dev = NULL; + +	fq->q.stamp = skb->tstamp; +	if (frag_type == LOWPAN_DISPATCH_FRAG1) { +		/* Calculate uncomp. 6lowpan header to estimate full size */ +		fq->q.meat += lowpan_uncompress_size(skb, NULL); +		fq->q.last_in |= INET_FRAG_FIRST_IN; +	} else { +		fq->q.meat += skb->len; +	} +	add_frag_mem_limit(&fq->q, skb->truesize); + +	if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && +	    fq->q.meat == fq->q.len) { +		int res; +		unsigned long orefdst = skb->_skb_refdst; + +		skb->_skb_refdst = 0UL; +		res = lowpan_frag_reasm(fq, prev, dev); +		skb->_skb_refdst = orefdst; +		return res; +	} + +	inet_frag_lru_move(&fq->q); +	return -1; +err: +	kfree_skb(skb); +	return -1; +} + +/*	Check if this packet is complete. + *	Returns NULL on failure by any reason, and pointer + *	to current nexthdr field in reassembled frame. + * + *	It is called with locked fq, and caller must check that + *	queue is eligible for reassembly i.e. it is not COMPLETE, + *	the last and the first frames arrived and all the bits are here. + */ +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, +			     struct net_device *dev) +{ +	struct sk_buff *fp, *head = fq->q.fragments; +	int sum_truesize; + +	inet_frag_kill(&fq->q, &lowpan_frags); + +	/* Make the one we just received the head. */ +	if (prev) { +		head = prev->next; +		fp = skb_clone(head, GFP_ATOMIC); + +		if (!fp) +			goto out_oom; + +		fp->next = head->next; +		if (!fp->next) +			fq->q.fragments_tail = fp; +		prev->next = fp; + +		skb_morph(head, fq->q.fragments); +		head->next = fq->q.fragments->next; + +		consume_skb(fq->q.fragments); +		fq->q.fragments = head; +	} + +	/* Head of list must not be cloned. */ +	if (skb_unclone(head, GFP_ATOMIC)) +		goto out_oom; + +	/* If the first fragment is fragmented itself, we split +	 * it to two chunks: the first with data and paged part +	 * and the second, holding only fragments. +	 */ +	if (skb_has_frag_list(head)) { +		struct sk_buff *clone; +		int i, plen = 0; + +		clone = alloc_skb(0, GFP_ATOMIC); +		if (!clone) +			goto out_oom; +		clone->next = head->next; +		head->next = clone; +		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; +		skb_frag_list_init(head); +		for (i = 0; i < skb_shinfo(head)->nr_frags; i++) +			plen += skb_frag_size(&skb_shinfo(head)->frags[i]); +		clone->len = head->data_len - plen; +		clone->data_len = clone->len; +		head->data_len -= clone->len; +		head->len -= clone->len; +		add_frag_mem_limit(&fq->q, clone->truesize); +	} + +	WARN_ON(head == NULL); + +	sum_truesize = head->truesize; +	for (fp = head->next; fp;) { +		bool headstolen; +		int delta; +		struct sk_buff *next = fp->next; + +		sum_truesize += fp->truesize; +		if (skb_try_coalesce(head, fp, &headstolen, &delta)) { +			kfree_skb_partial(fp, headstolen); +		} else { +			if (!skb_shinfo(head)->frag_list) +				skb_shinfo(head)->frag_list = fp; +			head->data_len += fp->len; +			head->len += fp->len; +			head->truesize += fp->truesize; +		} +		fp = next; +	} +	sub_frag_mem_limit(&fq->q, sum_truesize); + +	head->next = NULL; +	head->dev = dev; +	head->tstamp = fq->q.stamp; + +	fq->q.fragments = NULL; +	fq->q.fragments_tail = NULL; + +	return 1; +out_oom: +	net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); +	return -1; +} + +static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, +				struct lowpan_frag_info *frag_info) +{ +	bool fail; +	u8 pattern = 0, low = 0; + +	fail = lowpan_fetch_skb(skb, &pattern, 1); +	fail |= lowpan_fetch_skb(skb, &low, 1); +	frag_info->d_size = (pattern & 7) << 8 | low; +	fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2); + +	if (frag_type == LOWPAN_DISPATCH_FRAGN) { +		fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); +	} else { +		skb_reset_network_header(skb); +		frag_info->d_offset = 0; +	} + +	if (unlikely(fail)) +		return -EIO; + +	return 0; +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) +{ +	struct lowpan_frag_queue *fq; +	struct net *net = dev_net(skb->dev); +	struct lowpan_frag_info *frag_info = lowpan_cb(skb); +	struct ieee802154_addr source, dest; +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); +	int err; + +	source = mac_cb(skb)->source; +	dest = mac_cb(skb)->dest; + +	err = lowpan_get_frag_info(skb, frag_type, frag_info); +	if (err < 0) +		goto err; + +	if (frag_info->d_size > ieee802154_lowpan->max_dsize) +		goto err; + +	inet_frag_evictor(&ieee802154_lowpan->frags, &lowpan_frags, false); + +	fq = fq_find(net, frag_info, &source, &dest); +	if (fq != NULL) { +		int ret; +		spin_lock(&fq->q.lock); +		ret = lowpan_frag_queue(fq, skb, frag_type); +		spin_unlock(&fq->q.lock); + +		inet_frag_put(&fq->q, &lowpan_frags); +		return ret; +	} + +err: +	kfree_skb(skb); +	return -1; +} +EXPORT_SYMBOL(lowpan_frag_rcv); + +#ifdef CONFIG_SYSCTL +static struct ctl_table lowpan_frags_ns_ctl_table[] = { +	{ +		.procname	= "6lowpanfrag_high_thresh", +		.data		= &init_net.ieee802154_lowpan.frags.high_thresh, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec +	}, +	{ +		.procname	= "6lowpanfrag_low_thresh", +		.data		= &init_net.ieee802154_lowpan.frags.low_thresh, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec +	}, +	{ +		.procname	= "6lowpanfrag_time", +		.data		= &init_net.ieee802154_lowpan.frags.timeout, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec_jiffies, +	}, +	{ +		.procname	= "6lowpanfrag_max_datagram_size", +		.data		= &init_net.ieee802154_lowpan.max_dsize, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec +	}, +	{ } +}; + +static struct ctl_table lowpan_frags_ctl_table[] = { +	{ +		.procname	= "6lowpanfrag_secret_interval", +		.data		= &lowpan_frags.secret_interval, +		.maxlen		= sizeof(int), +		.mode		= 0644, +		.proc_handler	= proc_dointvec_jiffies, +	}, +	{ } +}; + +static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) +{ +	struct ctl_table *table; +	struct ctl_table_header *hdr; +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); + +	table = lowpan_frags_ns_ctl_table; +	if (!net_eq(net, &init_net)) { +		table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), +				GFP_KERNEL); +		if (table == NULL) +			goto err_alloc; + +		table[0].data = &ieee802154_lowpan->frags.high_thresh; +		table[1].data = &ieee802154_lowpan->frags.low_thresh; +		table[2].data = &ieee802154_lowpan->frags.timeout; +		table[3].data = &ieee802154_lowpan->max_dsize; + +		/* Don't export sysctls to unprivileged users */ +		if (net->user_ns != &init_user_ns) +			table[0].procname = NULL; +	} + +	hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); +	if (hdr == NULL) +		goto err_reg; + +	ieee802154_lowpan->sysctl.frags_hdr = hdr; +	return 0; + +err_reg: +	if (!net_eq(net, &init_net)) +		kfree(table); +err_alloc: +	return -ENOMEM; +} + +static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) +{ +	struct ctl_table *table; +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); + +	table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg; +	unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr); +	if (!net_eq(net, &init_net)) +		kfree(table); +} + +static struct ctl_table_header *lowpan_ctl_header; + +static int lowpan_frags_sysctl_register(void) +{ +	lowpan_ctl_header = register_net_sysctl(&init_net, +						"net/ieee802154/6lowpan", +						lowpan_frags_ctl_table); +	return lowpan_ctl_header == NULL ? -ENOMEM : 0; +} + +static void lowpan_frags_sysctl_unregister(void) +{ +	unregister_net_sysctl_table(lowpan_ctl_header); +} +#else +static inline int lowpan_frags_ns_sysctl_register(struct net *net) +{ +	return 0; +} + +static inline void lowpan_frags_ns_sysctl_unregister(struct net *net) +{ +} + +static inline int lowpan_frags_sysctl_register(void) +{ +	return 0; +} + +static inline void lowpan_frags_sysctl_unregister(void) +{ +} +#endif + +static int __net_init lowpan_frags_init_net(struct net *net) +{ +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); + +	ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; +	ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; +	ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; +	ieee802154_lowpan->max_dsize = 0xFFFF; + +	inet_frags_init_net(&ieee802154_lowpan->frags); + +	return lowpan_frags_ns_sysctl_register(net); +} + +static void __net_exit lowpan_frags_exit_net(struct net *net) +{ +	struct netns_ieee802154_lowpan *ieee802154_lowpan = +		net_ieee802154_lowpan(net); + +	lowpan_frags_ns_sysctl_unregister(net); +	inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags); +} + +static struct pernet_operations lowpan_frags_ops = { +	.init = lowpan_frags_init_net, +	.exit = lowpan_frags_exit_net, +}; + +int __init lowpan_net_frag_init(void) +{ +	int ret; + +	ret = lowpan_frags_sysctl_register(); +	if (ret) +		return ret; + +	ret = register_pernet_subsys(&lowpan_frags_ops); +	if (ret) +		goto err_pernet; + +	lowpan_frags.hashfn = lowpan_hashfn; +	lowpan_frags.constructor = lowpan_frag_init; +	lowpan_frags.destructor = NULL; +	lowpan_frags.skb_free = NULL; +	lowpan_frags.qsize = sizeof(struct frag_queue); +	lowpan_frags.match = lowpan_frag_match; +	lowpan_frags.frag_expire = lowpan_frag_expire; +	lowpan_frags.secret_interval = 10 * 60 * HZ; +	inet_frags_init(&lowpan_frags); + +	return ret; +err_pernet: +	lowpan_frags_sysctl_unregister(); +	return ret; +} + +void lowpan_net_frag_exit(void) +{ +	inet_frags_fini(&lowpan_frags); +	lowpan_frags_sysctl_unregister(); +	unregister_pernet_subsys(&lowpan_frags_ops); +} diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h new file mode 100644 index 00000000000..74e4a7c9819 --- /dev/null +++ b/net/ieee802154/reassembly.h @@ -0,0 +1,41 @@ +#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ +#define __IEEE802154_6LOWPAN_REASSEMBLY_H__ + +#include <net/inet_frag.h> + +struct lowpan_create_arg { +	__be16 tag; +	u16 d_size; +	const struct ieee802154_addr *src; +	const struct ieee802154_addr *dst; +}; + +/* Equivalent of ipv4 struct ip + */ +struct lowpan_frag_queue { +	struct inet_frag_queue	q; + +	__be16			tag; +	u16			d_size; +	struct ieee802154_addr	saddr; +	struct ieee802154_addr	daddr; +}; + +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) +{ +	switch (a->mode) { +	case IEEE802154_ADDR_LONG: +		return (((__force u64)a->extended_addr) >> 32) ^ +			(((__force u64)a->extended_addr) & 0xffffffff); +	case IEEE802154_ADDR_SHORT: +		return (__force u32)(a->short_addr); +	default: +		return 0; +	} +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); +void lowpan_net_frag_exit(void); +int lowpan_net_frag_init(void); + +#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c index ef56ab5b35f..8d6f6704da8 100644 --- a/net/ieee802154/wpan-class.c +++ b/net/ieee802154/wpan-class.c @@ -44,9 +44,7 @@ static DEVICE_ATTR_RO(name);  MASTER_SHOW(current_channel, "%d");  MASTER_SHOW(current_page, "%d"); -MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB", -	((signed char) (phy->transmit_power << 2)) >> 2, -	(phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 ); +MASTER_SHOW(transmit_power, "%d +- 1 dB");  MASTER_SHOW(cca_mode, "%d");  static ssize_t channels_supported_show(struct device *dev,  | 
