[PATCH 06/25] MBO: Implement MBO non preferred channel report

Ilan Peer ilan.peer at intel.com
Mon Feb 15 06:53:24 PST 2016


From: David Spinadel <david.spinadel at intel.com>

Add MBO IE with non preferred channels to association request.

Signed-off-by: David Spinadel <david.spinadel at intel.com>
---
 src/common/ieee802_11_common.c    |  24 ++++
 src/common/ieee802_11_common.h    |   2 +
 wpa_supplicant/Makefile           |   1 +
 wpa_supplicant/ctrl_iface.c       |   4 +
 wpa_supplicant/mbo.c              | 273 ++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/sme.c              |  15 +++
 wpa_supplicant/wpa_supplicant.c   |  25 ++++
 wpa_supplicant/wpa_supplicant_i.h |  19 +++
 8 files changed, 363 insertions(+)
 create mode 100644 wpa_supplicant/mbo.c

diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 2d5927c..44ff5cb 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1205,3 +1205,27 @@ const u8 *get_ie(const u8 *ies, size_t len, u8 eid)
 
 	return NULL;
 }
+
+
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
+{
+	/*
+	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
+	 * OUI (3), OUI type (1).
+	 */
+	if (len < 6 + attr_len) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Not enough place in buffer for MBO IE: buf len = %zu, attr_len = %zu",
+			   len, attr_len);
+		return 0;
+	}
+
+	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
+	*buf++ = attr_len + 4;
+	WPA_PUT_BE24(buf, OUI_WFA);
+	buf += 3;
+	*buf++ = MBO_OUI_TYPE;
+	os_memcpy(buf, attr, attr_len);
+
+	return 6 + attr_len;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index cfd949e..1ee70d3 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -128,4 +128,6 @@ const char * fc2str(u16 fc);
 
 const u8 *get_ie(const u8 *ies, size_t len, u8 eid);
 
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 99b2901..21e7672 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1636,6 +1636,7 @@ endif
 
 
 ifdef CONFIG_MBO
