 bce83697c5
			
		
	
	
	bce83697c5
	
	
	
		
			
			Instead of the home-grown d_fnstart(), d_fnend() and d_printf() macros, use dev_dbg() or remove the message entirely. Signed-off-by: David Vrabel <david.vrabel@csr.com>
		
			
				
	
	
		
			1781 lines
		
	
	
	
		
			54 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1781 lines
		
	
	
	
		
			54 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * WiMedia Logical Link Control Protocol (WLP)
 | |
|  * Message construction and parsing
 | |
|  *
 | |
|  * Copyright (C) 2007 Intel Corporation
 | |
|  * Reinette Chatre <reinette.chatre@intel.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.
 | |
|  *
 | |
|  *
 | |
|  * FIXME: docs
 | |
|  */
 | |
| 
 | |
| #include <linux/wlp.h>
 | |
| 
 | |
| #include "wlp-internal.h"
 | |
| 
 | |
| static
 | |
| const char *__wlp_assoc_frame[] = {
 | |
| 	[WLP_ASSOC_D1] = "WLP_ASSOC_D1",
 | |
| 	[WLP_ASSOC_D2] = "WLP_ASSOC_D2",
 | |
| 	[WLP_ASSOC_M1] = "WLP_ASSOC_M1",
 | |
| 	[WLP_ASSOC_M2] = "WLP_ASSOC_M2",
 | |
| 	[WLP_ASSOC_M3] = "WLP_ASSOC_M3",
 | |
| 	[WLP_ASSOC_M4] = "WLP_ASSOC_M4",
 | |
| 	[WLP_ASSOC_M5] = "WLP_ASSOC_M5",
 | |
| 	[WLP_ASSOC_M6] = "WLP_ASSOC_M6",
 | |
| 	[WLP_ASSOC_M7] = "WLP_ASSOC_M7",
 | |
| 	[WLP_ASSOC_M8] = "WLP_ASSOC_M8",
 | |
| 	[WLP_ASSOC_F0] = "WLP_ASSOC_F0",
 | |
| 	[WLP_ASSOC_E1] = "WLP_ASSOC_E1",
 | |
| 	[WLP_ASSOC_E2] = "WLP_ASSOC_E2",
 | |
| 	[WLP_ASSOC_C1] = "WLP_ASSOC_C1",
 | |
| 	[WLP_ASSOC_C2] = "WLP_ASSOC_C2",
 | |
| 	[WLP_ASSOC_C3] = "WLP_ASSOC_C3",
 | |
| 	[WLP_ASSOC_C4] = "WLP_ASSOC_C4",
 | |
| };
 | |
| 
 | |
| static const char *wlp_assoc_frame_str(unsigned id)
 | |
| {
 | |
| 	if (id >= ARRAY_SIZE(__wlp_assoc_frame))
 | |
| 		return "unknown association frame";
 | |
| 	return __wlp_assoc_frame[id];
 | |
| }
 | |
| 
 | |
| static const char *__wlp_assc_error[] = {
 | |
| 	"none",
 | |
| 	"Authenticator Failure",
 | |
| 	"Rogue activity suspected",
 | |
| 	"Device busy",
 | |
| 	"Setup Locked",
 | |
| 	"Registrar not ready",
 | |
| 	"Invalid WSS selection",
 | |
| 	"Message timeout",
 | |
| 	"Enrollment session timeout",
 | |
| 	"Device password invalid",
 | |
| 	"Unsupported version",
 | |
| 	"Internal error",
 | |
| 	"Undefined error",
 | |
| 	"Numeric comparison failure",
 | |
| 	"Waiting for user input",
 | |
| };
 | |
| 
 | |
| static const char *wlp_assc_error_str(unsigned id)
 | |
| {
 | |
| 	if (id >= ARRAY_SIZE(__wlp_assc_error))
 | |
| 		return "unknown WLP association error";
 | |
| 	return __wlp_assc_error[id];
 | |
| }
 | |
| 
 | |
| static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type,
 | |
| 				    size_t len)
 | |
| {
 | |
| 	hdr->type = cpu_to_le16(type);
 | |
| 	hdr->length = cpu_to_le16(len);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Populate fields of a constant sized attribute
 | |
|  *
 | |
|  * @returns: total size of attribute including size of new value
 | |
|  *
 | |
|  * We have two instances of this function (wlp_pset and wlp_set): one takes
 | |
|  * the value as a parameter, the other takes a pointer to the value as
 | |
|  * parameter. They thus only differ in how the value is assigned to the
 | |
|  * attribute.
 | |
|  *
 | |
|  * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of
 | |
|  * sizeof(type) to be able to use this same code for the structures that
 | |
|  * contain 8bit enum values and be able to deal with pointer types.
 | |
|  */
 | |
| #define wlp_set(type, type_code, name)					\
 | |
| static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value)	\
 | |
| {									\
 | |
| 	wlp_set_attr_hdr(&attr->hdr, type_code,				\
 | |
| 			 sizeof(*attr) - sizeof(struct wlp_attr_hdr));	\
 | |
| 	attr->name = value;						\
 | |
| 	return sizeof(*attr);						\
 | |
| }
 | |
| 
 | |
| #define wlp_pset(type, type_code, name)					\
 | |
| static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value)	\
 | |
| {									\
 | |
| 	wlp_set_attr_hdr(&attr->hdr, type_code,				\
 | |
| 			 sizeof(*attr) - sizeof(struct wlp_attr_hdr));	\
 | |
| 	attr->name = *value;						\
 | |
| 	return sizeof(*attr);						\
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Populate fields of a variable attribute
 | |
|  *
 | |
|  * @returns: total size of attribute including size of new value
 | |
|  *
 | |
|  * Provided with a pointer to the memory area reserved for the
 | |
|  * attribute structure, the field is populated with the value. The
 | |
|  * reserved memory has to contain enough space for the value.
 | |
|  */
 | |
| #define wlp_vset(type, type_code, name)					\
 | |
| static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value,	\
 | |
| 				size_t len)				\
 | |
| {									\
 | |
| 	wlp_set_attr_hdr(&attr->hdr, type_code, len);			\
 | |
| 	memcpy(attr->name, value, len);					\
 | |
| 	return sizeof(*attr) + len;					\
 | |
| }
 | |
| 
 | |
| wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name)
 | |
| wlp_vset(char *, WLP_ATTR_MANUF, manufacturer)
 | |
| wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type)
 | |
| wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name)
 | |
| wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr)
 | |
| wlp_vset(char *, WLP_ATTR_SERIAL, serial)
 | |
| wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name)
 | |
| wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e)
 | |
| wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r)
 | |
| wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid)
 | |
| wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
 | |
| /*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/
 | |
| wlp_set(u8, WLP_ATTR_WLP_VER, version)
 | |
| wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
 | |
| wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
 | |
| wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
 | |
| wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
 | |
| wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast)
 | |
| wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce)
 | |
| wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce)
 | |
| wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag)
 | |
| wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt)
 | |
| 
 | |
| /**
 | |
|  * Fill in the WSS information attributes
 | |
|  *
 | |
|  * We currently only support one WSS, and this is assumed in this function
 | |
|  * that can populate only one WSS information attribute.
 | |
|  */
 | |
| static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr,
 | |
| 			       struct wlp_wss *wss)
 | |
| {
 | |
| 	size_t datalen;
 | |
| 	void *ptr = attr->wss_info;
 | |
| 	size_t used = sizeof(*attr);
 | |
| 
 | |
| 	datalen = sizeof(struct wlp_wss_info) + strlen(wss->name);
 | |
| 	wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen);
 | |
| 	used = wlp_set_wssid(ptr, &wss->wssid);
 | |
| 	used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name));
 | |
| 	used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll);
 | |
| 	used += wlp_set_wss_sec_status(ptr + used, wss->secure_status);
 | |
| 	used += wlp_set_wss_bcast(ptr + used, &wss->bcast);
 | |
| 	return sizeof(*attr) + used;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Verify attribute header
 | |
|  *
 | |
|  * @hdr:     Pointer to attribute header that will be verified.
 | |
|  * @type:    Expected attribute type.
 | |
|  * @len:     Expected length of attribute value (excluding header).
 | |
|  *
 | |
|  * Most attribute values have a known length even when they do have a
 | |
