[PATCH 07/26] MBO: Implement MBO non preferred channel report
Ilan Peer
ilan.peer at intel.com
Mon Feb 15 06:53:26 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