diff options
Diffstat (limited to 'net/ieee802154')
| -rw-r--r-- | net/ieee802154/6lowpan_iphc.c | 801 | ||||
| -rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 683 | ||||
| -rw-r--r-- | net/ieee802154/Kconfig | 16 | ||||
| -rw-r--r-- | net/ieee802154/Makefile | 13 | ||||
| -rw-r--r-- | net/ieee802154/af802154.h | 5 | ||||
| -rw-r--r-- | net/ieee802154/af_ieee802154.c | 24 | ||||
| -rw-r--r-- | net/ieee802154/dgram.c | 187 | ||||
| -rw-r--r-- | net/ieee802154/header_ops.c | 325 | ||||
| -rw-r--r-- | net/ieee802154/ieee802154.h | 41 | ||||
| -rw-r--r-- | net/ieee802154/netlink.c | 78 | ||||
| -rw-r--r-- | net/ieee802154/nl-mac.c | 1234 | ||||
| -rw-r--r-- | net/ieee802154/nl-phy.c | 117 | ||||
| -rw-r--r-- | net/ieee802154/nl_policy.c | 26 | ||||
| -rw-r--r-- | net/ieee802154/raw.c | 30 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.c | 585 | ||||
| -rw-r--r-- | net/ieee802154/reassembly.h | 41 | ||||
| -rw-r--r-- | net/ieee802154/wpan-class.c | 32 | 
17 files changed, 3901 insertions, 337 deletions
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 1c1de97d264..8af1330b313 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -1,6 +1,5 @@  config IEEE802154 -	tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support (EXPERIMENTAL)" -	depends on EXPERIMENTAL +	tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support"  	---help---  	  IEEE Std 802.15.4 defines a low data rate, low power and low  	  complexity short range wireless personal area networks. It was @@ -10,3 +9,16 @@ config IEEE802154  	  Say Y here to compile LR-WPAN support into the kernel or say M to  	  compile it as modules. + +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. + +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 ce2d3358285..bf1b51497a4 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,5 +1,10 @@ -obj-$(CONFIG_IEEE802154) +=	ieee802154.o af_802154.o -ieee802154-y		:= netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o -af_802154-y		:= af_ieee802154.o raw.o dgram.o +obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o +obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o +obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o -ccflags-y += -Wall -DDEBUG +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 6df6ecf4970..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;  	} @@ -302,7 +304,7 @@ static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,  	struct packet_type *pt, struct net_device *orig_dev)  {  	if (!netif_running(dev)) -		return -ENODEV; +		goto drop;  	pr_debug("got frame, type %d, dev %p\n", dev->type, dev);  #ifdef DEBUG  	print_hex_dump_bytes("ieee802154_rcv ", DUMP_PREFIX_NONE, skb->data, skb->len); @@ -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 1a3334c2609..4f0ed878019 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -1,5 +1,5 @@  /* - * ZigBee socket interface + * IEEE 802.15.4 dgram socket interface   *   * Copyright 2007, 2008 Siemens AG   * @@ -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> @@ -44,8 +45,13 @@ struct dgram_sock {  	struct ieee802154_addr src_addr;  	struct ieee802154_addr dst_addr; -	unsigned bound:1; -	unsigned want_ack:1; +	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; @@ -206,9 +208,12 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  		struct msghdr *msg, size_t size)  {  	struct net_device *dev; -	unsigned mtu; +	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;  	if (msg->msg_flags & MSG_OOB) { @@ -216,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 @@ -229,38 +239,50 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,  	mtu = dev->mtu;  	pr_debug("name = %s, mtu = %u\n", dev->name, mtu); -	skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, +	if (size > mtu) { +		pr_debug("size = %Zu, mtu = %u\n", size, mtu); +		err = -EMSGSIZE; +		goto out_dev; +	} + +	hlen = LL_RESERVED_SPACE(dev); +	tlen = dev->needed_tailroom; +	skb = sock_alloc_send_skb(sk, hlen + tlen + size,  			msg->msg_flags & MSG_DONTWAIT,  			&err);  	if (!skb)  		goto out_dev; -	skb_reserve(skb, LL_RESERVED_SPACE(dev)); +	skb_reserve(skb, hlen);  	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; -	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); +	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; +	} + +	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; -	if (size > mtu) { -		pr_debug("size = %Zu, mtu = %u\n", size, mtu); -		err = -EINVAL; -		goto out_skb; -	} -  	skb->dev = dev;  	skb->sk  = sk;  	skb->protocol = htons(ETH_P_IEEE802154); @@ -288,6 +310,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,  	size_t copied = 0;  	int err = -EOPNOTSUPP;  	struct sk_buff *skb; +	DECLARE_SOCKADDR(struct sockaddr_ieee802154 *, saddr, msg->msg_name);  	skb = skb_recv_datagram(sk, flags, noblock, &err);  	if (!skb) @@ -306,6 +329,12 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,  	sock_recv_ts_and_drops(msg, sk, skb); +	if (saddr) { +		saddr->family = AF_IEEE802154; +		ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); +		*addr_len = sizeof(*saddr); +	} +  	if (flags & MSG_TRUNC)  		copied = skb->len;  done: @@ -318,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; @@ -326,41 +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; -	struct hlist_node *node;  	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, node, &dgram_head) { -		if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr, -					dgram_sk(sk))) { +	sk_for_each(sk, &dgram_head) { +		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); @@ -402,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;  	} @@ -417,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; @@ -432,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 c8097ae2482..26efcf4fd2f 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -44,7 +44,7 @@ struct genl_family nl802154_family = {  struct sk_buff *ieee802154_nl_create(int flags, u8 req)  {  	void *hdr; -	struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); +	struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);  	unsigned long f;  	if (!msg) @@ -64,13 +64,13 @@ struct sk_buff *ieee802154_nl_create(int flags, u8 req)  int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group)  { -	/* XXX: nlh is right at the start of msg */ -	void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); +	struct nlmsghdr *nlh = nlmsg_hdr(msg); +	void *hdr = genlmsg_data(nlmsg_data(nlh));  	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; @@ -80,7 +80,7 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,  		int flags, u8 req)  {  	void *hdr; -	struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); +	struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);  	if (!msg)  		return NULL; @@ -97,8 +97,8 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,  int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info)  { -	/* XXX: nlh is right at the start of msg */ -	void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); +	struct nlmsghdr *nlh = nlmsg_hdr(msg); +	void *hdr = genlmsg_data(nlmsg_data(nlh));  	if (genlmsg_end(msg, hdr) < 0)  		goto out; @@ -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 71ee1108d4f..a3281b8bfd5 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -30,6 +30,7 @@  #include <net/genetlink.h>  #include <net/sock.h>  #include <linux/nl802154.h> +#include <linux/export.h>  #include <net/af_ieee802154.h>  #include <net/nl802154.h>  #include <net/ieee802154.h> @@ -38,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) @@ -53,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;  	} @@ -62,17 +75,16 @@ int ieee802154_nl_assoc_indic(struct net_device *dev,  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); -	NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, -			dev->dev_addr); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, +			   addr->extended_addr) || +	    nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap)) +		goto nla_put_failure; -	NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, -			addr->hwaddr); - -	NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap); - -	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; @@ -91,15 +103,14 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	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_U8(msg, IEEE802154_ATTR_STATUS, status); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_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);  nla_put_failure:  	nlmsg_free(msg); @@ -118,21 +129,23 @@ int ieee802154_nl_disassoc_indic(struct net_device *dev,  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); -	NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, -			dev->dev_addr); - -	if (addr->addr_type == IEEE802154_ADDR_LONG) -		NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, -				addr->hwaddr); -	else -		NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, -				addr->short_addr); - -	NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || +	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, +		    dev->dev_addr)) +		goto nla_put_failure; +	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_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);  nla_put_failure:  	nlmsg_free(msg); @@ -150,14 +163,13 @@ int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	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_U8(msg, IEEE802154_ATTR_STATUS, status); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_u8(msg, IEEE802154_ATTR_STATUS, status)) +		goto nla_put_failure; +	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; @@ -176,14 +188,15 @@ int ieee802154_nl_beacon_indic(struct net_device *dev,  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	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); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_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);  nla_put_failure:  	nlmsg_free(msg); @@ -203,20 +216,18 @@ int ieee802154_nl_scan_confirm(struct net_device *dev,  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	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_U8(msg, IEEE802154_ATTR_STATUS, status); -	NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); -	NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); -	NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page); - -	if (edl) -		NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_u8(msg, IEEE802154_ATTR_STATUS, status) || +	    nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) || +	    nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) || +	    nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) || +	    (edl && +	     nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl))) +		goto nla_put_failure; +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -234,14 +245,13 @@ int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)  	if (!msg)  		return -ENOBUFS; -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); -	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_U8(msg, IEEE802154_ATTR_STATUS, status); - -	return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); +	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || +	    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_u8(msg, IEEE802154_ATTR_STATUS, status)) +		goto nla_put_failure; +	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);  nla_put_failure:  	nlmsg_free(msg); @@ -249,11 +259,13 @@ nla_put_failure:  }  EXPORT_SYMBOL(ieee802154_nl_start_confirm); -static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, +static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,  	u32 seq, int flags, struct net_device *dev)  {  	void *hdr;  	struct wpan_phy *phy; +	struct ieee802154_mlme_ops *ops; +	__le16 short_addr, pan_id;  	pr_debug("%s\n", __func__); @@ -262,19 +274,45 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid,  	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); -	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); +	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_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; +	} -	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));  	wpan_phy_put(phy);  	return genlmsg_end(msg, hdr); @@ -312,13 +350,12 @@ 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;  	u8 page; -	int ret = -EINVAL; +	int ret = -EOPNOTSUPP;  	if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||  	    !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || @@ -330,18 +367,20 @@ static int ieee802154_associate_req(struct sk_buff *skb,  	dev = ieee802154_nl_get_dev(info);  	if (!dev)  		return -ENODEV; +	if (!ieee802154_mlme_ops(dev)->assoc_req) +		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]); @@ -353,16 +392,16 @@ static int ieee802154_associate_req(struct sk_buff *skb,  			page,  			nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); +out:  	dev_put(dev);  	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; -	int ret = -EINVAL; +	int ret = -EOPNOTSUPP;  	if (!info->attrs[IEEE802154_ATTR_STATUS] ||  	    !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] || @@ -372,27 +411,28 @@ static int ieee802154_associate_resp(struct sk_buff *skb,  	dev = ieee802154_nl_get_dev(info);  	if (!dev)  		return -ENODEV; +	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:  	dev_put(dev);  	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; -	int ret = -EINVAL; +	int ret = -EOPNOTSUPP;  	if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&  		!info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) || @@ -402,15 +442,16 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,  	dev = ieee802154_nl_get_dev(info);  	if (!dev)  		return -ENODEV; +	if (!ieee802154_mlme_ops(dev)->disassoc_req) +		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); @@ -418,6 +459,7 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,  	ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,  			nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); +out:  	dev_put(dev);  	return ret;  } @@ -427,7 +469,7 @@ static int ieee802154_disassociate_req(struct sk_buff *skb,   * 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; @@ -435,7 +477,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  	u8 channel, bcn_ord, sf_ord;  	u8 page;  	int pan_coord, blx, coord_realign; -	int ret; +	int ret = -EOPNOTSUPP;  	if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||  	    !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || @@ -451,11 +493,14 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  	dev = ieee802154_nl_get_dev(info);  	if (!dev)  		return -ENODEV; +	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]); @@ -470,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; @@ -479,14 +524,15 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)  	ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,  		bcn_ord, sf_ord, pan_coord, blx, coord_realign); +out:  	dev_put(dev);  	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; +	int ret = -EOPNOTSUPP;  	u8 type;  	u32 channels;  	u8 duration; @@ -500,6 +546,8 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)  	dev = ieee802154_nl_get_dev(info);  	if (!dev)  		return -ENODEV; +	if (!ieee802154_mlme_ops(dev)->scan_req) +		goto out;  	type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);  	channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); @@ -514,12 +562,12 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)  	ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,  			duration); +out:  	dev_put(dev);  	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 */ @@ -533,11 +581,11 @@ static int ieee802154_list_iface(struct sk_buff *skb,  	if (!dev)  		return -ENODEV; -	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg)  		goto out_dev; -	rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, +	rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,  			0, dev);  	if (rc < 0)  		goto out_free; @@ -553,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; @@ -568,7 +615,7 @@ static int ieee802154_dump_iface(struct sk_buff *skb,  		if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))  			goto cont; -		if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, +		if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,  			cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)  			break;  cont: @@ -579,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 ed0eab39f53..89b265aea15 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -24,6 +24,7 @@  #include <linux/kernel.h>  #include <linux/slab.h> +#include <linux/if_arp.h>  #include <net/netlink.h>  #include <net/genetlink.h>  #include <net/wpan-phy.h> @@ -34,7 +35,7 @@  #include "ieee802154.h" -static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid, +static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,  	u32 seq, int flags, struct wpan_phy *phy)  {  	void *hdr; @@ -44,7 +45,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,  	pr_debug("%s\n", __func__);  	if (!buf) -		goto out; +		return -EMSGSIZE;  	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,  		IEEE802154_LIST_PHY); @@ -52,19 +53,20 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid,  		goto out;  	mutex_lock(&phy->pib_lock); -	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); - -	NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page); -	NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel); +	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || +	    nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || +	    nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel)) +		goto nla_put_failure;  	for (i = 0; i < 32; i++) {  		if (phy->channels_supported[i])  			buf[pages++] = phy->channels_supported[i] | (i << 27);  	} -	if (pages) -		NLA_PUT(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST, -				pages * sizeof(uint32_t), buf); - +	if (pages && +	    nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST, +		    pages * sizeof(uint32_t), buf)) +		goto nla_put_failure;  	mutex_unlock(&phy->pib_lock); +	kfree(buf);  	return genlmsg_end(msg, hdr);  nla_put_failure: @@ -75,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 */ @@ -99,11 +100,11 @@ static int ieee802154_list_phy(struct sk_buff *skb,  	if (!phy)  		return -ENODEV; -	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg)  		goto out_dev; -	rc = ieee802154_nl_fill_phy(msg, info->snd_pid, info->snd_seq, +	rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,  			0, phy);  	if (rc < 0)  		goto out_free; @@ -136,7 +137,7 @@ static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)  		return 0;  	rc = ieee802154_nl_fill_phy(data->skb, -			NETLINK_CB(data->cb->skb).pid, +			NETLINK_CB(data->cb->skb).portid,  			data->cb->nlh->nlmsg_seq,  			NLM_F_MULTI,  			phy); @@ -149,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, @@ -168,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; @@ -177,6 +176,7 @@ static int ieee802154_add_iface(struct sk_buff *skb,  	const char *devname;  	int rc = -ENOBUFS;  	struct net_device *dev; +	int type = __IEEE802154_DEV_INVALID;  	pr_debug("%s\n", __func__); @@ -212,21 +212,59 @@ static int ieee802154_add_iface(struct sk_buff *skb,  		goto nla_put_failure;  	} -	dev = phy->add_iface(phy, devname); +	if (info->attrs[IEEE802154_ATTR_HW_ADDR] && +	    nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) != +			IEEE802154_ADDR_LEN) { +		rc = -EINVAL; +		goto nla_put_failure; +	} + +	if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) { +		type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]); +		if (type >= __IEEE802154_DEV_MAX) { +			rc = -EINVAL; +			goto nla_put_failure; +		} +	} + +	dev = phy->add_iface(phy, devname, type);  	if (IS_ERR(dev)) {  		rc = PTR_ERR(dev);  		goto nla_put_failure;  	} -	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); +	if (info->attrs[IEEE802154_ATTR_HW_ADDR]) { +		struct sockaddr addr; +		addr.sa_family = ARPHRD_IEEE802154; +		nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR], +				IEEE802154_ADDR_LEN); + +		/* +		 * strangely enough, some callbacks (inetdev_event) from +		 * dev_set_mac_address require RTNL_LOCK +		 */ +		rtnl_lock(); +		rc = dev_set_mac_address(dev, &addr); +		rtnl_unlock(); +		if (rc) +			goto dev_unregister; +	} + +	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || +	    nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) +		goto nla_put_failure;  	dev_put(dev);  	wpan_phy_put(phy);  	return ieee802154_nl_reply(msg, info); +dev_unregister: +	rtnl_lock(); /* del_iface must be called with RTNL lock */ +	phy->del_iface(phy, dev); +	dev_put(dev); +	rtnl_unlock();  nla_put_failure:  	nlmsg_free(msg);  out_dev: @@ -234,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; @@ -301,10 +338,9 @@ static int ieee802154_del_iface(struct sk_buff *skb,  	rtnl_unlock(); - -	NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); -	NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, name); - +	if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || +	    nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name)) +		goto nla_put_failure;  	wpan_phy_put(phy);  	return ieee802154_nl_reply(msg, info); @@ -318,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 10970ca8574..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; @@ -106,8 +110,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,  		       size_t size)  {  	struct net_device *dev; -	unsigned mtu; +	unsigned int mtu;  	struct sk_buff *skb; +	int hlen, tlen;  	int err;  	if (msg->msg_flags & MSG_OOB) { @@ -137,12 +142,14 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,  		goto out_dev;  	} -	skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, +	hlen = LL_RESERVED_SPACE(dev); +	tlen = dev->needed_tailroom; +	skb = sock_alloc_send_skb(sk, hlen + tlen + size,  			msg->msg_flags & MSG_DONTWAIT, &err);  	if (!skb)  		goto out_dev; -	skb_reserve(skb, LL_RESERVED_SPACE(dev)); +	skb_reserve(skb, hlen);  	skb_reset_mac_header(skb);  	skb_reset_network_header(skb); @@ -206,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; @@ -218,10 +229,9 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)  void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)  {  	struct sock *sk; -	struct hlist_node *node;  	read_lock(&raw_lock); -	sk_for_each(sk, node, &raw_head) { +	sk_for_each(sk, &raw_head) {  		bh_lock_sock(sk);  		if (!sk->sk_bound_dev_if ||  		    sk->sk_bound_dev_if == dev->ifindex) { 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 1627ef2e852..8d6f6704da8 100644 --- a/net/ieee802154/wpan-class.c +++ b/net/ieee802154/wpan-class.c @@ -36,16 +36,15 @@ static ssize_t name ## _show(struct device *dev,			\  	ret = snprintf(buf, PAGE_SIZE, format_string "\n", args);	\  	mutex_unlock(&phy->pib_lock);					\  	return ret;							\ -} +}									\ +static DEVICE_ATTR_RO(name);  #define MASTER_SHOW(field, format_string)				\  	MASTER_SHOW_COMPLEX(field, format_string, phy->field)  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, @@ -66,15 +65,17 @@ static ssize_t channels_supported_show(struct device *dev,  	mutex_unlock(&phy->pib_lock);  	return len;  } - -static struct device_attribute pmib_attrs[] = { -	__ATTR_RO(current_channel), -	__ATTR_RO(current_page), -	__ATTR_RO(channels_supported), -	__ATTR_RO(transmit_power), -	__ATTR_RO(cca_mode), -	{}, +static DEVICE_ATTR_RO(channels_supported); + +static struct attribute *pmib_attrs[] = { +	&dev_attr_current_channel.attr, +	&dev_attr_current_page.attr, +	&dev_attr_channels_supported.attr, +	&dev_attr_transmit_power.attr, +	&dev_attr_cca_mode.attr, +	NULL,  }; +ATTRIBUTE_GROUPS(pmib);  static void wpan_phy_release(struct device *d)  { @@ -85,13 +86,13 @@ static void wpan_phy_release(struct device *d)  static struct class wpan_phy_class = {  	.name = "ieee802154",  	.dev_release = wpan_phy_release, -	.dev_attrs = pmib_attrs, +	.dev_groups = pmib_groups,  };  static DEFINE_MUTEX(wpan_phy_mutex);  static int wpan_phy_idx; -static int wpan_phy_match(struct device *dev, void *data) +static int wpan_phy_match(struct device *dev, const void *data)  {  	return !strcmp(dev_name(dev), (const char *)data);  } @@ -103,8 +104,7 @@ struct wpan_phy *wpan_phy_find(const char *str)  	if (WARN_ON(!str))  		return NULL; -	dev = class_find_device(&wpan_phy_class, NULL, -			(void *)str, wpan_phy_match); +	dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);  	if (!dev)  		return NULL;  | 