|  * length field. This knowledge can be used via this function to verify
 | |
|  * that the length field matches the expected value.
 | |
|  */
 | |
| static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr,
 | |
| 		       enum wlp_attr_type type, unsigned len)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 
 | |
| 	if (le16_to_cpu(hdr->type) != type) {
 | |
| 		dev_err(dev, "WLP: unexpected header type. Expected "
 | |
| 			"%u, got %u.\n", type, le16_to_cpu(hdr->type));
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	if (le16_to_cpu(hdr->length) != len) {
 | |
| 		dev_err(dev, "WLP: unexpected length in header. Expected "
 | |
| 			"%u, got %u.\n", len, le16_to_cpu(hdr->length));
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check if header of WSS information attribute valid
 | |
|  *
 | |
|  * @returns: length of WSS attributes (value of length attribute field) if
 | |
|  *             valid WSS information attribute found
 | |
|  *           -ENODATA if no WSS information attribute found
 | |
|  *           -EIO other error occured
 | |
|  *
 | |
|  * The WSS information attribute is optional. The function will be provided
 | |
|  * with a pointer to data that could _potentially_ be a WSS information
 | |
|  * attribute. If a valid WSS information attribute is found it will return
 | |
|  * 0, if no WSS information attribute is found it will return -ENODATA, and
 | |
|  * another error will be returned if it is a WSS information attribute, but
 | |
|  * some parsing failure occured.
 | |
|  */
 | |
| static int wlp_check_wss_info_attr_hdr(struct wlp *wlp,
 | |
| 				       struct wlp_attr_hdr *hdr, size_t buflen)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	size_t len;
 | |
| 	int result = 0;
 | |
| 
 | |
| 	if (buflen < sizeof(*hdr)) {
 | |
| 		dev_err(dev, "WLP: Not enough space in buffer to parse"
 | |
| 			" WSS information attribute header.\n");
 | |
| 		result = -EIO;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) {
 | |
| 		/* WSS information is optional */
 | |
| 		result = -ENODATA;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	len = le16_to_cpu(hdr->length);
 | |
| 	if (buflen < sizeof(*hdr) + len) {
 | |
| 		dev_err(dev, "WLP: Not enough space in buffer to parse "
 | |
| 			"variable data. Got %d, expected %d.\n",
 | |
| 			(int)buflen, (int)(sizeof(*hdr) + len));
 | |
| 		result = -EIO;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	result = len;
 | |
| out:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Get value of attribute from fixed size attribute field.
 | |
|  *
 | |
|  * @attr:    Pointer to attribute field.
 | |
|  * @value:   Pointer to variable in which attribute value will be placed.
 | |
|  * @buflen:  Size of buffer in which attribute field (including header)
 | |
|  *           can be found.
 | |
|  * @returns: Amount of given buffer consumed by parsing for this attribute.
 | |
|  *
 | |
|  * The size and type of the value is known by the type of the attribute.
 | |
|  */
 | |
| #define wlp_get(type, type_code, name)					\
 | |
| ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr,	\
 | |
| 		      type *value, ssize_t buflen)			\
 | |
| {									\
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;			\
 | |
| 	if (buflen < 0)							\
 | |
| 		return -EINVAL;						\
 | |
| 	if (buflen < sizeof(*attr)) {					\
 | |
| 		dev_err(dev, "WLP: Not enough space in buffer to parse"	\
 | |
| 			" attribute field. Need %d, received %zu\n",	\
 | |
| 			(int)sizeof(*attr), buflen);			\
 | |
| 		return -EIO;						\
 | |
| 	}								\
 | |
| 	if (wlp_check_attr_hdr(wlp, &attr->hdr, type_code,		\
 | |
| 			       sizeof(attr->name)) < 0) {		\
 | |
| 		dev_err(dev, "WLP: Header verification failed. \n");	\
 | |
| 		return -EINVAL;						\
 | |
| 	}								\
 | |
| 	*value = attr->name;						\
 | |
| 	return sizeof(*attr);						\
 | |
| }
 | |
| 
 | |
| #define wlp_get_sparse(type, type_code, name) \
 | |
| 	static wlp_get(type, type_code, name)
 | |
| 
 | |
| /**
 | |
|  * Get value of attribute from variable sized attribute field.
 | |
|  *
 | |
|  * @max:     The maximum size of this attribute. This value is dictated by
 | |
|  *           the maximum value from the WLP specification.
 | |
|  *
 | |
|  * @attr:    Pointer to attribute field.
 | |
|  * @value:   Pointer to variable that will contain the value. The memory
 | |
|  *           must already have been allocated for this value.
 | |
|  * @buflen:  Size of buffer in which attribute field (including header)
 | |
|  *           can be found.
 | |
|  * @returns: Amount of given bufferconsumed by parsing for this attribute.
 | |
|  */
 | |
| #define wlp_vget(type_val, type_code, name, max)			\
 | |
| static ssize_t wlp_get_##name(struct wlp *wlp,				\
 | |
| 			      struct wlp_attr_##name *attr,		\
 | |
| 			      type_val *value, ssize_t buflen)		\
 | |
| {									\
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;			\
 | |
| 	size_t len;							\
 | |
| 	if (buflen < 0)							\
 | |
| 		return -EINVAL;						\
 | |
| 	if (buflen < sizeof(*attr)) {					\
 | |
| 		dev_err(dev, "WLP: Not enough space in buffer to parse"	\
 | |
| 			" header.\n");					\
 | |
| 		return -EIO;						\
 | |
| 	}								\
 | |
| 	if (le16_to_cpu(attr->hdr.type) != type_code) {			\
 | |
| 		dev_err(dev, "WLP: Unexpected attribute type. Got %u, "	\
 | |
| 			"expected %u.\n", le16_to_cpu(attr->hdr.type),	\
 | |
| 			type_code);					\
 | |
| 		return -EINVAL;						\
 | |
| 	}								\
 | |
| 	len = le16_to_cpu(attr->hdr.length);				\
 | |
| 	if (len > max) {						\
 | |
| 		dev_err(dev, "WLP: Attribute larger than maximum "	\
 | |
| 			"allowed. Received %zu, max is %d.\n", len,	\
 | |
| 			(int)max);					\
 | |
| 		return -EFBIG;						\
 | |
| 	}								\
 | |
| 	if (buflen < sizeof(*attr) + len) {				\
 | |
| 		dev_err(dev, "WLP: Not enough space in buffer to parse "\
 | |
| 			"variable data.\n");				\
 | |
| 		return -EIO;						\
 | |
| 	}								\
 | |
| 	memcpy(value, (void *) attr + sizeof(*attr), len);		\
 | |
| 	return sizeof(*attr) + len;					\
 | |
| }
 | |
| 
 | |
| wlp_get(u8, WLP_ATTR_WLP_VER, version)
 | |
| wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
 | |
| wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
 | |
| wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
 | |
| wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e)
 | |
| wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r)
 | |
| wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid)
 | |
| wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
 | |
| wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
 | |
| wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast)
 | |
| wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag)
 | |
| wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt)
 | |
| wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce)
 | |
| wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce)
 | |
| 
 | |
| /* The buffers for the device info attributes can be found in the
 | |
|  * wlp_device_info struct. These buffers contain one byte more than the
 | |
|  * max allowed by the spec - this is done to be able to add the
 | |
|  * terminating \0 for user display. This terminating byte is not required
 | |
|  * in the actual attribute field (because it has a length field) so the
 | |
|  * maximum allowed for this value is one less than its size in the
 | |
|  * structure.
 | |
|  */
 | |
| wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name,
 | |
| 	 FIELD_SIZEOF(struct wlp_wss, name) - 1)
 | |
| wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name,
 | |
| 	 FIELD_SIZEOF(struct wlp_device_info, name) - 1)
 | |
| wlp_vget(char, WLP_ATTR_MANUF, manufacturer,
 | |
| 	 FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1)
 | |
| wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name,
 | |
| 	 FIELD_SIZEOF(struct wlp_device_info, model_name) - 1)
 | |
| wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr,
 | |
| 	 FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1)
 | |
| wlp_vget(char, WLP_ATTR_SERIAL, serial,
 | |
| 	 FIELD_SIZEOF(struct wlp_device_info, serial) - 1)
 | |
| 
 | |
| /**
 | |
|  * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info
 | |
|  *
 | |
|  * @attr: pointer to WSS name attribute in WSS information attribute field
 | |
|  * @info: structure that will be populated with data from WSS information
 | |
|  *        field (WSS name, Accept enroll, secure status, broadcast address)
 | |
|  * @buflen: size of buffer
 | |
|  *
 | |
|  * Although the WSSID attribute forms part of the WSS info attribute it is
 | |
|  * retrieved separately and stored in a different location.
 | |
|  */
 | |
| static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp,
 | |
| 				      struct wlp_attr_hdr *attr,
 | |
| 				      struct wlp_wss_tmp_info *info,
 | |
| 				      ssize_t buflen)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	void *ptr = attr;
 | |
| 	size_t used = 0;
 | |
| 	ssize_t result = -EINVAL;
 | |
| 
 | |
| 	result = wlp_get_wss_name(wlp, ptr, info->name, buflen);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS name from "
 | |
| 			"WSS info in D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 
 | |
| 	result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll,
 | |
| 				     buflen - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain accepting "
 | |
| 			"enrollment from WSS info in D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (info->accept_enroll != 0 && info->accept_enroll != 1) {
 | |
| 		dev_err(dev, "WLP: invalid value for accepting "
 | |
| 			"enrollment in D2 message.\n");
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 
 | |
| 	result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status,
 | |
| 					buflen - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain secure "
 | |
| 			"status from WSS info in D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (info->sec_status != 0 && info->sec_status != 1) {
 | |
| 		dev_err(dev, "WLP: invalid value for secure "
 | |
| 			"status in D2 message.\n");
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 
 | |
| 	result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast,
 | |
| 				   buflen - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain broadcast "
 | |
| 			"address from WSS info in D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = used;
 | |
| error_parse:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create a new WSSID entry for the neighbor, allocate temporary storage
 | |
|  *
 | |
|  * Each neighbor can have many WSS active. We maintain a list of WSSIDs
 | |
|  * advertised by neighbor. During discovery we also cache information about
 | |
|  * these WSS in temporary storage.
 | |
|  *
 | |
|  * The temporary storage will be removed after it has been used (eg.
 | |
|  * displayed to user), the wssid element will be removed from the list when
 | |
|  * the neighbor is rediscovered or when it disappears.
 | |
|  */
 | |
| static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp,
 | |
| 					      struct wlp_neighbor_e *neighbor)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_wssid_e *wssid_e;
 | |
| 
 | |
| 	wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL);
 | |
| 	if (wssid_e == NULL) {
 | |
| 		dev_err(dev, "WLP: unable to allocate memory "
 | |
| 			"for WSS information.\n");
 | |
| 		goto error_alloc;
 | |
| 	}
 | |
| 	wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL);
 | |
| 	if (wssid_e->info == NULL) {
 | |
| 		dev_err(dev, "WLP: unable to allocate memory "
 | |
| 			"for temporary WSS information.\n");
 | |
| 		kfree(wssid_e);
 | |
| 		wssid_e = NULL;
 | |
| 		goto error_alloc;
 | |
| 	}
 | |
| 	list_add(&wssid_e->node, &neighbor->wssid);
 | |
| error_alloc:
 | |
| 	return wssid_e;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse WSS information attribute
 | |
|  *
 | |
|  * @attr: pointer to WSS information attribute header
 | |
|  * @buflen: size of buffer in which WSS information attribute appears
 | |
|  * @wssid: will place wssid from WSS info attribute in this location
 | |
|  * @wss_info: will place other information from WSS information attribute
 | |
|  * in this location
 | |
|  *
 | |
|  * memory for @wssid and @wss_info must be allocated when calling this
 | |
|  */
 | |
| static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr,
 | |
| 				size_t buflen, struct wlp_uuid *wssid,
 | |
| 				struct wlp_wss_tmp_info *wss_info)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	ssize_t result;
 | |
| 	size_t len;
 | |
| 	size_t used = 0;
 | |
| 	void *ptr;
 | |
| 
 | |
| 	result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr,
 | |
| 					     buflen);
 | |
| 	if (result < 0)
 | |
| 		goto out;
 | |
| 	len = result;
 | |
| 	used = sizeof(*attr);
 | |
| 	ptr = attr;
 | |
| 
 | |
| 	result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info,
 | |
| 					buflen - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS information "
 | |
| 			"from WSS information attributes. \n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	if (len + sizeof(*attr) != used) {
 | |
| 		dev_err(dev, "WLP: Amount of data parsed does not "
 | |
| 			"match length field. Parsed %zu, length "
 | |
| 			"field %zu. \n", used, len);
 | |
| 		result = -EINVAL;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	result = used;
 | |
| out:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Retrieve WSS info from association frame
 | |
|  *
 | |
|  * @attr:     pointer to WSS information attribute
 | |
|  * @neighbor: ptr to neighbor being discovered, NULL if enrollment in
 | |
|  *            progress
 | |
|  * @wss:      ptr to WSS being enrolled in, NULL if discovery in progress
 | |
|  * @buflen:   size of buffer in which WSS information appears
 | |
|  *
 | |
|  * The WSS information attribute appears in the D2 association message.
 | |
|  * This message is used in two ways: to discover all neighbors or to enroll
 | |
|  * into a WSS activated by a neighbor. During discovery we only want to
 | |
|  * store the WSS info in a cache, to be deleted right after it has been
 | |
|  * used (eg. displayed to the user). During enrollment we store the WSS
 | |
|  * information for the lifetime of enrollment.
 | |
|  *
 | |
|  * During discovery we are interested in all WSS information, during
 | |
|  * enrollment we are only interested in the WSS being enrolled in. Even so,
 | |
|  * when in enrollment we keep parsing the message after finding the WSS of
 | |
|  * interest, this simplifies the calling routine in that it can be sure
 | |
|  * that all WSS information attributes have been parsed out of the message.
 | |
|  *
 | |
|  * Association frame is process with nbmutex held. The list access is safe.
 | |
|  */
 | |
| static ssize_t wlp_get_all_wss_info(struct wlp *wlp,
 | |
| 				    struct wlp_attr_wss_info *attr,
 | |
| 				    struct wlp_neighbor_e *neighbor,
 | |
| 				    struct wlp_wss *wss, ssize_t buflen)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	size_t used = 0;
 | |
| 	ssize_t result = -EINVAL;
 | |
| 	struct wlp_attr_wss_info *cur;
 | |
| 	struct wlp_uuid wssid;
 | |
| 	struct wlp_wss_tmp_info wss_info;
 | |
| 	unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */
 | |
| 	struct wlp_wssid_e *wssid_e;
 | |
| 	char buf[WLP_WSS_UUID_STRSIZE];
 | |
| 
 | |
| 	if (buflen < 0)
 | |
| 		goto out;
 | |
| 
 | |
| 	if (neighbor != NULL && wss == NULL)
 | |
| 		enroll = 0; /* discovery */
 | |
| 	else if (wss != NULL && neighbor == NULL)
 | |
| 		enroll = 1; /* enrollment */
 | |
| 	else
 | |
| 		goto out;
 | |
| 
 | |
| 	cur = attr;
 | |
| 	while (buflen - used > 0) {
 | |
| 		memset(&wss_info, 0, sizeof(wss_info));
 | |
| 		cur = (void *)cur + used;
 | |
| 		result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid,
 | |
| 					  &wss_info);
 | |
| 		if (result == -ENODATA) {
 | |
| 			result = used;
 | |
| 			goto out;
 | |
| 		} else if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to parse WSS information "
 | |
| 				"from WSS information attribute. \n");
 | |
| 			result = -EINVAL;
 | |
| 			goto error_parse;
 | |
| 		}
 | |
| 		if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
 | |
| 			if (wss_info.accept_enroll != 1) {
 | |
| 				dev_err(dev, "WLP: Requested WSS does "
 | |
| 					"not accept enrollment.\n");
 | |
| 				result = -EINVAL;
 | |
| 				goto out;
 | |
| 			}
 | |
| 			memcpy(wss->name, wss_info.name, sizeof(wss->name));
 | |
| 			wss->bcast = wss_info.bcast;
 | |
| 			wss->secure_status = wss_info.sec_status;
 | |
| 			wss->accept_enroll = wss_info.accept_enroll;
 | |
| 			wss->state = WLP_WSS_STATE_PART_ENROLLED;
 | |
| 			wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
 | |
| 			dev_dbg(dev, "WLP: Found WSS %s. Enrolling.\n", buf);
 | |
| 		} else {
 | |
| 			wssid_e = wlp_create_wssid_e(wlp, neighbor);
 | |
| 			if (wssid_e == NULL) {
 | |
| 				dev_err(dev, "WLP: Cannot create new WSSID "
 | |
| 					"entry for neighbor %02x:%02x.\n",
 | |
| 					neighbor->uwb_dev->dev_addr.data[1],
 | |
| 					neighbor->uwb_dev->dev_addr.data[0]);
 | |
| 				result = -ENOMEM;
 | |
| 				goto out;
 | |
| 			}
 | |
| 			wssid_e->wssid = wssid;
 | |
| 			*wssid_e->info = wss_info;
 | |
| 		}
 | |
| 		used += result;
 | |
| 	}
 | |
| 	result = used;
 | |
| error_parse:
 | |
| 	if (result < 0 && !enroll) /* this was a discovery */
 | |
| 		wlp_remove_neighbor_tmp_info(neighbor);
 | |
| out:
 | |
| 	return result;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse WSS information attributes into cache for discovery
 | |
|  *
 | |
|  * @attr: the first WSS information attribute in message
 | |
|  * @neighbor: the neighbor whose cache will be populated
 | |
|  * @buflen: size of the input buffer
 | |
|  */
 | |
| static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp,
 | |
| 					 struct wlp_attr_wss_info *attr,
 | |
| 					 struct wlp_neighbor_e *neighbor,
 | |
| 					 ssize_t buflen)
 | |
| {
 | |
| 	return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse WSS information attributes into WSS struct for enrollment
 | |
|  *
 | |
|  * @attr: the first WSS information attribute in message
 | |
|  * @wss: the WSS that will be enrolled
 | |
|  * @buflen: size of the input buffer
 | |
|  */
 | |
| static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp,
 | |
| 					  struct wlp_attr_wss_info *attr,
 | |
| 					  struct wlp_wss *wss, ssize_t buflen)
 | |
| {
 | |
| 	return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Construct a D1 association frame
 | |
|  *
 | |
|  * We use the radio control functions to determine the values of the device
 | |
|  * properties. These are of variable length and the total space needed is
 | |
|  * tallied first before we start constructing the message. The radio
 | |
|  * control functions return strings that are terminated with \0. This
 | |
|  * character should not be included in the message (there is a length field
 | |
|  * accompanying it in the attribute).
 | |
|  */
 | |
| static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 			      struct sk_buff **skb)
 | |
| {
 | |
| 
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result = 0;
 | |
| 	struct wlp_device_info *info;
 | |
| 	size_t used = 0;
 | |
| 	struct wlp_frame_assoc *_d1;
 | |
| 	struct sk_buff *_skb;
 | |
| 	void *d1_itr;
 | |
| 
 | |
| 	if (wlp->dev_info == NULL) {
 | |
| 		result = __wlp_setup_device_info(wlp);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to setup device "
 | |
| 				"information for D1 message.\n");
 | |
| 			goto error;
 | |
| 		}
 | |
| 	}
 | |
| 	info = wlp->dev_info;
 | |
| 	_skb = dev_alloc_skb(sizeof(*_d1)
 | |
| 		      + sizeof(struct wlp_attr_uuid_e)
 | |
| 		      + sizeof(struct wlp_attr_wss_sel_mthd)
 | |
| 		      + sizeof(struct wlp_attr_dev_name)
 | |
| 		      + strlen(info->name)
 | |
| 		      + sizeof(struct wlp_attr_manufacturer)
 | |
| 		      + strlen(info->manufacturer)
 | |
| 		      + sizeof(struct wlp_attr_model_name)
 | |
| 		      + strlen(info->model_name)
 | |
| 		      + sizeof(struct wlp_attr_model_nr)
 | |
| 		      + strlen(info->model_nr)
 | |
| 		      + sizeof(struct wlp_attr_serial)
 | |
| 		      + strlen(info->serial)
 | |
| 		      + sizeof(struct wlp_attr_prim_dev_type)
 | |
| 		      + sizeof(struct wlp_attr_wlp_assc_err));
 | |
| 	if (_skb == NULL) {
 | |
| 		dev_err(dev, "WLP: Cannot allocate memory for association "
 | |
| 			"message.\n");
 | |
| 		result = -ENOMEM;
 | |
| 		goto error;
 | |
| 	}
 | |
| 	_d1 = (void *) _skb->data;
 | |
| 	_d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
 | |
| 	_d1->hdr.type = WLP_FRAME_ASSOCIATION;
 | |
| 	_d1->type = WLP_ASSOC_D1;
 | |
| 
 | |
| 	wlp_set_version(&_d1->version, WLP_VERSION);
 | |
| 	wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1);
 | |
| 	d1_itr = _d1->attr;
 | |
| 	used = wlp_set_uuid_e(d1_itr, &wlp->uuid);
 | |
| 	used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT);
 | |
| 	used += wlp_set_dev_name(d1_itr + used, info->name,
 | |
| 				 strlen(info->name));
 | |
| 	used += wlp_set_manufacturer(d1_itr + used, info->manufacturer,
 | |
| 				     strlen(info->manufacturer));
 | |
| 	used += wlp_set_model_name(d1_itr + used, info->model_name,
 | |
| 				   strlen(info->model_name));
 | |
| 	used += wlp_set_model_nr(d1_itr + used, info->model_nr,
 | |
| 				 strlen(info->model_nr));
 | |
| 	used += wlp_set_serial(d1_itr + used, info->serial,
 | |
| 			       strlen(info->serial));
 | |
| 	used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type);
 | |
| 	used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE);
 | |
| 	skb_put(_skb, sizeof(*_d1) + used);
 | |
| 	*skb = _skb;
 | |
| error:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Construct a D2 association frame
 | |
|  *
 | |
|  * We use the radio control functions to determine the values of the device
 | |
|  * properties. These are of variable length and the total space needed is
 | |
|  * tallied first before we start constructing the message. The radio
 | |
|  * control functions return strings that are terminated with \0. This
 | |
|  * character should not be included in the message (there is a length field
 | |
|  * accompanying it in the attribute).
 | |
|  */
 | |
| static
 | |
| int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 		       struct sk_buff **skb, struct wlp_uuid *uuid_e)
 | |
| {
 | |
| 
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result = 0;
 | |
| 	struct wlp_device_info *info;
 | |
| 	size_t used = 0;
 | |
| 	struct wlp_frame_assoc *_d2;
 | |
| 	struct sk_buff *_skb;
 | |
| 	void *d2_itr;
 | |
| 	size_t mem_needed;
 | |
| 
 | |
| 	if (wlp->dev_info == NULL) {
 | |
| 		result = __wlp_setup_device_info(wlp);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to setup device "
 | |
| 				"information for D2 message.\n");
 | |
| 			goto error;
 | |
| 		}
 | |
| 	}
 | |
| 	info = wlp->dev_info;
 | |
| 	mem_needed = sizeof(*_d2)
 | |
| 		      + sizeof(struct wlp_attr_uuid_e)
 | |
| 		      + sizeof(struct wlp_attr_uuid_r)
 | |
| 		      + sizeof(struct wlp_attr_dev_name)
 | |
| 		      + strlen(info->name)
 | |
| 		      + sizeof(struct wlp_attr_manufacturer)
 | |
| 		      + strlen(info->manufacturer)
 | |
| 		      + sizeof(struct wlp_attr_model_name)
 | |
| 		      + strlen(info->model_name)
 | |
| 		      + sizeof(struct wlp_attr_model_nr)
 | |
| 		      + strlen(info->model_nr)
 | |
| 		      + sizeof(struct wlp_attr_serial)
 | |
| 		      + strlen(info->serial)
 | |
| 		      + sizeof(struct wlp_attr_prim_dev_type)
 | |
| 		      + sizeof(struct wlp_attr_wlp_assc_err);
 | |
| 	if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
 | |
| 		mem_needed += sizeof(struct wlp_attr_wss_info)
 | |
