From: David Spinadel <david.spinadel@xxxxxxxxx> Add MBO IE with non preferred channels to association request. Signed-off-by: David Spinadel <david.spinadel@xxxxxxxxx> --- 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@xxxxxxxxxxxxxxx> + * 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap