1780fb4a2SCy Schubert /* 2780fb4a2SCy Schubert * wpa_supplicant - MBO 3780fb4a2SCy Schubert * 4780fb4a2SCy Schubert * Copyright(c) 2015 Intel Deutschland GmbH 5780fb4a2SCy Schubert * Contact Information: 6780fb4a2SCy Schubert * Intel Linux Wireless <ilw@linux.intel.com> 7780fb4a2SCy Schubert * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 8780fb4a2SCy Schubert * 9780fb4a2SCy Schubert * This software may be distributed under the terms of the BSD license. 10780fb4a2SCy Schubert * See README for more details. 11780fb4a2SCy Schubert */ 12780fb4a2SCy Schubert 13780fb4a2SCy Schubert #include "utils/includes.h" 14780fb4a2SCy Schubert 15780fb4a2SCy Schubert #include "utils/common.h" 16780fb4a2SCy Schubert #include "common/ieee802_11_defs.h" 17780fb4a2SCy Schubert #include "common/gas.h" 18*c1d255d3SCy Schubert #include "rsn_supp/wpa.h" 19780fb4a2SCy Schubert #include "config.h" 20780fb4a2SCy Schubert #include "wpa_supplicant_i.h" 21780fb4a2SCy Schubert #include "driver_i.h" 22780fb4a2SCy Schubert #include "bss.h" 23780fb4a2SCy Schubert #include "scan.h" 24780fb4a2SCy Schubert 25780fb4a2SCy Schubert /* type + length + oui + oui type */ 26780fb4a2SCy Schubert #define MBO_IE_HEADER 6 27780fb4a2SCy Schubert 28780fb4a2SCy Schubert 29780fb4a2SCy Schubert static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) 30780fb4a2SCy Schubert { 31780fb4a2SCy Schubert if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE) 32780fb4a2SCy Schubert return -1; 33780fb4a2SCy Schubert 34780fb4a2SCy Schubert /* Only checking the validity of the channel and oper_class */ 35780fb4a2SCy Schubert if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1) 36780fb4a2SCy Schubert return -1; 37780fb4a2SCy Schubert 38780fb4a2SCy Schubert return 0; 39780fb4a2SCy Schubert } 40780fb4a2SCy Schubert 41780fb4a2SCy Schubert 4285732ac8SCy Schubert const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr) 4385732ac8SCy Schubert { 4485732ac8SCy Schubert const u8 *mbo; 4585732ac8SCy Schubert u8 ie_len = mbo_ie[1]; 4685732ac8SCy Schubert 4785732ac8SCy Schubert if (ie_len < MBO_IE_HEADER - 2) 4885732ac8SCy Schubert return NULL; 4985732ac8SCy Schubert mbo = mbo_ie + MBO_IE_HEADER; 5085732ac8SCy Schubert 5185732ac8SCy Schubert return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr); 5285732ac8SCy Schubert } 5385732ac8SCy Schubert 5485732ac8SCy Schubert 554bc52338SCy Schubert const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, 564bc52338SCy Schubert enum mbo_attr_id attr) 574bc52338SCy Schubert { 584bc52338SCy Schubert const u8 *mbo_ie; 594bc52338SCy Schubert 604bc52338SCy Schubert mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE); 614bc52338SCy Schubert if (!mbo_ie) 624bc52338SCy Schubert return NULL; 634bc52338SCy Schubert 644bc52338SCy Schubert return mbo_attr_from_mbo_ie(mbo_ie, attr); 654bc52338SCy Schubert } 664bc52338SCy Schubert 674bc52338SCy Schubert 68780fb4a2SCy Schubert const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) 69780fb4a2SCy Schubert { 70780fb4a2SCy Schubert const u8 *mbo, *end; 71780fb4a2SCy Schubert 72780fb4a2SCy Schubert if (!bss) 73780fb4a2SCy Schubert return NULL; 74780fb4a2SCy Schubert 75780fb4a2SCy Schubert mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); 76780fb4a2SCy Schubert if (!mbo) 77780fb4a2SCy Schubert return NULL; 78780fb4a2SCy Schubert 79780fb4a2SCy Schubert end = mbo + 2 + mbo[1]; 80780fb4a2SCy Schubert mbo += MBO_IE_HEADER; 81780fb4a2SCy Schubert 82780fb4a2SCy Schubert return get_ie(mbo, end - mbo, attr); 83780fb4a2SCy Schubert } 84780fb4a2SCy Schubert 85780fb4a2SCy Schubert 86*c1d255d3SCy Schubert void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 87*c1d255d3SCy Schubert struct wpa_ssid *ssid) 88*c1d255d3SCy Schubert { 89*c1d255d3SCy Schubert const u8 *rsne, *mbo, *oce; 90*c1d255d3SCy Schubert struct wpa_ie_data ie; 91*c1d255d3SCy Schubert 92*c1d255d3SCy Schubert wpa_s->disable_mbo_oce = 0; 93*c1d255d3SCy Schubert if (!bss) 94*c1d255d3SCy Schubert return; 95*c1d255d3SCy Schubert mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND); 96*c1d255d3SCy Schubert oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND); 97*c1d255d3SCy Schubert if (!mbo && !oce) 98*c1d255d3SCy Schubert return; 99*c1d255d3SCy Schubert if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON)) 100*c1d255d3SCy Schubert return; /* STA-CFON is not required to enable PMF */ 101*c1d255d3SCy Schubert rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); 102*c1d255d3SCy Schubert if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0) 103*c1d255d3SCy Schubert return; /* AP is not using RSN */ 104*c1d255d3SCy Schubert 105*c1d255d3SCy Schubert if (!(ie.capabilities & WPA_CAPABILITY_MFPC)) 106*c1d255d3SCy Schubert wpa_s->disable_mbo_oce = 1; /* AP uses RSN without PMF */ 107*c1d255d3SCy Schubert if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) 108*c1d255d3SCy Schubert wpa_s->disable_mbo_oce = 1; /* STA uses RSN without PMF */ 109*c1d255d3SCy Schubert if (wpa_s->disable_mbo_oce) 110*c1d255d3SCy Schubert wpa_printf(MSG_INFO, 111*c1d255d3SCy Schubert "MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF"); 112*c1d255d3SCy Schubert } 113*c1d255d3SCy Schubert 114*c1d255d3SCy Schubert 115780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, 116780fb4a2SCy Schubert struct wpabuf *mbo, 117780fb4a2SCy Schubert u8 start, u8 end) 118780fb4a2SCy Schubert { 119780fb4a2SCy Schubert u8 i; 120780fb4a2SCy Schubert 121780fb4a2SCy Schubert wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class); 122780fb4a2SCy Schubert 123780fb4a2SCy Schubert for (i = start; i < end; i++) 124780fb4a2SCy Schubert wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan); 125780fb4a2SCy Schubert 126780fb4a2SCy Schubert wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference); 127780fb4a2SCy Schubert wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason); 128780fb4a2SCy Schubert } 129780fb4a2SCy Schubert 130780fb4a2SCy Schubert 1314bc52338SCy Schubert static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size) 1324bc52338SCy Schubert { 1334bc52338SCy Schubert wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); 1344bc52338SCy Schubert wpabuf_put_u8(mbo, size); /* Length */ 1354bc52338SCy Schubert } 1364bc52338SCy Schubert 1374bc52338SCy Schubert 138780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, 139780fb4a2SCy Schubert struct wpabuf *mbo, u8 start, u8 end) 140780fb4a2SCy Schubert { 141780fb4a2SCy Schubert size_t size = end - start + 3; 142780fb4a2SCy Schubert 143780fb4a2SCy Schubert if (size + 2 > wpabuf_tailroom(mbo)) 144780fb4a2SCy Schubert return; 145780fb4a2SCy Schubert 1464bc52338SCy Schubert wpas_mbo_non_pref_chan_attr_hdr(mbo, size); 147780fb4a2SCy Schubert wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 148780fb4a2SCy Schubert } 149780fb4a2SCy Schubert 150780fb4a2SCy Schubert 151780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len) 152780fb4a2SCy Schubert { 153780fb4a2SCy Schubert wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC); 154780fb4a2SCy Schubert wpabuf_put_u8(mbo, len); /* Length */ 155780fb4a2SCy Schubert wpabuf_put_be24(mbo, OUI_WFA); 156780fb4a2SCy Schubert wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); 157780fb4a2SCy Schubert } 158780fb4a2SCy Schubert 159780fb4a2SCy Schubert 160780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s, 161780fb4a2SCy Schubert struct wpabuf *mbo, u8 start, 162780fb4a2SCy Schubert u8 end) 163780fb4a2SCy Schubert { 164780fb4a2SCy Schubert size_t size = end - start + 7; 165780fb4a2SCy Schubert 166780fb4a2SCy Schubert if (size + 2 > wpabuf_tailroom(mbo)) 167780fb4a2SCy Schubert return; 168780fb4a2SCy Schubert 169780fb4a2SCy Schubert wpas_mbo_non_pref_chan_subelem_hdr(mbo, size); 170780fb4a2SCy Schubert wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 171780fb4a2SCy Schubert } 172780fb4a2SCy Schubert 173780fb4a2SCy Schubert 174780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, 175780fb4a2SCy Schubert struct wpabuf *mbo, int subelement) 176780fb4a2SCy Schubert { 177780fb4a2SCy Schubert u8 i, start = 0; 178780fb4a2SCy Schubert struct wpa_mbo_non_pref_channel *start_pref; 179780fb4a2SCy Schubert 180780fb4a2SCy Schubert if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { 181780fb4a2SCy Schubert if (subelement) 182780fb4a2SCy Schubert wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); 1834bc52338SCy Schubert else 1844bc52338SCy Schubert wpas_mbo_non_pref_chan_attr_hdr(mbo, 0); 185780fb4a2SCy Schubert return; 186780fb4a2SCy Schubert } 187780fb4a2SCy Schubert start_pref = &wpa_s->non_pref_chan[0]; 188780fb4a2SCy Schubert 189780fb4a2SCy Schubert for (i = 1; i <= wpa_s->non_pref_chan_num; i++) { 190780fb4a2SCy Schubert struct wpa_mbo_non_pref_channel *non_pref = NULL; 191780fb4a2SCy Schubert 192780fb4a2SCy Schubert if (i < wpa_s->non_pref_chan_num) 193780fb4a2SCy Schubert non_pref = &wpa_s->non_pref_chan[i]; 194780fb4a2SCy Schubert if (!non_pref || 195780fb4a2SCy Schubert non_pref->oper_class != start_pref->oper_class || 196780fb4a2SCy Schubert non_pref->reason != start_pref->reason || 197780fb4a2SCy Schubert non_pref->preference != start_pref->preference) { 198780fb4a2SCy Schubert if (subelement) 199780fb4a2SCy Schubert wpas_mbo_non_pref_chan_subelement(wpa_s, mbo, 200780fb4a2SCy Schubert start, i); 201780fb4a2SCy Schubert else 202780fb4a2SCy Schubert wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start, 203780fb4a2SCy Schubert i); 204780fb4a2SCy Schubert 205780fb4a2SCy Schubert if (!non_pref) 206780fb4a2SCy Schubert return; 207780fb4a2SCy Schubert 208780fb4a2SCy Schubert start = i; 209780fb4a2SCy Schubert start_pref = non_pref; 210780fb4a2SCy Schubert } 211780fb4a2SCy Schubert } 212780fb4a2SCy Schubert } 213780fb4a2SCy Schubert 214780fb4a2SCy Schubert 21585732ac8SCy Schubert int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, 21685732ac8SCy Schubert int add_oce_capa) 217780fb4a2SCy Schubert { 218780fb4a2SCy Schubert struct wpabuf *mbo; 219780fb4a2SCy Schubert int res; 220780fb4a2SCy Schubert 22185732ac8SCy Schubert if (len < MBO_IE_HEADER + 3 + 7 + 22285732ac8SCy Schubert ((wpa_s->enable_oce & OCE_STA) ? 3 : 0)) 223780fb4a2SCy Schubert return 0; 224780fb4a2SCy Schubert 225780fb4a2SCy Schubert /* Leave room for the MBO IE header */ 226780fb4a2SCy Schubert mbo = wpabuf_alloc(len - MBO_IE_HEADER); 227780fb4a2SCy Schubert if (!mbo) 228780fb4a2SCy Schubert return 0; 229780fb4a2SCy Schubert 230780fb4a2SCy Schubert /* Add non-preferred channels attribute */ 231780fb4a2SCy Schubert wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0); 232780fb4a2SCy Schubert 233780fb4a2SCy Schubert /* 234780fb4a2SCy Schubert * Send cellular capabilities attribute even if AP does not advertise 235780fb4a2SCy Schubert * cellular capabilities. 236780fb4a2SCy Schubert */ 237780fb4a2SCy Schubert wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA); 238780fb4a2SCy Schubert wpabuf_put_u8(mbo, 1); 239780fb4a2SCy Schubert wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa); 240780fb4a2SCy Schubert 24185732ac8SCy Schubert /* Add OCE capability indication attribute if OCE is enabled */ 24285732ac8SCy Schubert if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) { 24385732ac8SCy Schubert wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND); 24485732ac8SCy Schubert wpabuf_put_u8(mbo, 1); 24585732ac8SCy Schubert wpabuf_put_u8(mbo, OCE_RELEASE); 24685732ac8SCy Schubert } 24785732ac8SCy Schubert 248780fb4a2SCy Schubert res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); 249780fb4a2SCy Schubert if (!res) 25085732ac8SCy Schubert wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE"); 251780fb4a2SCy Schubert 252780fb4a2SCy Schubert wpabuf_free(mbo); 253780fb4a2SCy Schubert return res; 254780fb4a2SCy Schubert } 255780fb4a2SCy Schubert 256780fb4a2SCy Schubert 257780fb4a2SCy Schubert static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s, 258780fb4a2SCy Schubert const u8 *data, size_t len) 259780fb4a2SCy Schubert { 260780fb4a2SCy Schubert struct wpabuf *buf; 261780fb4a2SCy Schubert int res; 262780fb4a2SCy Schubert 263780fb4a2SCy Schubert /* 264780fb4a2SCy Schubert * Send WNM-Notification Request frame only in case of a change in 265780fb4a2SCy Schubert * non-preferred channels list during association, if the AP supports 266780fb4a2SCy Schubert * MBO. 267780fb4a2SCy Schubert */ 268780fb4a2SCy Schubert if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss || 269780fb4a2SCy Schubert !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) 270780fb4a2SCy Schubert return; 271780fb4a2SCy Schubert 272780fb4a2SCy Schubert buf = wpabuf_alloc(4 + len); 273780fb4a2SCy Schubert if (!buf) 274780fb4a2SCy Schubert return; 275780fb4a2SCy Schubert 276780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_WNM); 277780fb4a2SCy Schubert wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); 278780fb4a2SCy Schubert wpa_s->mbo_wnm_token++; 279780fb4a2SCy Schubert if (wpa_s->mbo_wnm_token == 0) 280780fb4a2SCy Schubert wpa_s->mbo_wnm_token++; 281780fb4a2SCy Schubert wpabuf_put_u8(buf, wpa_s->mbo_wnm_token); 282780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */ 283780fb4a2SCy Schubert 284780fb4a2SCy Schubert wpabuf_put_data(buf, data, len); 285780fb4a2SCy Schubert 286780fb4a2SCy Schubert res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 287780fb4a2SCy Schubert wpa_s->own_addr, wpa_s->bssid, 288780fb4a2SCy Schubert wpabuf_head(buf), wpabuf_len(buf), 0); 289780fb4a2SCy Schubert if (res < 0) 290780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 291780fb4a2SCy Schubert "Failed to send WNM-Notification Request frame with non-preferred channel list"); 292780fb4a2SCy Schubert 293780fb4a2SCy Schubert wpabuf_free(buf); 294780fb4a2SCy Schubert } 295780fb4a2SCy Schubert 296780fb4a2SCy Schubert 297780fb4a2SCy Schubert static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) 298780fb4a2SCy Schubert { 299780fb4a2SCy Schubert struct wpabuf *buf; 300780fb4a2SCy Schubert 301780fb4a2SCy Schubert buf = wpabuf_alloc(512); 302780fb4a2SCy Schubert if (!buf) 303780fb4a2SCy Schubert return; 304780fb4a2SCy Schubert 305780fb4a2SCy Schubert wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); 306780fb4a2SCy Schubert wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), 307780fb4a2SCy Schubert wpabuf_len(buf)); 3084bc52338SCy Schubert wpas_update_mbo_connect_params(wpa_s); 309780fb4a2SCy Schubert wpabuf_free(buf); 310780fb4a2SCy Schubert } 311780fb4a2SCy Schubert 312780fb4a2SCy Schubert 313780fb4a2SCy Schubert static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a, 314780fb4a2SCy Schubert struct wpa_mbo_non_pref_channel *b) 315780fb4a2SCy Schubert { 316780fb4a2SCy Schubert return a->oper_class == b->oper_class && a->chan == b->chan; 317780fb4a2SCy Schubert } 318780fb4a2SCy Schubert 319780fb4a2SCy Schubert 320780fb4a2SCy Schubert /* 321780fb4a2SCy Schubert * wpa_non_pref_chan_cmp - Compare two channels for sorting 322780fb4a2SCy Schubert * 323780fb4a2SCy Schubert * In MBO IE non-preferred channel subelement we can put many channels in an 324780fb4a2SCy Schubert * attribute if they are in the same operating class and have the same 325780fb4a2SCy Schubert * preference and reason. To make it easy for the functions that build 326780fb4a2SCy Schubert * the IE attributes and WNM Request subelements, save the channels sorted 327780fb4a2SCy Schubert * by their oper_class and reason. 328780fb4a2SCy Schubert */ 329780fb4a2SCy Schubert static int wpa_non_pref_chan_cmp(const void *_a, const void *_b) 330780fb4a2SCy Schubert { 331780fb4a2SCy Schubert const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; 332780fb4a2SCy Schubert 333780fb4a2SCy Schubert if (a->oper_class != b->oper_class) 3344bc52338SCy Schubert return (int) a->oper_class - (int) b->oper_class; 335780fb4a2SCy Schubert if (a->reason != b->reason) 3364bc52338SCy Schubert return (int) a->reason - (int) b->reason; 3374bc52338SCy Schubert return (int) a->preference - (int) b->preference; 338780fb4a2SCy Schubert } 339780fb4a2SCy Schubert 340780fb4a2SCy Schubert 341780fb4a2SCy Schubert int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, 342780fb4a2SCy Schubert const char *non_pref_chan) 343780fb4a2SCy Schubert { 344780fb4a2SCy Schubert char *cmd, *token, *context = NULL; 345780fb4a2SCy Schubert struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans; 346780fb4a2SCy Schubert size_t num = 0, size = 0; 347780fb4a2SCy Schubert unsigned i; 348780fb4a2SCy Schubert 349780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s", 350780fb4a2SCy Schubert non_pref_chan ? non_pref_chan : "N/A"); 351780fb4a2SCy Schubert 352780fb4a2SCy Schubert /* 35385732ac8SCy Schubert * The shortest channel configuration is 7 characters - 3 colons and 35485732ac8SCy Schubert * 4 values. 355780fb4a2SCy Schubert */ 35685732ac8SCy Schubert if (!non_pref_chan || os_strlen(non_pref_chan) < 7) 357780fb4a2SCy Schubert goto update; 358780fb4a2SCy Schubert 359780fb4a2SCy Schubert cmd = os_strdup(non_pref_chan); 360780fb4a2SCy Schubert if (!cmd) 361780fb4a2SCy Schubert return -1; 362780fb4a2SCy Schubert 363780fb4a2SCy Schubert while ((token = str_token(cmd, " ", &context))) { 364780fb4a2SCy Schubert struct wpa_mbo_non_pref_channel *chan; 365780fb4a2SCy Schubert int ret; 366780fb4a2SCy Schubert unsigned int _oper_class; 367780fb4a2SCy Schubert unsigned int _chan; 368780fb4a2SCy Schubert unsigned int _preference; 369780fb4a2SCy Schubert unsigned int _reason; 370780fb4a2SCy Schubert 371780fb4a2SCy Schubert if (num == size) { 372780fb4a2SCy Schubert size = size ? size * 2 : 1; 373780fb4a2SCy Schubert tmp_chans = os_realloc_array(chans, size, 374780fb4a2SCy Schubert sizeof(*chans)); 375780fb4a2SCy Schubert if (!tmp_chans) { 376780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 377780fb4a2SCy Schubert "Couldn't reallocate non_pref_chan"); 378780fb4a2SCy Schubert goto fail; 379780fb4a2SCy Schubert } 380780fb4a2SCy Schubert chans = tmp_chans; 381780fb4a2SCy Schubert } 382780fb4a2SCy Schubert 383780fb4a2SCy Schubert chan = &chans[num]; 384780fb4a2SCy Schubert 385780fb4a2SCy Schubert ret = sscanf(token, "%u:%u:%u:%u", &_oper_class, 386780fb4a2SCy Schubert &_chan, &_preference, &_reason); 387780fb4a2SCy Schubert if (ret != 4 || 388780fb4a2SCy Schubert _oper_class > 255 || _chan > 255 || 389780fb4a2SCy Schubert _preference > 255 || _reason > 65535 ) { 390780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s", 391780fb4a2SCy Schubert token); 392780fb4a2SCy Schubert goto fail; 393780fb4a2SCy Schubert } 394780fb4a2SCy Schubert chan->oper_class = _oper_class; 395780fb4a2SCy Schubert chan->chan = _chan; 396780fb4a2SCy Schubert chan->preference = _preference; 397780fb4a2SCy Schubert chan->reason = _reason; 398780fb4a2SCy Schubert 399780fb4a2SCy Schubert if (wpas_mbo_validate_non_pref_chan(chan->oper_class, 400780fb4a2SCy Schubert chan->chan, chan->reason)) { 401780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 402780fb4a2SCy Schubert "Invalid non_pref_chan: oper class %d chan %d reason %d", 403780fb4a2SCy Schubert chan->oper_class, chan->chan, chan->reason); 404780fb4a2SCy Schubert goto fail; 405780fb4a2SCy Schubert } 406780fb4a2SCy Schubert 407780fb4a2SCy Schubert for (i = 0; i < num; i++) 408780fb4a2SCy Schubert if (wpa_non_pref_chan_is_eq(chan, &chans[i])) 409780fb4a2SCy Schubert break; 410780fb4a2SCy Schubert if (i != num) { 411780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 412780fb4a2SCy Schubert "oper class %d chan %d is duplicated", 413780fb4a2SCy Schubert chan->oper_class, chan->chan); 414780fb4a2SCy Schubert goto fail; 415780fb4a2SCy Schubert } 416780fb4a2SCy Schubert 417780fb4a2SCy Schubert num++; 418780fb4a2SCy Schubert } 419780fb4a2SCy Schubert 420780fb4a2SCy Schubert os_free(cmd); 421780fb4a2SCy Schubert 422780fb4a2SCy Schubert if (chans) { 423780fb4a2SCy Schubert qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel), 424780fb4a2SCy Schubert wpa_non_pref_chan_cmp); 425780fb4a2SCy Schubert } 426780fb4a2SCy Schubert 427780fb4a2SCy Schubert update: 428780fb4a2SCy Schubert os_free(wpa_s->non_pref_chan); 429780fb4a2SCy Schubert wpa_s->non_pref_chan = chans; 430780fb4a2SCy Schubert wpa_s->non_pref_chan_num = num; 431780fb4a2SCy Schubert wpas_mbo_non_pref_chan_changed(wpa_s); 432780fb4a2SCy Schubert 433780fb4a2SCy Schubert return 0; 434780fb4a2SCy Schubert 435780fb4a2SCy Schubert fail: 436780fb4a2SCy Schubert os_free(chans); 437780fb4a2SCy Schubert os_free(cmd); 438780fb4a2SCy Schubert return -1; 439780fb4a2SCy Schubert } 440780fb4a2SCy Schubert 441780fb4a2SCy Schubert 442780fb4a2SCy Schubert void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) 443780fb4a2SCy Schubert { 44485732ac8SCy Schubert u8 *len; 44585732ac8SCy Schubert 446780fb4a2SCy Schubert wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); 44785732ac8SCy Schubert len = wpabuf_put(ie, 1); 44885732ac8SCy Schubert 449780fb4a2SCy Schubert wpabuf_put_be24(ie, OUI_WFA); 450780fb4a2SCy Schubert wpabuf_put_u8(ie, MBO_OUI_TYPE); 451780fb4a2SCy Schubert 452780fb4a2SCy Schubert wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA); 453780fb4a2SCy Schubert wpabuf_put_u8(ie, 1); 454780fb4a2SCy Schubert wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa); 45585732ac8SCy Schubert if (wpa_s->enable_oce & OCE_STA) { 45685732ac8SCy Schubert wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND); 45785732ac8SCy Schubert wpabuf_put_u8(ie, 1); 45885732ac8SCy Schubert wpabuf_put_u8(ie, OCE_RELEASE); 459780fb4a2SCy Schubert } 46085732ac8SCy Schubert *len = (u8 *) wpabuf_put(ie, 0) - len - 1; 461780fb4a2SCy Schubert } 462780fb4a2SCy Schubert 463780fb4a2SCy Schubert 464780fb4a2SCy Schubert void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, 465780fb4a2SCy Schubert size_t len) 466780fb4a2SCy Schubert { 46785732ac8SCy Schubert const u8 *pos, *cell_pref = NULL; 468780fb4a2SCy Schubert u8 id, elen; 469780fb4a2SCy Schubert u16 disallowed_sec = 0; 470780fb4a2SCy Schubert 471780fb4a2SCy Schubert if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA || 472780fb4a2SCy Schubert mbo_ie[3] != MBO_OUI_TYPE) 473780fb4a2SCy Schubert return; 474780fb4a2SCy Schubert 475780fb4a2SCy Schubert pos = mbo_ie + 4; 476780fb4a2SCy Schubert len -= 4; 477780fb4a2SCy Schubert 478780fb4a2SCy Schubert while (len >= 2) { 479780fb4a2SCy Schubert id = *pos++; 480780fb4a2SCy Schubert elen = *pos++; 481780fb4a2SCy Schubert len -= 2; 482780fb4a2SCy Schubert 483780fb4a2SCy Schubert if (elen > len) 484780fb4a2SCy Schubert goto fail; 485780fb4a2SCy Schubert 486780fb4a2SCy Schubert switch (id) { 487780fb4a2SCy Schubert case MBO_ATTR_ID_CELL_DATA_PREF: 488780fb4a2SCy Schubert if (elen != 1) 489780fb4a2SCy Schubert goto fail; 490780fb4a2SCy Schubert 491780fb4a2SCy Schubert if (wpa_s->conf->mbo_cell_capa == 492780fb4a2SCy Schubert MBO_CELL_CAPA_AVAILABLE) 493780fb4a2SCy Schubert cell_pref = pos; 494780fb4a2SCy Schubert else 495780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 496780fb4a2SCy Schubert "MBO: Station does not support Cellular data connection"); 497780fb4a2SCy Schubert break; 498780fb4a2SCy Schubert case MBO_ATTR_ID_TRANSITION_REASON: 499780fb4a2SCy Schubert if (elen != 1) 500780fb4a2SCy Schubert goto fail; 501780fb4a2SCy Schubert 50285732ac8SCy Schubert wpa_s->wnm_mbo_trans_reason_present = 1; 50385732ac8SCy Schubert wpa_s->wnm_mbo_transition_reason = *pos; 504780fb4a2SCy Schubert break; 505780fb4a2SCy Schubert case MBO_ATTR_ID_ASSOC_RETRY_DELAY: 506780fb4a2SCy Schubert if (elen != 2) 507780fb4a2SCy Schubert goto fail; 508780fb4a2SCy Schubert 509780fb4a2SCy Schubert if (wpa_s->wnm_mode & 510780fb4a2SCy Schubert WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { 511780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 512780fb4a2SCy Schubert "MBO: Unexpected association retry delay, BSS is terminating"); 513780fb4a2SCy Schubert goto fail; 514780fb4a2SCy Schubert } else if (wpa_s->wnm_mode & 515780fb4a2SCy Schubert WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 516780fb4a2SCy Schubert disallowed_sec = WPA_GET_LE16(pos); 51785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 51885732ac8SCy Schubert "MBO: Association retry delay: %u", 51985732ac8SCy Schubert disallowed_sec); 520780fb4a2SCy Schubert } else { 521780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 522780fb4a2SCy Schubert "MBO: Association retry delay attribute not in disassoc imminent mode"); 523780fb4a2SCy Schubert } 524780fb4a2SCy Schubert 525780fb4a2SCy Schubert break; 526780fb4a2SCy Schubert case MBO_ATTR_ID_AP_CAPA_IND: 527780fb4a2SCy Schubert case MBO_ATTR_ID_NON_PREF_CHAN_REPORT: 528780fb4a2SCy Schubert case MBO_ATTR_ID_CELL_DATA_CAPA: 529780fb4a2SCy Schubert case MBO_ATTR_ID_ASSOC_DISALLOW: 530780fb4a2SCy Schubert case MBO_ATTR_ID_TRANSITION_REJECT_REASON: 531780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 532780fb4a2SCy Schubert "MBO: Attribute %d should not be included in BTM Request frame", 533780fb4a2SCy Schubert id); 534780fb4a2SCy Schubert break; 535780fb4a2SCy Schubert default: 536780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u", 537780fb4a2SCy Schubert id); 538780fb4a2SCy Schubert return; 539780fb4a2SCy Schubert } 540780fb4a2SCy Schubert 541780fb4a2SCy Schubert pos += elen; 542780fb4a2SCy Schubert len -= elen; 543780fb4a2SCy Schubert } 544780fb4a2SCy Schubert 545780fb4a2SCy Schubert if (cell_pref) 546780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", 547780fb4a2SCy Schubert *cell_pref); 548780fb4a2SCy Schubert 54985732ac8SCy Schubert if (wpa_s->wnm_mbo_trans_reason_present) 550780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", 55185732ac8SCy Schubert wpa_s->wnm_mbo_transition_reason); 552780fb4a2SCy Schubert 553780fb4a2SCy Schubert if (disallowed_sec && wpa_s->current_bss) 554780fb4a2SCy Schubert wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, 5554bc52338SCy Schubert disallowed_sec, 0); 556780fb4a2SCy Schubert 557780fb4a2SCy Schubert return; 558780fb4a2SCy Schubert fail: 559780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)", 560780fb4a2SCy Schubert id, elen, len); 561780fb4a2SCy Schubert } 562780fb4a2SCy Schubert 563780fb4a2SCy Schubert 564780fb4a2SCy Schubert size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, 565780fb4a2SCy Schubert size_t len, 566780fb4a2SCy Schubert enum mbo_transition_reject_reason reason) 567780fb4a2SCy Schubert { 568780fb4a2SCy Schubert u8 reject_attr[3]; 569780fb4a2SCy Schubert 570780fb4a2SCy Schubert reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON; 571780fb4a2SCy Schubert reject_attr[1] = 1; 572780fb4a2SCy Schubert reject_attr[2] = reason; 573780fb4a2SCy Schubert 574780fb4a2SCy Schubert return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr)); 575780fb4a2SCy Schubert } 576780fb4a2SCy Schubert 577780fb4a2SCy Schubert 578780fb4a2SCy Schubert void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) 579780fb4a2SCy Schubert { 580780fb4a2SCy Schubert u8 cell_capa[7]; 581780fb4a2SCy Schubert 582780fb4a2SCy Schubert if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) { 583780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 584780fb4a2SCy Schubert "MBO: Cellular capability already set to %u", 585780fb4a2SCy Schubert mbo_cell_capa); 586780fb4a2SCy Schubert return; 587780fb4a2SCy Schubert } 588780fb4a2SCy Schubert 589780fb4a2SCy Schubert wpa_s->conf->mbo_cell_capa = mbo_cell_capa; 590780fb4a2SCy Schubert 591780fb4a2SCy Schubert cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC; 592780fb4a2SCy Schubert cell_capa[1] = 5; /* Length */ 593780fb4a2SCy Schubert WPA_PUT_BE24(cell_capa + 2, OUI_WFA); 594780fb4a2SCy Schubert cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA; 595780fb4a2SCy Schubert cell_capa[6] = mbo_cell_capa; 596780fb4a2SCy Schubert 597780fb4a2SCy Schubert wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); 598780fb4a2SCy Schubert wpa_supplicant_set_default_scan_ies(wpa_s); 5994bc52338SCy Schubert wpas_update_mbo_connect_params(wpa_s); 600780fb4a2SCy Schubert } 601780fb4a2SCy Schubert 602780fb4a2SCy Schubert 603780fb4a2SCy Schubert struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, 60485732ac8SCy Schubert struct wpa_bss *bss, u32 mbo_subtypes) 605780fb4a2SCy Schubert { 606780fb4a2SCy Schubert struct wpabuf *anqp_buf; 607780fb4a2SCy Schubert u8 *len_pos; 60885732ac8SCy Schubert u8 i; 609780fb4a2SCy Schubert 610780fb4a2SCy Schubert if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) { 611780fb4a2SCy Schubert wpa_printf(MSG_INFO, "MBO: " MACSTR 612780fb4a2SCy Schubert " does not support MBO - cannot request MBO ANQP elements from it", 613780fb4a2SCy Schubert MAC2STR(bss->bssid)); 614780fb4a2SCy Schubert return NULL; 615780fb4a2SCy Schubert } 616780fb4a2SCy Schubert 61785732ac8SCy Schubert /* Allocate size for the maximum case - all MBO subtypes are set */ 61885732ac8SCy Schubert anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE); 619780fb4a2SCy Schubert if (!anqp_buf) 620780fb4a2SCy Schubert return NULL; 621780fb4a2SCy Schubert 622780fb4a2SCy Schubert len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC); 623780fb4a2SCy Schubert wpabuf_put_be24(anqp_buf, OUI_WFA); 624780fb4a2SCy Schubert wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE); 625780fb4a2SCy Schubert 62685732ac8SCy Schubert wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST); 62785732ac8SCy Schubert 62885732ac8SCy Schubert /* The first valid MBO subtype is 1 */ 62985732ac8SCy Schubert for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) { 63085732ac8SCy Schubert if (mbo_subtypes & BIT(i)) 63185732ac8SCy Schubert wpabuf_put_u8(anqp_buf, i); 63285732ac8SCy Schubert } 63385732ac8SCy Schubert 634780fb4a2SCy Schubert gas_anqp_set_element_len(anqp_buf, len_pos); 635780fb4a2SCy Schubert 636780fb4a2SCy Schubert return anqp_buf; 637780fb4a2SCy Schubert } 63885732ac8SCy Schubert 63985732ac8SCy Schubert 64085732ac8SCy Schubert void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, 64185732ac8SCy Schubert struct wpa_bss *bss, const u8 *sa, 64285732ac8SCy Schubert const u8 *data, size_t slen) 64385732ac8SCy Schubert { 64485732ac8SCy Schubert const u8 *pos = data; 64585732ac8SCy Schubert u8 subtype; 64685732ac8SCy Schubert 64785732ac8SCy Schubert if (slen < 1) 64885732ac8SCy Schubert return; 64985732ac8SCy Schubert 65085732ac8SCy Schubert subtype = *pos++; 65185732ac8SCy Schubert slen--; 65285732ac8SCy Schubert 65385732ac8SCy Schubert switch (subtype) { 65485732ac8SCy Schubert case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: 65585732ac8SCy Schubert if (slen < 1) 65685732ac8SCy Schubert break; 65785732ac8SCy Schubert wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR 65885732ac8SCy Schubert " cell_conn_pref=%u", MAC2STR(sa), *pos); 65985732ac8SCy Schubert break; 66085732ac8SCy Schubert default: 66185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u", 66285732ac8SCy Schubert subtype); 66385732ac8SCy Schubert break; 66485732ac8SCy Schubert } 66585732ac8SCy Schubert } 666