| 			      + sizeof(struct wlp_wss_info)
 | |
| 			      + strlen(wlp->wss.name);
 | |
| 	_skb = dev_alloc_skb(mem_needed);
 | |
| 	if (_skb == NULL) {
 | |
| 		dev_err(dev, "WLP: Cannot allocate memory for association "
 | |
| 			"message.\n");
 | |
| 		result = -ENOMEM;
 | |
| 		goto error;
 | |
| 	}
 | |
| 	_d2 = (void *) _skb->data;
 | |
| 	_d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
 | |
| 	_d2->hdr.type = WLP_FRAME_ASSOCIATION;
 | |
| 	_d2->type = WLP_ASSOC_D2;
 | |
| 
 | |
| 	wlp_set_version(&_d2->version, WLP_VERSION);
 | |
| 	wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2);
 | |
| 	d2_itr = _d2->attr;
 | |
| 	used = wlp_set_uuid_e(d2_itr, uuid_e);
 | |
| 	used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid);
 | |
| 	if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
 | |
| 		used += wlp_set_wss_info(d2_itr + used, &wlp->wss);
 | |
| 	used += wlp_set_dev_name(d2_itr + used, info->name,
 | |
| 				 strlen(info->name));
 | |
| 	used += wlp_set_manufacturer(d2_itr + used, info->manufacturer,
 | |
| 				     strlen(info->manufacturer));
 | |
| 	used += wlp_set_model_name(d2_itr + used, info->model_name,
 | |
| 				   strlen(info->model_name));
 | |
| 	used += wlp_set_model_nr(d2_itr + used, info->model_nr,
 | |
| 				 strlen(info->model_nr));
 | |
| 	used += wlp_set_serial(d2_itr + used, info->serial,
 | |
| 			       strlen(info->serial));
 | |
| 	used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type);
 | |
| 	used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE);
 | |
| 	skb_put(_skb, sizeof(*_d2) + used);
 | |
| 	*skb = _skb;
 | |
| error:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Allocate memory for and populate fields of F0 association frame
 | |
|  *
 | |
|  * Currently (while focusing on unsecure enrollment) we ignore the
 | |
|  * nonce's that could be placed in the message. Only the error field is
 | |
|  * populated by the value provided by the caller.
 | |
|  */
 | |
| static
 | |
| int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb,
 | |
| 		       enum wlp_assc_error error)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result = -ENOMEM;
 | |
| 	struct {
 | |
| 		struct wlp_frame_assoc f0_hdr;
 | |
| 		struct wlp_attr_enonce enonce;
 | |
| 		struct wlp_attr_rnonce rnonce;
 | |
| 		struct wlp_attr_wlp_assc_err assc_err;
 | |
| 	} *f0;
 | |
| 	struct sk_buff *_skb;
 | |
| 	struct wlp_nonce tmp;
 | |
| 
 | |
| 	_skb = dev_alloc_skb(sizeof(*f0));
 | |
| 	if (_skb == NULL) {
 | |
| 		dev_err(dev, "WLP: Unable to allocate memory for F0 "
 | |
| 			"association frame. \n");
 | |
| 		goto error_alloc;
 | |
| 	}
 | |
| 	f0 = (void *) _skb->data;
 | |
| 	f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
 | |
| 	f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
 | |
| 	f0->f0_hdr.type = WLP_ASSOC_F0;
 | |
| 	wlp_set_version(&f0->f0_hdr.version, WLP_VERSION);
 | |
| 	wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0);
 | |
| 	memset(&tmp, 0, sizeof(tmp));
 | |
| 	wlp_set_enonce(&f0->enonce, &tmp);
 | |
| 	wlp_set_rnonce(&f0->rnonce, &tmp);
 | |
| 	wlp_set_wlp_assc_err(&f0->assc_err, error);
 | |
| 	skb_put(_skb, sizeof(*f0));
 | |
| 	*skb = _skb;
 | |
| 	result = 0;
 | |
| error_alloc:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse F0 frame
 | |
|  *
 | |
|  * We just retrieve the values and print it as an error to the user.
 | |
|  * Calling function already knows an error occured (F0 indicates error), so
 | |
|  * we just parse the content as debug for higher layers.
 | |
|  */
 | |
| int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_frame_assoc *f0 = (void *) skb->data;
 | |
| 	void *ptr = skb->data;
 | |
| 	size_t len = skb->len;
 | |
| 	size_t used;
 | |
| 	ssize_t result;
 | |
| 	struct wlp_nonce enonce, rnonce;
 | |
| 	enum wlp_assc_error assc_err;
 | |
| 	char enonce_buf[WLP_WSS_NONCE_STRSIZE];
 | |
| 	char rnonce_buf[WLP_WSS_NONCE_STRSIZE];
 | |
| 
 | |
| 	used = sizeof(*f0);
 | |
| 	result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Enrollee nonce "
 | |
| 			"attribute from F0 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Registrar nonce "
 | |
| 			"attribute from F0 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WLP Association error "
 | |
| 			"attribute from F0 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce);
 | |
| 	wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce);
 | |
| 	dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee "
 | |
| 		"nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n",
 | |
| 		enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err));
 | |
| 	result = 0;
 | |
| error_parse:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Retrieve variable device information from association message
 | |
|  *
 | |
|  * The device information parsed is not required in any message. This
 | |
|  * routine will thus not fail if an attribute is not present.
 | |
|  * The attributes are expected in a certain order, even if all are not
 | |
|  * present. The "attribute type" value is used to ensure the attributes
 | |
|  * are parsed in the correct order.
 | |
|  *
 | |
|  * If an error is encountered during parsing the function will return an
 | |
|  * error code, when this happens the given device_info structure may be
 | |
|  * partially filled.
 | |
|  */
 | |
| static
 | |
| int wlp_get_variable_info(struct wlp *wlp, void *data,
 | |
| 			  struct wlp_device_info *dev_info, ssize_t len)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	size_t used = 0;
 | |
| 	struct wlp_attr_hdr *hdr;
 | |
| 	ssize_t result = 0;
 | |
| 	unsigned last = 0;
 | |
| 
 | |
