Home | History | Annotate | Line # | Download | only in wpa_supplicant
mbo.c revision 1.1.1.2.2.1
      1          1.1  christos /*
      2          1.1  christos  * wpa_supplicant - MBO
      3          1.1  christos  *
      4          1.1  christos  * Copyright(c) 2015 Intel Deutschland GmbH
      5          1.1  christos  * Contact Information:
      6          1.1  christos  * Intel Linux Wireless <ilw (at) linux.intel.com>
      7          1.1  christos  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
      8          1.1  christos  *
      9          1.1  christos  * This software may be distributed under the terms of the BSD license.
     10          1.1  christos  * See README for more details.
     11          1.1  christos  */
     12          1.1  christos 
     13          1.1  christos #include "utils/includes.h"
     14          1.1  christos 
     15          1.1  christos #include "utils/common.h"
     16          1.1  christos #include "common/ieee802_11_defs.h"
     17          1.1  christos #include "common/gas.h"
     18          1.1  christos #include "config.h"
     19          1.1  christos #include "wpa_supplicant_i.h"
     20          1.1  christos #include "driver_i.h"
     21          1.1  christos #include "bss.h"
     22          1.1  christos #include "scan.h"
     23          1.1  christos 
     24          1.1  christos /* type + length + oui + oui type */
     25          1.1  christos #define MBO_IE_HEADER 6
     26          1.1  christos 
     27          1.1  christos 
     28          1.1  christos static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
     29          1.1  christos {
     30          1.1  christos 	if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
     31          1.1  christos 		return -1;
     32          1.1  christos 
     33          1.1  christos 	/* Only checking the validity of the channel and oper_class */
     34          1.1  christos 	if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
     35          1.1  christos 		return -1;
     36          1.1  christos 
     37          1.1  christos 	return 0;
     38          1.1  christos }
     39          1.1  christos 
     40          1.1  christos 
     41      1.1.1.2  christos const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr)
     42      1.1.1.2  christos {
     43      1.1.1.2  christos 	const u8 *mbo;
     44      1.1.1.2  christos 	u8 ie_len = mbo_ie[1];
     45      1.1.1.2  christos 
     46      1.1.1.2  christos 	if (ie_len < MBO_IE_HEADER - 2)
     47      1.1.1.2  christos 		return NULL;
     48      1.1.1.2  christos 	mbo = mbo_ie + MBO_IE_HEADER;
     49      1.1.1.2  christos 
     50      1.1.1.2  christos 	return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr);
     51      1.1.1.2  christos }
     52      1.1.1.2  christos 
     53      1.1.1.2  christos 
     54  1.1.1.2.2.1    martin const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
     55  1.1.1.2.2.1    martin 				 enum mbo_attr_id attr)
     56  1.1.1.2.2.1    martin {
     57  1.1.1.2.2.1    martin 	const u8 *mbo_ie;
     58  1.1.1.2.2.1    martin 
     59  1.1.1.2.2.1    martin 	mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE);
     60  1.1.1.2.2.1    martin 	if (!mbo_ie)
     61  1.1.1.2.2.1    martin 		return NULL;
     62  1.1.1.2.2.1    martin 
     63  1.1.1.2.2.1    martin 	return mbo_attr_from_mbo_ie(mbo_ie, attr);
     64  1.1.1.2.2.1    martin }
     65  1.1.1.2.2.1    martin 
     66  1.1.1.2.2.1    martin 
     67          1.1  christos const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
     68          1.1  christos {
     69          1.1  christos 	const u8 *mbo, *end;
     70          1.1  christos 
     71          1.1  christos 	if (!bss)
     72          1.1  christos 		return NULL;
     73          1.1  christos 
     74          1.1  christos 	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
     75          1.1  christos 	if (!mbo)
     76          1.1  christos 		return NULL;
     77          1.1  christos 
     78          1.1  christos 	end = mbo + 2 + mbo[1];
     79          1.1  christos 	mbo += MBO_IE_HEADER;
     80          1.1  christos 
     81          1.1  christos 	return get_ie(mbo, end - mbo, attr);
     82          1.1  christos }
     83          1.1  christos 
     84          1.1  christos 
     85          1.1  christos static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
     86          1.1  christos 					     struct wpabuf *mbo,
     87          1.1  christos 					     u8 start, u8 end)
     88          1.1  christos {
     89          1.1  christos 	u8 i;
     90          1.1  christos 
     91          1.1  christos 	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
     92          1.1  christos 
     93          1.1  christos 	for (i = start; i < end; i++)
     94          1.1  christos 		wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
     95          1.1  christos 
     96          1.1  christos 	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
     97          1.1  christos 	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
     98          1.1  christos }
     99          1.1  christos 
    100          1.1  christos 
    101  1.1.1.2.2.1    martin static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size)
    102  1.1.1.2.2.1    martin {
    103  1.1.1.2.2.1    martin 	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
    104  1.1.1.2.2.1    martin 	wpabuf_put_u8(mbo, size); /* Length */
    105  1.1.1.2.2.1    martin }
    106  1.1.1.2.2.1    martin 
    107  1.1.1.2.2.1    martin 
    108          1.1  christos static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
    109          1.1  christos 					struct wpabuf *mbo, u8 start, u8 end)
    110          1.1  christos {
    111          1.1  christos 	size_t size = end - start + 3;
    112          1.1  christos 
    113          1.1  christos 	if (size + 2 > wpabuf_tailroom(mbo))
    114          1.1  christos 		return;
    115          1.1  christos 
    116  1.1.1.2.2.1    martin 	wpas_mbo_non_pref_chan_attr_hdr(mbo, size);
    117          1.1  christos 	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
    118          1.1  christos }
    119          1.1  christos 
    120          1.1  christos 
    121          1.1  christos static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
    122          1.1  christos {
    123          1.1  christos 	wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
    124          1.1  christos 	wpabuf_put_u8(mbo, len); /* Length */
    125          1.1  christos 	wpabuf_put_be24(mbo, OUI_WFA);
    126          1.1  christos 	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
    127          1.1  christos }
    128          1.1  christos 
    129          1.1  christos 
    130          1.1  christos static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
    131          1.1  christos 					      struct wpabuf *mbo, u8 start,
    132          1.1  christos 					      u8 end)
    133          1.1  christos {
    134          1.1  christos 	size_t size = end - start + 7;
    135          1.1  christos 
    136          1.1  christos 	if (size + 2 > wpabuf_tailroom(mbo))
    137          1.1  christos 		return;
    138          1.1  christos 
    139          1.1  christos 	wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
    140          1.1  christos 	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
    141          1.1  christos }
    142          1.1  christos 
    143          1.1  christos 
    144          1.1  christos static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
    145          1.1  christos 					 struct wpabuf *mbo, int subelement)
    146          1.1  christos {
    147          1.1  christos 	u8 i, start = 0;
    148          1.1  christos 	struct wpa_mbo_non_pref_channel *start_pref;
    149          1.1  christos 
    150          1.1  christos 	if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
    151          1.1  christos 		if (subelement)
    152          1.1  christos 			wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
    153  1.1.1.2.2.1    martin 		else
    154  1.1.1.2.2.1    martin 			wpas_mbo_non_pref_chan_attr_hdr(mbo, 0);
    155          1.1  christos 		return;
    156          1.1  christos 	}
    157          1.1  christos 	start_pref = &wpa_s->non_pref_chan[0];
    158          1.1  christos 
    159          1.1  christos 	for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
    160          1.1  christos 		struct wpa_mbo_non_pref_channel *non_pref = NULL;
    161          1.1  christos 
    162          1.1  christos 		if (i < wpa_s->non_pref_chan_num)
    163          1.1  christos 			non_pref = &wpa_s->non_pref_chan[i];
    164          1.1  christos 		if (!non_pref ||
    165          1.1  christos 		    non_pref->oper_class != start_pref->oper_class ||
    166          1.1  christos 		    non_pref->reason != start_pref->reason ||
    167          1.1  christos 		    non_pref->preference != start_pref->preference) {
    168          1.1  christos 			if (subelement)
    169          1.1  christos 				wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
    170          1.1  christos 								  start, i);
    171          1.1  christos 			else
    172          1.1  christos 				wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
    173          1.1  christos 							    i);
    174          1.1  christos 
    175          1.1  christos 			if (!non_pref)
    176          1.1  christos 				return;
    177          1.1  christos 
    178          1.1  christos 			start = i;
    179          1.1  christos 			start_pref = non_pref;
    180          1.1  christos 		}
    181          1.1  christos 	}
    182          1.1  christos }
    183          1.1  christos 
    184          1.1  christos 
    185      1.1.1.2  christos int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
    186      1.1.1.2  christos 		int add_oce_capa)
    187          1.1  christos {
    188          1.1  christos 	struct wpabuf *mbo;
    189          1.1  christos 	int res;
    190          1.1  christos 
    191      1.1.1.2  christos 	if (len < MBO_IE_HEADER + 3 + 7 +
    192      1.1.1.2  christos 	    ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
    193          1.1  christos 		return 0;
    194          1.1  christos 
    195          1.1  christos 	/* Leave room for the MBO IE header */
    196          1.1  christos 	mbo = wpabuf_alloc(len - MBO_IE_HEADER);
    197          1.1  christos 	if (!mbo)
    198          1.1  christos 		return 0;
    199          1.1  christos 
    200          1.1  christos 	/* Add non-preferred channels attribute */
    201          1.1  christos 	wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
    202          1.1  christos 
    203          1.1  christos 	/*
    204          1.1  christos 	 * Send cellular capabilities attribute even if AP does not advertise
    205          1.1  christos 	 * cellular capabilities.
    206          1.1  christos 	 */
    207          1.1  christos 	wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
    208          1.1  christos 	wpabuf_put_u8(mbo, 1);
    209          1.1  christos 	wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
    210          1.1  christos 
    211      1.1.1.2  christos 	/* Add OCE capability indication attribute if OCE is enabled */
    212      1.1.1.2  christos 	if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) {
    213      1.1.1.2  christos 		wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
    214      1.1.1.2  christos 		wpabuf_put_u8(mbo, 1);
    215      1.1.1.2  christos 		wpabuf_put_u8(mbo, OCE_RELEASE);
    216      1.1.1.2  christos 	}
    217      1.1.1.2  christos 
    218          1.1  christos 	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
    219          1.1  christos 	if (!res)
    220      1.1.1.2  christos 		wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
    221          1.1  christos 
    222          1.1  christos 	wpabuf_free(mbo);
    223          1.1  christos 	return res;
    224          1.1  christos }
    225          1.1  christos 
    226          1.1  christos 
    227          1.1  christos static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
    228          1.1  christos 					   const u8 *data, size_t len)
    229          1.1  christos {
    230          1.1  christos 	struct wpabuf *buf;
    231          1.1  christos 	int res;
    232          1.1  christos 
    233          1.1  christos 	/*
    234          1.1  christos 	 * Send WNM-Notification Request frame only in case of a change in
    235          1.1  christos 	 * non-preferred channels list during association, if the AP supports
    236          1.1  christos 	 * MBO.
    237          1.1  christos 	 */
    238          1.1  christos 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss ||
    239          1.1  christos 	    !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
    240          1.1  christos 		return;
    241          1.1  christos 
    242          1.1  christos 	buf = wpabuf_alloc(4 + len);
    243          1.1  christos 	if (!buf)
    244          1.1  christos 		return;
    245          1.1  christos 
    246          1.1  christos 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
    247          1.1  christos 	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
    248          1.1  christos 	wpa_s->mbo_wnm_token++;
    249          1.1  christos 	if (wpa_s->mbo_wnm_token == 0)
    250          1.1  christos 		wpa_s->mbo_wnm_token++;
    251          1.1  christos 	wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
    252          1.1  christos 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
    253          1.1  christos 
    254          1.1  christos 	wpabuf_put_data(buf, data, len);
    255          1.1  christos 
    256          1.1  christos 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
    257          1.1  christos 				  wpa_s->own_addr, wpa_s->bssid,
    258          1.1  christos 				  wpabuf_head(buf), wpabuf_len(buf), 0);
    259          1.1  christos 	if (res < 0)
    260          1.1  christos 		wpa_printf(MSG_DEBUG,
    261          1.1  christos 			   "Failed to send WNM-Notification Request frame with non-preferred channel list");
    262          1.1  christos 
    263          1.1  christos 	wpabuf_free(buf);
    264          1.1  christos }
    265          1.1  christos 
    266          1.1  christos 
    267          1.1  christos static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
    268          1.1  christos {
    269          1.1  christos 	struct wpabuf *buf;
    270          1.1  christos 
    271          1.1  christos 	buf = wpabuf_alloc(512);
    272          1.1  christos 	if (!buf)
    273          1.1  christos 		return;
    274          1.1  christos 
    275          1.1  christos 	wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
    276          1.1  christos 	wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
    277          1.1  christos 				       wpabuf_len(buf));
    278  1.1.1.2.2.1    martin 	wpas_update_mbo_connect_params(wpa_s);
    279          1.1  christos 	wpabuf_free(buf);
    280          1.1  christos }
    281          1.1  christos 
    282          1.1  christos 
    283          1.1  christos static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
    284          1.1  christos 				   struct wpa_mbo_non_pref_channel *b)
    285          1.1  christos {
    286          1.1  christos 	return a->oper_class == b->oper_class && a->chan == b->chan;
    287          1.1  christos }
    288          1.1  christos 
    289          1.1  christos 
    290          1.1  christos /*
    291          1.1  christos  * wpa_non_pref_chan_cmp - Compare two channels for sorting
    292          1.1  christos  *
    293          1.1  christos  * In MBO IE non-preferred channel subelement we can put many channels in an
    294          1.1  christos  * attribute if they are in the same operating class and have the same
    295          1.1  christos  * preference and reason. To make it easy for the functions that build
    296          1.1  christos  * the IE attributes and WNM Request subelements, save the channels sorted
    297          1.1  christos  * by their oper_class and reason.
    298          1.1  christos  */
    299          1.1  christos static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
    300          1.1  christos {
    301          1.1  christos 	const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
    302          1.1  christos 
    303          1.1  christos 	if (a->oper_class != b->oper_class)
    304  1.1.1.2.2.1    martin 		return (int) a->oper_class - (int) b->oper_class;
    305          1.1  christos 	if (a->reason != b->reason)
    306  1.1.1.2.2.1    martin 		return (int) a->reason - (int) b->reason;
    307  1.1.1.2.2.1    martin 	return (int) a->preference - (int) b->preference;
    308          1.1  christos }
    309          1.1  christos 
    310          1.1  christos 
    311          1.1  christos int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
    312          1.1  christos 				  const char *non_pref_chan)
    313          1.1  christos {
    314          1.1  christos 	char *cmd, *token, *context = NULL;
    315          1.1  christos 	struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
    316          1.1  christos 	size_t num = 0, size = 0;
    317          1.1  christos 	unsigned i;
    318          1.1  christos 
    319          1.1  christos 	wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
    320          1.1  christos 		   non_pref_chan ? non_pref_chan : "N/A");
    321          1.1  christos 
    322          1.1  christos 	/*
    323      1.1.1.2  christos 	 * The shortest channel configuration is 7 characters - 3 colons and
    324      1.1.1.2  christos 	 * 4 values.
    325          1.1  christos 	 */
    326      1.1.1.2  christos 	if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
    327          1.1  christos 		goto update;
    328          1.1  christos 
    329          1.1  christos 	cmd = os_strdup(non_pref_chan);
    330          1.1  christos 	if (!cmd)
    331          1.1  christos 		return -1;
    332          1.1  christos 
    333          1.1  christos 	while ((token = str_token(cmd, " ", &context))) {
    334          1.1  christos 		struct wpa_mbo_non_pref_channel *chan;
    335          1.1  christos 		int ret;
    336          1.1  christos 		unsigned int _oper_class;
    337          1.1  christos 		unsigned int _chan;
    338          1.1  christos 		unsigned int _preference;
    339          1.1  christos 		unsigned int _reason;
    340          1.1  christos 
    341          1.1  christos 		if (num == size) {
    342          1.1  christos 			size = size ? size * 2 : 1;
    343          1.1  christos 			tmp_chans = os_realloc_array(chans, size,
    344          1.1  christos 						     sizeof(*chans));
    345          1.1  christos 			if (!tmp_chans) {
    346          1.1  christos 				wpa_printf(MSG_ERROR,
    347          1.1  christos 					   "Couldn't reallocate non_pref_chan");
    348          1.1  christos 				goto fail;
    349          1.1  christos 			}
    350          1.1  christos 			chans = tmp_chans;
    351          1.1  christos 		}
    352          1.1  christos 
    353          1.1  christos 		chan = &chans[num];
    354          1.1  christos 
    355          1.1  christos 		ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
    356          1.1  christos 			     &_chan, &_preference, &_reason);
    357          1.1  christos 		if (ret != 4 ||
    358          1.1  christos 		    _oper_class > 255 || _chan > 255 ||
    359          1.1  christos 		    _preference > 255 || _reason > 65535 ) {
    360          1.1  christos 			wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
    361          1.1  christos 				   token);
    362          1.1  christos 			goto fail;
    363          1.1  christos 		}
    364          1.1  christos 		chan->oper_class = _oper_class;
    365          1.1  christos 		chan->chan = _chan;
    366          1.1  christos 		chan->preference = _preference;
    367          1.1  christos 		chan->reason = _reason;
    368          1.1  christos 
    369          1.1  christos 		if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
    370          1.1  christos 						    chan->chan, chan->reason)) {
    371          1.1  christos 			wpa_printf(MSG_ERROR,
    372          1.1  christos 				   "Invalid non_pref_chan: oper class %d chan %d reason %d",
    373          1.1  christos 				   chan->oper_class, chan->chan, chan->reason);
    374          1.1  christos 			goto fail;
    375          1.1  christos 		}
    376          1.1  christos 
    377          1.1  christos 		for (i = 0; i < num; i++)
    378          1.1  christos 			if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
    379          1.1  christos 				break;
    380          1.1  christos 		if (i != num) {
    381          1.1  christos 			wpa_printf(MSG_ERROR,
    382          1.1  christos 				   "oper class %d chan %d is duplicated",
    383          1.1  christos 				   chan->oper_class, chan->chan);
    384          1.1  christos 			goto fail;
    385          1.1  christos 		}
    386          1.1  christos 
    387          1.1  christos 		num++;
    388          1.1  christos 	}
    389          1.1  christos 
    390          1.1  christos 	os_free(cmd);
    391          1.1  christos 
    392          1.1  christos 	if (chans) {
    393          1.1  christos 		qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
    394          1.1  christos 		      wpa_non_pref_chan_cmp);
    395          1.1  christos 	}
    396          1.1  christos 
    397          1.1  christos update:
    398          1.1  christos 	os_free(wpa_s->non_pref_chan);
    399          1.1  christos 	wpa_s->non_pref_chan = chans;
    400          1.1  christos 	wpa_s->non_pref_chan_num = num;
    401          1.1  christos 	wpas_mbo_non_pref_chan_changed(wpa_s);
    402          1.1  christos 
    403          1.1  christos 	return 0;
    404          1.1  christos 
    405          1.1  christos fail:
    406          1.1  christos 	os_free(chans);
    407          1.1  christos 	os_free(cmd);
    408          1.1  christos 	return -1;
    409          1.1  christos }
    410          1.1  christos 
    411          1.1  christos 
    412          1.1  christos void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
    413          1.1  christos {
    414      1.1.1.2  christos 	u8 *len;
    415      1.1.1.2  christos 
    416          1.1  christos 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
    417      1.1.1.2  christos 	len = wpabuf_put(ie, 1);
    418      1.1.1.2  christos 
    419          1.1  christos 	wpabuf_put_be24(ie, OUI_WFA);
    420          1.1  christos 	wpabuf_put_u8(ie, MBO_OUI_TYPE);
    421          1.1  christos 
    422          1.1  christos 	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
    423          1.1  christos 	wpabuf_put_u8(ie, 1);
    424          1.1  christos 	wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
    425      1.1.1.2  christos 	if (wpa_s->enable_oce & OCE_STA) {
    426      1.1.1.2  christos 		wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
    427      1.1.1.2  christos 		wpabuf_put_u8(ie, 1);
    428      1.1.1.2  christos 		wpabuf_put_u8(ie, OCE_RELEASE);
    429          1.1  christos 	}
    430      1.1.1.2  christos 	*len = (u8 *) wpabuf_put(ie, 0) - len - 1;
    431          1.1  christos }
    432          1.1  christos 
    433          1.1  christos 
    434          1.1  christos void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
    435          1.1  christos 			   size_t len)
    436          1.1  christos {
    437      1.1.1.2  christos 	const u8 *pos, *cell_pref = NULL;
    438          1.1  christos 	u8 id, elen;
    439          1.1  christos 	u16 disallowed_sec = 0;
    440          1.1  christos 
    441          1.1  christos 	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
    442          1.1  christos 	    mbo_ie[3] != MBO_OUI_TYPE)
    443          1.1  christos 		return;
    444          1.1  christos 
    445          1.1  christos 	pos = mbo_ie + 4;
    446          1.1  christos 	len -= 4;
    447          1.1  christos 
    448          1.1  christos 	while (len >= 2) {
    449          1.1  christos 		id = *pos++;
    450          1.1  christos 		elen = *pos++;
    451          1.1  christos 		len -= 2;
    452          1.1  christos 
    453          1.1  christos 		if (elen > len)
    454          1.1  christos 			goto fail;
    455          1.1  christos 
    456          1.1  christos 		switch (id) {
    457          1.1  christos 		case MBO_ATTR_ID_CELL_DATA_PREF:
    458          1.1  christos 			if (elen != 1)
    459          1.1  christos 				goto fail;
    460          1.1  christos 
    461          1.1  christos 			if (wpa_s->conf->mbo_cell_capa ==
    462          1.1  christos 			    MBO_CELL_CAPA_AVAILABLE)
    463          1.1  christos 				cell_pref = pos;
    464          1.1  christos 			else
    465          1.1  christos 				wpa_printf(MSG_DEBUG,
    466          1.1  christos 					   "MBO: Station does not support Cellular data connection");
    467          1.1  christos 			break;
    468          1.1  christos 		case MBO_ATTR_ID_TRANSITION_REASON:
    469          1.1  christos 			if (elen != 1)
    470          1.1  christos 				goto fail;
    471          1.1  christos 
    472      1.1.1.2  christos 			wpa_s->wnm_mbo_trans_reason_present = 1;
    473      1.1.1.2  christos 			wpa_s->wnm_mbo_transition_reason = *pos;
    474          1.1  christos 			break;
    475          1.1  christos 		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
    476          1.1  christos 			if (elen != 2)
    477          1.1  christos 				goto fail;
    478          1.1  christos 
    479          1.1  christos 			if (wpa_s->wnm_mode &
    480          1.1  christos 			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
    481          1.1  christos 				wpa_printf(MSG_DEBUG,
    482          1.1  christos 					   "MBO: Unexpected association retry delay, BSS is terminating");
    483          1.1  christos 				goto fail;
    484          1.1  christos 			} else if (wpa_s->wnm_mode &
    485          1.1  christos 				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
    486          1.1  christos 				disallowed_sec = WPA_GET_LE16(pos);
    487      1.1.1.2  christos 				wpa_printf(MSG_DEBUG,
    488      1.1.1.2  christos 					   "MBO: Association retry delay: %u",
    489      1.1.1.2  christos 					   disallowed_sec);
    490          1.1  christos 			} else {
    491          1.1  christos 				wpa_printf(MSG_DEBUG,
    492          1.1  christos 					   "MBO: Association retry delay attribute not in disassoc imminent mode");
    493          1.1  christos 			}
    494          1.1  christos 
    495          1.1  christos 			break;
    496          1.1  christos 		case MBO_ATTR_ID_AP_CAPA_IND:
    497          1.1  christos 		case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
    498          1.1  christos 		case MBO_ATTR_ID_CELL_DATA_CAPA:
    499          1.1  christos 		case MBO_ATTR_ID_ASSOC_DISALLOW:
    500          1.1  christos 		case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
    501          1.1  christos 			wpa_printf(MSG_DEBUG,
    502          1.1  christos 				   "MBO: Attribute %d should not be included in BTM Request frame",
    503          1.1  christos 				   id);
    504          1.1  christos 			break;
    505          1.1  christos 		default:
    506          1.1  christos 			wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
    507          1.1  christos 				   id);
    508          1.1  christos 			return;
    509          1.1  christos 		}
    510          1.1  christos 
    511          1.1  christos 		pos += elen;
    512          1.1  christos 		len -= elen;
    513          1.1  christos 	}
    514          1.1  christos 
    515          1.1  christos 	if (cell_pref)
    516          1.1  christos 		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
    517          1.1  christos 			*cell_pref);
    518          1.1  christos 
    519      1.1.1.2  christos 	if (wpa_s->wnm_mbo_trans_reason_present)
    520          1.1  christos 		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
    521      1.1.1.2  christos 			wpa_s->wnm_mbo_transition_reason);
    522          1.1  christos 
    523          1.1  christos 	if (disallowed_sec && wpa_s->current_bss)
    524          1.1  christos 		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
    525  1.1.1.2.2.1    martin 				     disallowed_sec, 0);
    526          1.1  christos 
    527          1.1  christos 	return;
    528          1.1  christos fail:
    529          1.1  christos 	wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
    530          1.1  christos 		   id, elen, len);
    531          1.1  christos }
    532          1.1  christos 
    533          1.1  christos 
    534          1.1  christos size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
    535          1.1  christos 				    size_t len,
    536          1.1  christos 				    enum mbo_transition_reject_reason reason)
    537          1.1  christos {
    538          1.1  christos 	u8 reject_attr[3];
    539          1.1  christos 
    540          1.1  christos 	reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
    541          1.1  christos 	reject_attr[1] = 1;
    542          1.1  christos 	reject_attr[2] = reason;
    543          1.1  christos 
    544          1.1  christos 	return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
    545          1.1  christos }
    546          1.1  christos 
    547          1.1  christos 
    548          1.1  christos void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
    549          1.1  christos {
    550          1.1  christos 	u8 cell_capa[7];
    551          1.1  christos 
    552          1.1  christos 	if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) {
    553          1.1  christos 		wpa_printf(MSG_DEBUG,
    554          1.1  christos 			   "MBO: Cellular capability already set to %u",
    555          1.1  christos 			   mbo_cell_capa);
    556          1.1  christos 		return;
    557          1.1  christos 	}
    558          1.1  christos 
    559          1.1  christos 	wpa_s->conf->mbo_cell_capa = mbo_cell_capa;
    560          1.1  christos 
    561          1.1  christos 	cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
    562          1.1  christos 	cell_capa[1] = 5; /* Length */
    563          1.1  christos 	WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
    564          1.1  christos 	cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
    565          1.1  christos 	cell_capa[6] = mbo_cell_capa;
    566          1.1  christos 
    567          1.1  christos 	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
    568          1.1  christos 	wpa_supplicant_set_default_scan_ies(wpa_s);
    569  1.1.1.2.2.1    martin 	wpas_update_mbo_connect_params(wpa_s);
    570          1.1  christos }
    571          1.1  christos 
    572          1.1  christos 
    573          1.1  christos struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
    574      1.1.1.2  christos 				   struct wpa_bss *bss, u32 mbo_subtypes)
    575          1.1  christos {
    576          1.1  christos 	struct wpabuf *anqp_buf;
    577          1.1  christos 	u8 *len_pos;
    578      1.1.1.2  christos 	u8 i;
    579          1.1  christos 
    580          1.1  christos 	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
    581          1.1  christos 		wpa_printf(MSG_INFO, "MBO: " MACSTR
    582          1.1  christos 			   " does not support MBO - cannot request MBO ANQP elements from it",
    583          1.1  christos 			   MAC2STR(bss->bssid));
    584          1.1  christos 		return NULL;
    585          1.1  christos 	}
    586          1.1  christos 
    587      1.1.1.2  christos 	/* Allocate size for the maximum case - all MBO subtypes are set */
    588      1.1.1.2  christos 	anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
    589          1.1  christos 	if (!anqp_buf)
    590          1.1  christos 		return NULL;
    591          1.1  christos 
    592          1.1  christos 	len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
    593          1.1  christos 	wpabuf_put_be24(anqp_buf, OUI_WFA);
    594          1.1  christos 	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
    595          1.1  christos 
    596      1.1.1.2  christos 	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
    597      1.1.1.2  christos 
    598      1.1.1.2  christos 	/* The first valid MBO subtype is 1 */
    599      1.1.1.2  christos 	for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
    600      1.1.1.2  christos 		if (mbo_subtypes & BIT(i))
    601      1.1.1.2  christos 			wpabuf_put_u8(anqp_buf, i);
    602      1.1.1.2  christos 	}
    603      1.1.1.2  christos 
    604          1.1  christos 	gas_anqp_set_element_len(anqp_buf, len_pos);
    605          1.1  christos 
    606          1.1  christos 	return anqp_buf;
    607          1.1  christos }
    608      1.1.1.2  christos 
    609      1.1.1.2  christos 
    610      1.1.1.2  christos void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
    611      1.1.1.2  christos 			    struct wpa_bss *bss, const u8 *sa,
    612      1.1.1.2  christos 			    const u8 *data, size_t slen)
    613      1.1.1.2  christos {
    614      1.1.1.2  christos 	const u8 *pos = data;
    615      1.1.1.2  christos 	u8 subtype;
    616      1.1.1.2  christos 
    617      1.1.1.2  christos 	if (slen < 1)
    618      1.1.1.2  christos 		return;
    619      1.1.1.2  christos 
    620      1.1.1.2  christos 	subtype = *pos++;
    621      1.1.1.2  christos 	slen--;
    622      1.1.1.2  christos 
    623      1.1.1.2  christos 	switch (subtype) {
    624      1.1.1.2  christos 	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
    625      1.1.1.2  christos 		if (slen < 1)
    626      1.1.1.2  christos 			break;
    627      1.1.1.2  christos 		wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
    628      1.1.1.2  christos 			" cell_conn_pref=%u", MAC2STR(sa), *pos);
    629      1.1.1.2  christos 		break;
    630      1.1.1.2  christos 	default:
    631      1.1.1.2  christos 		wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
    632      1.1.1.2  christos 			   subtype);
    633      1.1.1.2  christos 		break;
    634      1.1.1.2  christos 	}
    635      1.1.1.2  christos }
    636