+OBJS += mbo.o
 CFLAGS += -DCONFIG_MBO
 endif
 
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b3d6246..a4cbd46 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -490,6 +490,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	} else if (os_strcasecmp(cmd, "setband") == 0) {
 		ret = wpas_ctrl_set_band(wpa_s, value);
+#ifdef CONFIG_MBO
+	} else if (os_strcasecmp(cmd, "np_chan") == 0) {
+		ret = wpas_mbo_update_np_chan(wpa_s, value);
+#endif
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
new file mode 100644
index 0000000..f545034
--- /dev/null
+++ b/wpa_supplicant/mbo.c
@@ -0,0 +1,273 @@
+/*
+ * WPA Supplicant
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Contact Information:
+ * Intel Linux Wireless <ilw at linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+/* type + length + oui + oui type */
+#define MBO_IE_HEADER 6
+
+static int wpas_mbo_validate_np_chan(struct wpa_supplicant *wpa_s,
+				     u8 oper_class, u8 chan, u8 reason)
+{
+	if (reason >= MBO_NP_CHAN_REASON_RESERVED)
+			return -1;
+
+	/* Only checking the validity of the channel and oper_class */
+	if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
+		return -1;
+
+	return 0;
+}
+
+
+const u8 *wpas_mbo_get_bss_attr(struct wpa_supplicant *wpa_s,
+				struct wpa_bss *bss, enum mbo_attr_id attr)
+{
+	const u8 *mbo, *end;
+
+	if (!bss)
+		return NULL;
+
+	mbo = wpa_bss_get_vendor_ie_subtype(bss, OUI_WFA, MBO_OUI_TYPE);
+	if (!mbo)
+		return NULL;
+
+	end = mbo + 2 + mbo[1];
+	mbo += MBO_IE_HEADER;
+
+	while (mbo < end) {
+		if (mbo[0] == attr)
+			return mbo;
+
+		mbo += 2 + mbo[1];
+	}
+
+	return NULL;
+}
+
+
+static void wpas_mbo_np_chan_attr_body(struct wpa_supplicant *wpa_s,
+				       struct wpabuf *mbo, u8 start, u8 end)
+{
+	u8 i;
+
+	wpabuf_put_u8(mbo, wpa_s->np_chan[start].oper_class);
+
+	for (i = start; i < end; i++)
+		wpabuf_put_u8(mbo, wpa_s->np_chan[i].chan);
+
+	wpabuf_put_u8(mbo, wpa_s->np_chan[start].preference);
+	wpabuf_put_u8(mbo, wpa_s->np_chan[start].reason);
+	wpabuf_put_u8(mbo, wpa_s->np_chan[start].reason_detail);
+}
+
+
+static void wpas_mbo_np_chan_attr(struct wpa_supplicant *wpa_s,
+				  struct wpabuf *mbo, u8 start, u8 end)
+{
+	size_t size = end - start + 4;
+
+	if (start == end)
+		return;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NP_CHAN_REPORT);
+	wpabuf_put_u8(mbo, size); /* Length */
+
+	wpas_mbo_np_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_np_chan_attrs(struct wpa_supplicant *wpa_s,
+				   struct wpabuf *mbo)
+
+{
+	u8 oper_class = 0, reason = 0, reason_detail = 0, preference = 0;
+	u8 i, start = 0;
+
+	if (!wpa_s->np_chan || !wpa_s->np_chan_num)
+		return;
+
+	for (i = 0; i <= wpa_s->np_chan_num; i++) {
+		if (i == wpa_s->np_chan_num ||
+		    wpa_s->np_chan[i].oper_class != oper_class ||
+		    wpa_s->np_chan[i].reason != reason ||
+		    wpa_s->np_chan[i].reason_detail != reason_detail ||
+		    wpa_s->np_chan[i].preference != preference) {
+			wpas_mbo_np_chan_attr(wpa_s, mbo, start, i);
+
+			if (i == wpa_s->np_chan_num)
+				return;
+
+			start = i;
+			oper_class = wpa_s->np_chan[i].oper_class;
+			reason = wpa_s->np_chan[i].reason;
+			reason_detail = wpa_s->np_chan[i].reason_detail;
+			preference = wpa_s->np_chan[i].preference;
+		}
+	}
+}
+
+
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+{
+	struct wpabuf *mbo;
+	int res;
+
+	if (!wpa_s->np_chan || !wpa_s->np_chan_num)
+		return 0;
+
+	if (len < MBO_IE_HEADER + 7)
+		return 0;
+
+	/* Leave room for the MBO IE header */
+	mbo = wpabuf_alloc(len - MBO_IE_HEADER);
+	if (!mbo)
+		return 0;
+
+	/* Add Non-preferred channels attribute */
+	wpas_mbo_np_chan_attrs(wpa_s, mbo);
+
+	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
+	if (!res)
+		wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+
+	wpabuf_free(mbo);
+	return res;
+}
+
+
+static int wpa_np_chan_is_eq(struct wpa_mbo_np_channel *a,
+			     struct wpa_mbo_np_channel *b)
+{
+	return a->oper_class == b->oper_class && a->chan == b->chan;
+}
+
+
+/*
+ * wpa_np_chan_cmp - compares two channels for sorting
+ *
+ * In MBO IE and non preferred channel subelement we can put many channels in
+ * an attribute if they are in the same oper calss and have the same preference,
+ * reason and reason detail. To make it easy for the functions that build the
+ * IE attributes and WNM req subelements save the channels sorted by their
+ * oper_class, reason and reason_detail.
+ */
+static int wpa_np_chan_cmp(const void *_a, const void *_b)
+{
+	const struct wpa_mbo_np_channel *a = _a, *b = _b;
+
+	if (a->oper_class != b->oper_class)
+		return a->oper_class - b->oper_class;
+	if (a->reason != b->reason)
+		return a->reason - b->reason;
+	if (a->reason_detail != b->reason_detail)
+		return a->reason_detail -  b->reason_detail;
+	return a->preference - b->preference;
+}
+
+
+int wpas_mbo_update_np_chan(struct wpa_supplicant *wpa_s, const char *np_chan)
+{
+	char *cmd, *token, *context = NULL;
+	struct wpa_mbo_np_channel *chans = NULL;
+	size_t num = 0, size = 0;
+	unsigned i;
+
+	wpa_printf(MSG_DEBUG, "Update non preferred channels, np_chan = %s",
+		   np_chan);
+
+	/*
+	 * The shortest channel configuration is 10 characters - commas, 3
+	 * colons and 4 values that one of them (oper_class) is 2 digits or more
+	 */
+	if (!np_chan || os_strlen(np_chan) < 10)
+		goto update;
+
+	cmd = os_strdup(np_chan);
+	if (!cmd)
+		return -1;
+
+	while ((token = str_token(cmd + 1, " ", &context))) {
+		struct wpa_mbo_np_channel *chan;
+		int ret;
+
+		if (num == size) {
+			size = size ? size * 2 : 1;
+			chans = os_realloc_array(chans, size, sizeof(*chans));
+			if (!chans) {
+				wpa_printf(MSG_ERROR,
+					   "Couldn't reallocate np_chan");
+				goto free;
+			}
+		}
+
+		chan = &chans[num];
+
+		ret = sscanf(token, "%hhu:%hhu:%hhu:%u:%hhu", &chan->oper_class,
+			     &chan->chan, &chan->preference, &chan->reason,
+			     &chan->reason_detail);
+		if (ret != 4 && ret != 5) {
+			wpa_printf(MSG_ERROR, "Invalid np chan input %s",
+				   token);
+			goto free;
+		}
+
+		if (ret == 4)
+			chan->reason_detail = 0;
+
+		if (wpas_mbo_validate_np_chan(wpa_s, chan->oper_class,
+					      chan->chan, chan->reason)) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid np_chan: oper class %d chan %d reason %d",
+				   chan->oper_class, chan->chan,
+				   chan->reason);
+			goto free;
+		}
+
+		for (i = 0; i < num; i++)
+			if (wpa_np_chan_is_eq(chan, &chans[i]))
+				break;
+		if (i != num) {
+			wpa_printf(MSG_ERROR,
+				   "oper class %d chan %d is duplicated",
+				   chan->oper_class, chan->chan);
+			goto free;
+		}
+
+		num++;
+	}
+
+	os_free(cmd);
+
+	qsort(chans, num, sizeof(struct wpa_mbo_np_channel), wpa_np_chan_cmp);
+
+update:
+	os_free(wpa_s->np_chan);
+	wpa_s->np_chan = chans;
+	wpa_s->np_chan_num = num;
+
+	return 0;
+
+free:
+	os_free(chans);
+	os_free(cmd);
+	return -1;
+}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 889525b..b37c20a 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -482,6 +482,21 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 
 	sme_auth_handle_rrm(wpa_s, bss);
 