| 	while (len - used > 0) {
 | |
| 		if (len - used < sizeof(*hdr)) {
 | |
| 			dev_err(dev, "WLP: Partial data in frame, cannot "
 | |
| 				"parse. \n");
 | |
| 			goto error_parse;
 | |
| 		}
 | |
| 		hdr = data + used;
 | |
| 		switch (le16_to_cpu(hdr->type)) {
 | |
| 		case WLP_ATTR_MANUF:
 | |
| 			if (last >= WLP_ATTR_MANUF) {
 | |
| 				dev_err(dev, "WLP: Incorrect order of "
 | |
| 					"attribute values in D1 msg.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			result = wlp_get_manufacturer(wlp, data + used,
 | |
| 						      dev_info->manufacturer,
 | |
| 						      len - used);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to obtain "
 | |
| 					"Manufacturer attribute from D1 "
 | |
| 					"message.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			last = WLP_ATTR_MANUF;
 | |
| 			used += result;
 | |
| 			break;
 | |
| 		case WLP_ATTR_MODEL_NAME:
 | |
| 			if (last >= WLP_ATTR_MODEL_NAME) {
 | |
| 				dev_err(dev, "WLP: Incorrect order of "
 | |
| 					"attribute values in D1 msg.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			result = wlp_get_model_name(wlp, data + used,
 | |
| 						    dev_info->model_name,
 | |
| 						    len - used);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to obtain Model "
 | |
| 					"name attribute from D1 message.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			last = WLP_ATTR_MODEL_NAME;
 | |
| 			used += result;
 | |
| 			break;
 | |
| 		case WLP_ATTR_MODEL_NR:
 | |
| 			if (last >= WLP_ATTR_MODEL_NR) {
 | |
| 				dev_err(dev, "WLP: Incorrect order of "
 | |
| 					"attribute values in D1 msg.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			result = wlp_get_model_nr(wlp, data + used,
 | |
| 						  dev_info->model_nr,
 | |
| 						  len - used);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to obtain Model "
 | |
| 					"number attribute from D1 message.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			last = WLP_ATTR_MODEL_NR;
 | |
| 			used += result;
 | |
| 			break;
 | |
| 		case WLP_ATTR_SERIAL:
 | |
| 			if (last >= WLP_ATTR_SERIAL) {
 | |
| 				dev_err(dev, "WLP: Incorrect order of "
 | |
| 					"attribute values in D1 msg.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			result = wlp_get_serial(wlp, data + used,
 | |
| 						dev_info->serial, len - used);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to obtain Serial "
 | |
| 					"number attribute from D1 message.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			last = WLP_ATTR_SERIAL;
 | |
| 			used += result;
 | |
| 			break;
 | |
| 		case WLP_ATTR_PRI_DEV_TYPE:
 | |
| 			if (last >= WLP_ATTR_PRI_DEV_TYPE) {
 | |
| 				dev_err(dev, "WLP: Incorrect order of "
 | |
| 					"attribute values in D1 msg.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			result = wlp_get_prim_dev_type(wlp, data + used,
 | |
| 						       &dev_info->prim_dev_type,
 | |
| 						       len - used);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to obtain Primary "
 | |
| 					"device type attribute from D1 "
 | |
| 					"message.\n");
 | |
| 				goto error_parse;
 | |
| 			}
 | |
| 			dev_info->prim_dev_type.category =
 | |
| 				le16_to_cpu(dev_info->prim_dev_type.category);
 | |
| 			dev_info->prim_dev_type.subID =
 | |
| 				le16_to_cpu(dev_info->prim_dev_type.subID);
 | |
| 			last = WLP_ATTR_PRI_DEV_TYPE;
 | |
| 			used += result;
 | |
| 			break;
 | |
| 		default:
 | |
| 			/* This is not variable device information. */
 | |
| 			goto out;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| out:
 | |
| 	return used;
 | |
| error_parse:
 | |
| 	return -EINVAL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse incoming D1 frame, populate attribute values
 | |
|  *
 | |
|  * Caller provides pointers to memory already allocated for attributes
 | |
|  * expected in the D1 frame. These variables will be populated.
 | |
|  */
 | |
| static
 | |
| int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb,
 | |
| 		       struct wlp_uuid *uuid_e,
 | |
| 		       enum wlp_wss_sel_mthd *sel_mthd,
 | |
| 		       struct wlp_device_info *dev_info,
 | |
| 		       enum wlp_assc_error *assc_err)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_frame_assoc *d1 = (void *) skb->data;
 | |
| 	void *ptr = skb->data;
 | |
| 	size_t len = skb->len;
 | |
| 	size_t used;
 | |
| 	ssize_t result;
 | |
| 
 | |
| 	used = sizeof(*d1);
 | |
| 	result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS selection method "
 | |
| 			"from D1 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_dev_name(wlp, ptr + used, dev_info->name,
 | |
| 				     len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Name from D1 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Information from "
 | |
| 			"D1 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WLP Association Error "
 | |
| 			"Information from D1 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	result = 0;
 | |
| error_parse:
 | |
| 	return result;
 | |
| }
 | |
| /**
 | |
|  * Handle incoming D1 frame
 | |
|  *
 | |
|  * The frame has already been verified to contain an Association header with
 | |
|  * the correct version number. Parse the incoming frame, construct and send
 | |
|  * a D2 frame in response.
 | |
|  *
 | |
|  * It is not clear what to do with most fields in the incoming D1 frame. We
 | |
|  * retrieve and discard the information here for now.
 | |
|  */
 | |
| void wlp_handle_d1_frame(struct work_struct *ws)
 | |
| {
 | |
| 	struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
 | |
| 						  struct wlp_assoc_frame_ctx,
 | |
| 						  ws);
 | |
| 	struct wlp *wlp = frame_ctx->wlp;
 | |
| 	struct wlp_wss *wss = &wlp->wss;
 | |
| 	struct sk_buff *skb = frame_ctx->skb;
 | |
| 	struct uwb_dev_addr *src = &frame_ctx->src;
 | |
| 	int result;
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_uuid uuid_e;
 | |
| 	enum wlp_wss_sel_mthd sel_mthd = 0;
 | |
| 	struct wlp_device_info dev_info;
 | |
| 	enum wlp_assc_error assc_err;
 | |
| 	struct sk_buff *resp = NULL;
 | |
| 
 | |
| 	/* Parse D1 frame */
 | |
| 	mutex_lock(&wss->mutex);
 | |
| 	mutex_lock(&wlp->mutex); /* to access wlp->uuid */
 | |
| 	memset(&dev_info, 0, sizeof(dev_info));
 | |
| 	result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info,
 | |
| 				    &assc_err);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n");
 | |
| 		kfree_skb(skb);
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	kfree_skb(skb);
 | |
| 	if (!wlp_uuid_is_set(&wlp->uuid)) {
 | |
| 		dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
 | |
| 			"proceed. Respong to D1 message with error F0.\n");
 | |
| 		result = wlp_build_assoc_f0(wlp, &resp,
 | |
| 					    WLP_ASSOC_ERROR_NOT_READY);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to construct F0 message.\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Construct D2 frame */
 | |
| 		result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to construct D2 message.\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 	}
 | |
| 	/* Send D2 frame */
 | |
| 	BUG_ON(wlp->xmit_frame == NULL);
 | |
| 	result = wlp->xmit_frame(wlp, resp, src);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: Unable to transmit D2 association "
 | |
| 			"message: %d\n", result);
 | |
| 		if (result == -ENXIO)
 | |
| 			dev_err(dev, "WLP: Is network interface up? \n");
 | |
| 		/* We could try again ... */
 | |
| 		dev_kfree_skb_any(resp); /* we need to free if tx fails */
 | |
| 	}
 | |
| out:
 | |
| 	kfree(frame_ctx);
 | |
| 	mutex_unlock(&wlp->mutex);
 | |
| 	mutex_unlock(&wss->mutex);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse incoming D2 frame, create and populate temporary cache
 | |
|  *
 | |
|  * @skb: socket buffer in which D2 frame can be found
 | |
|  * @neighbor: the neighbor that sent the D2 frame
 | |
|  *
 | |
|  * Will allocate memory for temporary storage of information learned during
 | |
|  * discovery.
 | |
|  */
 | |
| int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb,
 | |
| 				struct wlp_neighbor_e *neighbor)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_frame_assoc *d2 = (void *) skb->data;
 | |
| 	void *ptr = skb->data;
 | |
| 	size_t len = skb->len;
 | |
| 	size_t used;
 | |
| 	ssize_t result;
 | |
| 	struct wlp_uuid uuid_e;
 | |
| 	struct wlp_device_info *nb_info;
 | |
| 	enum wlp_assc_error assc_err;
 | |
| 
 | |
| 	used = sizeof(*d2);
 | |
| 	result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
 | |
| 		dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
 | |
| 			"local UUID sent in D1. \n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor,
 | |
| 					   len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS information "
 | |
| 			"from D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
 | |
| 	if (neighbor->info == NULL) {
 | |
| 		dev_err(dev, "WLP: cannot allocate memory to store device "
 | |
| 			"info.\n");
 | |
| 		result = -ENOMEM;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	nb_info = neighbor->info;
 | |
| 	result = wlp_get_dev_name(wlp, ptr + used, nb_info->name,
 | |
| 				  len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Name from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Information from "
 | |
| 			"D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WLP Association Error "
 | |
| 			"Information from D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (assc_err != WLP_ASSOC_ERROR_NONE) {
 | |
| 		dev_err(dev, "WLP: neighbor device returned association "
 | |
| 			"error %d\n", assc_err);
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	result = 0;
 | |
| error_parse:
 | |
| 	if (result < 0)
 | |
| 		wlp_remove_neighbor_tmp_info(neighbor);
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in
 | |
|  *
 | |
|  * @wss: our WSS that will be enrolled
 | |
|  * @skb: socket buffer in which D2 frame can be found
 | |
|  * @neighbor: the neighbor that sent the D2 frame
 | |
|  * @wssid: the wssid of the WSS in which we want to enroll
 | |
|  *
 | |
|  * Forms part of enrollment sequence. We are trying to enroll in WSS with
 | |
|  * @wssid by using @neighbor as registrar. A D1 message was sent to
 | |
|  * @neighbor and now we need to parse the D2 response. The neighbor's
 | |
|  * response is searched for the requested WSS and if found (and it accepts
 | |
|  * enrollment), we store the information.
 | |
|  */
 | |
| int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb,
 | |
| 				 struct wlp_neighbor_e *neighbor,
 | |
| 				 struct wlp_uuid *wssid)
 | |
| {
 | |
| 	struct wlp *wlp = container_of(wss, struct wlp, wss);
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	void *ptr = skb->data;
 | |
| 	size_t len = skb->len;
 | |
| 	size_t used;
 | |
| 	ssize_t result;
 | |
| 	struct wlp_uuid uuid_e;
 | |
| 	struct wlp_uuid uuid_r;
 | |
| 	struct wlp_device_info nb_info;
 | |
| 	enum wlp_assc_error assc_err;
 | |
| 	char uuid_bufA[WLP_WSS_UUID_STRSIZE];
 | |
| 	char uuid_bufB[WLP_WSS_UUID_STRSIZE];
 | |
| 
 | |
| 	used = sizeof(struct wlp_frame_assoc);
 | |
| 	result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
 | |
| 		dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
 | |
| 			"local UUID sent in D1. \n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) {
 | |
| 		wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA),
 | |
| 				   &neighbor->uuid);
 | |
| 		wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r);
 | |
| 		dev_err(dev, "WLP: UUID of neighbor does not match UUID "
 | |
| 			"learned during discovery. Originally discovered: %s, "
 | |
| 			"now from D2 message: %s\n", uuid_bufA, uuid_bufB);
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	wss->wssid = *wssid;
 | |
| 	result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS information "
 | |
| 			"from D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
 | |
| 		dev_err(dev, "WLP: D2 message did not contain information "
 | |
| 			"for successful enrollment. \n");
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	/* Place device information on stack to continue parsing of message */
 | |
| 	result = wlp_get_dev_name(wlp, ptr + used, nb_info.name,
 | |
| 				  len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Name from D2 "
 | |
| 			"message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain Device Information from "
 | |
| 			"D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WLP Association Error "
 | |
| 			"Information from D2 message.\n");
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	if (assc_err != WLP_ASSOC_ERROR_NONE) {
 | |
| 		dev_err(dev, "WLP: neighbor device returned association "
 | |
| 			"error %d\n", assc_err);
 | |
| 		if (wss->state == WLP_WSS_STATE_PART_ENROLLED) {
 | |
| 			dev_err(dev, "WLP: Enrolled in WSS (should not "
 | |
| 				"happen according to spec). Undoing. \n");
 | |
| 			wlp_wss_reset(wss);
 | |
| 		}
 | |
| 		result = -EINVAL;
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	result = 0;
 | |
| error_parse:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse C3/C4 frame into provided variables
 | |
|  *
 | |
|  * @wssid: will point to copy of wssid retrieved from C3/C4 frame
 | |
|  * @tag:   will point to copy of tag retrieved from C3/C4 frame
 | |
|  * @virt_addr: will point to copy of virtual address retrieved from C3/C4
 | |
|  * frame.
 | |
|  *
 | |
|  * Calling function has to allocate memory for these values.
 | |
|  *
 | |
|  * skb contains a valid C3/C4 frame, return the individual fields of this
 | |
|  * frame in the provided variables.
 | |
|  */
 | |
| int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb,
 | |
| 		       struct wlp_uuid *wssid, u8 *tag,
 | |
| 		       struct uwb_mac_addr *virt_addr)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result;
 | |
| 	void *ptr = skb->data;
 | |
| 	size_t len = skb->len;
 | |
| 	size_t used;
 | |
| 	struct wlp_frame_assoc *assoc = ptr;
 | |
| 
 | |
| 	used = sizeof(*assoc);
 | |
| 	result = wlp_get_wssid(wlp, ptr + used, wssid, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSSID attribute from "
 | |
| 			"%s message.\n", wlp_assoc_frame_str(assoc->type));
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS tag attribute from "
 | |
| 			"%s message.\n", wlp_assoc_frame_str(assoc->type));
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| 	used += result;
 | |
| 	result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSS virtual address "
 | |
| 			"attribute from %s message.\n",
 | |
| 			wlp_assoc_frame_str(assoc->type));
 | |
| 		goto error_parse;
 | |
| 	}
 | |
| error_parse:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Allocate memory for and populate fields of C1 or C2 association frame
 | |
|  *
 | |
|  * The C1 and C2 association frames appear identical - except for the type.
 | |
|  */
 | |
| static
 | |
| int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 			 struct sk_buff **skb, enum wlp_assoc_type type)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result  = -ENOMEM;
 | |
| 	struct {
 | |
| 		struct wlp_frame_assoc c_hdr;
 | |
| 		struct wlp_attr_wssid wssid;
 | |
| 	} *c;
 | |
| 	struct sk_buff *_skb;
 | |
| 
 | |
| 	_skb = dev_alloc_skb(sizeof(*c));
 | |
| 	if (_skb == NULL) {
 | |
| 		dev_err(dev, "WLP: Unable to allocate memory for C1/C2 "
 | |
| 			"association frame. \n");
 | |
| 		goto error_alloc;
 | |
| 	}
 | |
| 	c = (void *) _skb->data;
 | |
| 	c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
 | |
| 	c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
 | |
| 	c->c_hdr.type = type;
 | |
| 	wlp_set_version(&c->c_hdr.version, WLP_VERSION);
 | |
| 	wlp_set_msg_type(&c->c_hdr.msg_type, type);
 | |
| 	wlp_set_wssid(&c->wssid, &wss->wssid);
 | |
| 	skb_put(_skb, sizeof(*c));
 | |
| 	*skb = _skb;
 | |
| 	result = 0;
 | |
| error_alloc:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| static
 | |
| int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 		       struct sk_buff **skb)
 | |
| {
 | |
| 	return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1);
 | |
| }
 | |
| 
 | |
| static
 | |
| int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 		       struct sk_buff **skb)
 | |
| {
 | |
| 	return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Allocate memory for and populate fields of C3 or C4 association frame
 | |
|  *
 | |
|  * The C3 and C4 association frames appear identical - except for the type.
 | |
|  */
 | |
| static
 | |
| int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 			 struct sk_buff **skb, enum wlp_assoc_type type)
 | |
| {
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	int result  = -ENOMEM;
 | |
| 	struct {
 | |
| 		struct wlp_frame_assoc c_hdr;
 | |
| 		struct wlp_attr_wssid wssid;
 | |
| 		struct wlp_attr_wss_tag wss_tag;
 | |
| 		struct wlp_attr_wss_virt wss_virt;
 | |
| 	} *c;
 | |
| 	struct sk_buff *_skb;
 | |
| 
 | |
| 	_skb = dev_alloc_skb(sizeof(*c));
 | |
| 	if (_skb == NULL) {
 | |
| 		dev_err(dev, "WLP: Unable to allocate memory for C3/C4 "
 | |
| 			"association frame. \n");
 | |
| 		goto error_alloc;
 | |
| 	}
 | |
| 	c = (void *) _skb->data;
 | |
| 	c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
 | |
| 	c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
 | |
| 	c->c_hdr.type = type;
 | |
| 	wlp_set_version(&c->c_hdr.version, WLP_VERSION);
 | |
| 	wlp_set_msg_type(&c->c_hdr.msg_type, type);
 | |
| 	wlp_set_wssid(&c->wssid, &wss->wssid);
 | |
| 	wlp_set_wss_tag(&c->wss_tag, wss->tag);
 | |
| 	wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr);
 | |
| 	skb_put(_skb, sizeof(*c));
 | |
| 	*skb = _skb;
 | |
| 	result = 0;
 | |
| error_alloc:
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| static
 | |
| int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 		       struct sk_buff **skb)
 | |
| {
 | |
| 	return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3);
 | |
| }
 | |
| 
 | |
| static
 | |
| int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 		       struct sk_buff **skb)
 | |
| {
 | |
| 	return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4);
 | |
| }
 | |
| 
 | |
| 
 | |
| #define wlp_send_assoc(type, id)					\
 | |
| static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss,	\
 | |
| 				 struct uwb_dev_addr *dev_addr)		\
 | |