+#ifdef CONFIG_MBO
+	if (wpa_bss_get_vendor_ie_subtype(bss, OUI_WFA, MBO_OUI_TYPE)) {
+		u8 *pos;
+		size_t len;
+		int res;
+
+		pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+		len = sizeof(wpa_s->sme.assoc_req_ie) -
+			wpa_s->sme.assoc_req_ie_len;
+		res = wpas_mbo_ie(wpa_s, pos, len);
+		if (res >= 0)
+			wpa_s->sme.assoc_req_ie_len += res;
+	}
+#endif /* CONFIG_MBO */
+
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
 	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index cdc6a53..dc7a586 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -549,6 +549,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 	wpa_s->sched_scan_plans_num = 0;
 	os_free(wpa_s->sched_scan_plans);
 	wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+	wpa_s->np_chan_num = 0;
+	os_free(wpa_s->np_chan);
+#endif /* CONFIG_MBO */
 }
 
 
@@ -1418,6 +1423,9 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 		if (wpa_s->conf->hs20)
 			*pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+		*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
 		break;
 	case 6: /* Bits 48-55 */
 		break;
@@ -2293,6 +2301,20 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_MBO
+	if (wpa_bss_get_vendor_ie_subtype(bss, OUI_WFA, MBO_OUI_TYPE)) {
+		u8 *pos;
+		size_t len;
+		int res;
+
+		pos = wpa_ie + wpa_ie_len;
+		len = sizeof(wpa_ie) - wpa_ie_len;
+		res = wpas_mbo_ie(wpa_s, pos, len);
+		if (res >= 0)
+			wpa_ie_len += res;
+	}
+#endif /* CONFIG_MBO */
+
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
@@ -4761,6 +4783,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_HS20
 	hs20_init(wpa_s);
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	wpas_mbo_update_np_chan(wpa_s, wpa_s->conf->np_chan);
+#endif /* CONFIG_MBO */
 
 	return 0;
 }
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 999ab8f..9519fb6 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1016,6 +1016,18 @@ struct wpa_supplicant {
 	const struct wpabuf *fst_ies;
 	struct wpabuf *received_mb_ies;
 #endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+	/* Multi band operation non preffered channel */
+	struct wpa_mbo_np_channel {
+		enum mbo_np_chan_reason reason;
+		u8 oper_class;
+		u8 chan;
+		u8 reason_detail;
+		u8 preference;
+	} *np_chan;
+	size_t np_chan_num;
+#endif /* CONFIG_MBO */
 };
 
 
@@ -1128,6 +1140,13 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
 					      const u8 *frame, size_t len,
 					      int rssi);
 
+
+/* MBO funcs */
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len);
+const u8 *wpas_mbo_get_bss_attr(struct wpa_supplicant *wpa_s,
+				struct wpa_bss *bss, enum mbo_attr_id attr);
+int wpas_mbo_update_np_chan(struct wpa_supplicant *wpa_s, const char *np_chan);
+
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
  * @wpa_s: Pointer to wpa_supplicant data
-- 
1.9.1




More information about the Hostap mailing list