| {									\
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;			\
 | |
| 	int result;							\
 | |
| 	struct sk_buff *skb = NULL;					\
 | |
| 									\
 | |
| 	/* Build the frame */						\
 | |
| 	result = wlp_build_assoc_##type(wlp, wss, &skb);		\
 | |
| 	if (result < 0) {						\
 | |
| 		dev_err(dev, "WLP: Unable to construct %s association "	\
 | |
| 			"frame: %d\n", wlp_assoc_frame_str(id), result);\
 | |
| 		goto error_build_assoc;					\
 | |
| 	}								\
 | |
| 	/* Send the frame */						\
 | |
| 	BUG_ON(wlp->xmit_frame == NULL);				\
 | |
| 	result = wlp->xmit_frame(wlp, skb, dev_addr);			\
 | |
| 	if (result < 0) {						\
 | |
| 		dev_err(dev, "WLP: Unable to transmit %s association "	\
 | |
| 			"message: %d\n", wlp_assoc_frame_str(id),	\
 | |
| 			result);					\
 | |
| 		if (result == -ENXIO)					\
 | |
| 			dev_err(dev, "WLP: Is network interface "	\
 | |
| 				"up? \n");				\
 | |
| 		goto error_xmit;					\
 | |
| 	}								\
 | |
| 	return 0;							\
 | |
| error_xmit:								\
 | |
| 	/* We could try again ... */					\
 | |
| 	dev_kfree_skb_any(skb);/*we need to free if tx fails*/		\
 | |
| error_build_assoc:							\
 | |
| 	return result;							\
 | |
| }
 | |
| 
 | |
| wlp_send_assoc(d1, WLP_ASSOC_D1)
 | |
| wlp_send_assoc(c1, WLP_ASSOC_C1)
 | |
| wlp_send_assoc(c3, WLP_ASSOC_C3)
 | |
| 
 | |
| int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss,
 | |
| 			 struct uwb_dev_addr *dev_addr,
 | |
| 			 enum wlp_assoc_type type)
 | |
| {
 | |
| 	int result = 0;
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	switch (type) {
 | |
| 	case WLP_ASSOC_D1:
 | |
| 		result = wlp_send_assoc_d1(wlp, wss, dev_addr);
 | |
| 		break;
 | |
| 	case WLP_ASSOC_C1:
 | |
| 		result = wlp_send_assoc_c1(wlp, wss, dev_addr);
 | |
| 		break;
 | |
| 	case WLP_ASSOC_C3:
 | |
| 		result = wlp_send_assoc_c3(wlp, wss, dev_addr);
 | |
| 		break;
 | |
| 	default:
 | |
| 		dev_err(dev, "WLP: Received request to send unknown "
 | |
| 			"association message.\n");
 | |
| 		result = -EINVAL;
 | |
| 		break;
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Handle incoming C1 frame
 | |
|  *
 | |
|  * The frame has already been verified to contain an Association header with
 | |
|  * the correct version number. Parse the incoming frame, construct and send
 | |
|  * a C2 frame in response.
 | |
|  */
 | |
| void wlp_handle_c1_frame(struct work_struct *ws)
 | |
| {
 | |
| 	struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
 | |
| 						  struct wlp_assoc_frame_ctx,
 | |
| 						  ws);
 | |
| 	struct wlp *wlp = frame_ctx->wlp;
 | |
| 	struct wlp_wss *wss = &wlp->wss;
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data;
 | |
| 	unsigned int len = frame_ctx->skb->len;
 | |
| 	struct uwb_dev_addr *src = &frame_ctx->src;
 | |
| 	int result;
 | |
| 	struct wlp_uuid wssid;
 | |
| 	struct sk_buff *resp = NULL;
 | |
| 
 | |
| 	/* Parse C1 frame */
 | |
| 	mutex_lock(&wss->mutex);
 | |
| 	result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid,
 | |
| 			       len - sizeof(*c1));
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
 | |
| 	    && wss->state == WLP_WSS_STATE_ACTIVE) {
 | |
| 		/* Construct C2 frame */
 | |
| 		result = wlp_build_assoc_c2(wlp, wss, &resp);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to construct C2 message.\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Construct F0 frame */
 | |
| 		result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to construct F0 message.\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 	}
 | |
| 	/* Send C2 frame */
 | |
| 	BUG_ON(wlp->xmit_frame == NULL);
 | |
| 	result = wlp->xmit_frame(wlp, resp, src);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: Unable to transmit response association "
 | |
| 			"message: %d\n", result);
 | |
| 		if (result == -ENXIO)
 | |
| 			dev_err(dev, "WLP: Is network interface up? \n");
 | |
| 		/* We could try again ... */
 | |
| 		dev_kfree_skb_any(resp); /* we need to free if tx fails */
 | |
| 	}
 | |
| out:
 | |
| 	kfree_skb(frame_ctx->skb);
 | |
| 	kfree(frame_ctx);
 | |
| 	mutex_unlock(&wss->mutex);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Handle incoming C3 frame
 | |
|  *
 | |
|  * The frame has already been verified to contain an Association header with
 | |
|  * the correct version number. Parse the incoming frame, construct and send
 | |
|  * a C4 frame in response. If the C3 frame identifies a WSS that is locally
 | |
|  * active then we connect to this neighbor (add it to our EDA cache).
 | |
|  */
 | |
| void wlp_handle_c3_frame(struct work_struct *ws)
 | |
| {
 | |
| 	struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
 | |
| 						  struct wlp_assoc_frame_ctx,
 | |
| 						  ws);
 | |
| 	struct wlp *wlp = frame_ctx->wlp;
 | |
| 	struct wlp_wss *wss = &wlp->wss;
 | |
| 	struct device *dev = &wlp->rc->uwb_dev.dev;
 | |
| 	struct sk_buff *skb = frame_ctx->skb;
 | |
| 	struct uwb_dev_addr *src = &frame_ctx->src;
 | |
| 	int result;
 | |
| 	struct sk_buff *resp = NULL;
 | |
| 	struct wlp_uuid wssid;
 | |
| 	u8 tag;
 | |
| 	struct uwb_mac_addr virt_addr;
 | |
| 
 | |
| 	/* Parse C3 frame */
 | |
| 	mutex_lock(&wss->mutex);
 | |
| 	result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: unable to obtain values from C3 frame.\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
 | |
| 	    && wss->state >= WLP_WSS_STATE_ACTIVE) {
 | |
| 		result = wlp_eda_update_node(&wlp->eda, src, wss,
 | |
| 					     (void *) virt_addr.data, tag,
 | |
| 					     WLP_WSS_CONNECTED);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to update EDA cache "
 | |
| 				"with new connected neighbor information.\n");
 | |
| 			result = wlp_build_assoc_f0(wlp, &resp,
 | |
| 						    WLP_ASSOC_ERROR_INT);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to construct F0 "
 | |
| 					"message.\n");
 | |
| 				goto out;
 | |
| 			}
 | |
| 		} else {
 | |
| 			wss->state = WLP_WSS_STATE_CONNECTED;
 | |
| 			/* Construct C4 frame */
 | |
| 			result = wlp_build_assoc_c4(wlp, wss, &resp);
 | |
| 			if (result < 0) {
 | |
| 				dev_err(dev, "WLP: Unable to construct C4 "
 | |
| 					"message.\n");
 | |
| 				goto out;
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Construct F0 frame */
 | |
| 		result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
 | |
| 		if (result < 0) {
 | |
| 			dev_err(dev, "WLP: Unable to construct F0 message.\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 	}
 | |
| 	/* Send C4 frame */
 | |
| 	BUG_ON(wlp->xmit_frame == NULL);
 | |
| 	result = wlp->xmit_frame(wlp, resp, src);
 | |
| 	if (result < 0) {
 | |
| 		dev_err(dev, "WLP: Unable to transmit response association "
 | |
| 			"message: %d\n", result);
 | |
| 		if (result == -ENXIO)
 | |
| 			dev_err(dev, "WLP: Is network interface up? \n");
 | |
| 		/* We could try again ... */
 | |
| 		dev_kfree_skb_any(resp); /* we need to free if tx fails */
 | |
| 	}
 | |
| out:
 | |
| 	kfree_skb(frame_ctx->skb);
 | |
| 	kfree(frame_ctx);
 | |
| 	mutex_unlock(&wss->mutex);
 | |
| }
 | |
| 
 | |
| 
 |