1252190Srpaulo/*
2252190Srpaulo * hostapd / IEEE 802.11 Management
3252190Srpaulo * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
4252190Srpaulo *
5252190Srpaulo * This software may be distributed under the terms of the BSD license.
6252190Srpaulo * See README for more details.
7252190Srpaulo */
8252190Srpaulo
9252190Srpaulo#include "utils/includes.h"
10252190Srpaulo
11252190Srpaulo#include "utils/common.h"
12252190Srpaulo#include "common/ieee802_11_defs.h"
13346981Scy#include "common/ocv.h"
14252190Srpaulo#include "hostapd.h"
15252190Srpaulo#include "sta_info.h"
16252190Srpaulo#include "ap_config.h"
17252190Srpaulo#include "ap_drv_ops.h"
18346981Scy#include "wpa_auth.h"
19252190Srpaulo#include "ieee802_11.h"
20252190Srpaulo
21252190Srpaulo
22252190Srpaulo#ifdef CONFIG_IEEE80211W
23252190Srpaulo
24252190Srpaulou8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
25252190Srpaulo				     struct sta_info *sta, u8 *eid)
26252190Srpaulo{
27252190Srpaulo	u8 *pos = eid;
28252190Srpaulo	u32 timeout, tu;
29281806Srpaulo	struct os_reltime now, passed;
30252190Srpaulo
31252190Srpaulo	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
32252190Srpaulo	*pos++ = 5;
33252190Srpaulo	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
34281806Srpaulo	os_get_reltime(&now);
35281806Srpaulo	os_reltime_sub(&now, &sta->sa_query_start, &passed);
36252190Srpaulo	tu = (passed.sec * 1000000 + passed.usec) / 1024;
37252190Srpaulo	if (hapd->conf->assoc_sa_query_max_timeout > tu)
38252190Srpaulo		timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
39252190Srpaulo	else
40252190Srpaulo		timeout = 0;
41252190Srpaulo	if (timeout < hapd->conf->assoc_sa_query_max_timeout)
42252190Srpaulo		timeout++; /* add some extra time for local timers */
43252190Srpaulo	WPA_PUT_LE32(pos, timeout);
44252190Srpaulo	pos += 4;
45252190Srpaulo
46252190Srpaulo	return pos;
47252190Srpaulo}
48252190Srpaulo
49252190Srpaulo
50252190Srpaulo/* MLME-SAQuery.request */
51252190Srpaulovoid ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
52252190Srpaulo				  const u8 *addr, const u8 *trans_id)
53252190Srpaulo{
54346981Scy#ifdef CONFIG_OCV
55346981Scy	struct sta_info *sta;
56346981Scy#endif /* CONFIG_OCV */
57346981Scy	struct ieee80211_mgmt *mgmt;
58346981Scy	u8 *oci_ie = NULL;
59346981Scy	u8 oci_ie_len = 0;
60252190Srpaulo	u8 *end;
61252190Srpaulo
62252190Srpaulo	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
63252190Srpaulo		   MACSTR, MAC2STR(addr));
64252190Srpaulo	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
65252190Srpaulo		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
66252190Srpaulo
67346981Scy#ifdef CONFIG_OCV
68346981Scy	sta = ap_get_sta(hapd, addr);
69346981Scy	if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
70346981Scy		struct wpa_channel_info ci;
71346981Scy
72346981Scy		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
73346981Scy			wpa_printf(MSG_WARNING,
74346981Scy				   "Failed to get channel info for OCI element in SA Query Request");
75346981Scy			return;
76346981Scy		}
77346981Scy
78346981Scy		oci_ie_len = OCV_OCI_EXTENDED_LEN;
79346981Scy		oci_ie = os_zalloc(oci_ie_len);
80346981Scy		if (!oci_ie) {
81346981Scy			wpa_printf(MSG_WARNING,
82346981Scy				   "Failed to allocate buffer for OCI element in SA Query Request");
83346981Scy			return;
84346981Scy		}
85346981Scy
86346981Scy		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
87346981Scy			os_free(oci_ie);
88346981Scy			return;
89346981Scy		}
90346981Scy	}
91346981Scy#endif /* CONFIG_OCV */
92346981Scy
93346981Scy	mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
94346981Scy	if (!mgmt) {
95346981Scy		wpa_printf(MSG_DEBUG,
96346981Scy			   "Failed to allocate buffer for SA Query Response frame");
97346981Scy		os_free(oci_ie);
98346981Scy		return;
99346981Scy	}
100346981Scy
101346981Scy	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
102346981Scy					   WLAN_FC_STYPE_ACTION);
103346981Scy	os_memcpy(mgmt->da, addr, ETH_ALEN);
104346981Scy	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
105346981Scy	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
106346981Scy	mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
107346981Scy	mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
108346981Scy	os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
109252190Srpaulo		  WLAN_SA_QUERY_TR_ID_LEN);
110346981Scy	end = mgmt->u.action.u.sa_query_req.variable;
111346981Scy#ifdef CONFIG_OCV
112346981Scy	if (oci_ie_len > 0) {
113346981Scy		os_memcpy(end, oci_ie, oci_ie_len);
114346981Scy		end += oci_ie_len;
115346981Scy	}
116346981Scy#endif /* CONFIG_OCV */
117346981Scy	if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0)
118281806Srpaulo		wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
119346981Scy
120346981Scy	os_free(mgmt);
121346981Scy	os_free(oci_ie);
122252190Srpaulo}
123252190Srpaulo
124252190Srpaulo
125252190Srpaulostatic void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
126252190Srpaulo					  const u8 *sa, const u8 *trans_id)
127252190Srpaulo{
128252190Srpaulo	struct sta_info *sta;
129346981Scy	struct ieee80211_mgmt *resp;
130346981Scy	u8 *oci_ie = NULL;
131346981Scy	u8 oci_ie_len = 0;
132252190Srpaulo	u8 *end;
133252190Srpaulo
134252190Srpaulo	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
135252190Srpaulo		   MACSTR, MAC2STR(sa));
136252190Srpaulo	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
137252190Srpaulo		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
138252190Srpaulo
139252190Srpaulo	sta = ap_get_sta(hapd, sa);
140252190Srpaulo	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
141252190Srpaulo		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
142252190Srpaulo			   "from unassociated STA " MACSTR, MAC2STR(sa));
143252190Srpaulo		return;
144252190Srpaulo	}
145252190Srpaulo
146346981Scy#ifdef CONFIG_OCV
147346981Scy	if (wpa_auth_uses_ocv(sta->wpa_sm)) {
148346981Scy		struct wpa_channel_info ci;
149346981Scy
150346981Scy		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
151346981Scy			wpa_printf(MSG_WARNING,
152346981Scy				   "Failed to get channel info for OCI element in SA Query Response");
153346981Scy			return;
154346981Scy		}
155346981Scy
156346981Scy		oci_ie_len = OCV_OCI_EXTENDED_LEN;
157346981Scy		oci_ie = os_zalloc(oci_ie_len);
158346981Scy		if (!oci_ie) {
159346981Scy			wpa_printf(MSG_WARNING,
160346981Scy				   "Failed to allocate buffer for for OCI element in SA Query Response");
161346981Scy			return;
162346981Scy		}
163346981Scy
164346981Scy		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
165346981Scy			os_free(oci_ie);
166346981Scy			return;
167346981Scy		}
168346981Scy	}
169346981Scy#endif /* CONFIG_OCV */
170346981Scy
171346981Scy	resp = os_zalloc(sizeof(*resp) + oci_ie_len);
172346981Scy	if (!resp) {
173346981Scy		wpa_printf(MSG_DEBUG,
174346981Scy			   "Failed to allocate buffer for SA Query Response frame");
175346981Scy		os_free(oci_ie);
176346981Scy		return;
177346981Scy	}
178346981Scy
179252190Srpaulo	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
180252190Srpaulo		   MACSTR, MAC2STR(sa));
181252190Srpaulo
182346981Scy	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
183346981Scy					   WLAN_FC_STYPE_ACTION);
184346981Scy	os_memcpy(resp->da, sa, ETH_ALEN);
185346981Scy	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
186346981Scy	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
187346981Scy	resp->u.action.category = WLAN_ACTION_SA_QUERY;
188346981Scy	resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
189346981Scy	os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
190252190Srpaulo		  WLAN_SA_QUERY_TR_ID_LEN);
191346981Scy	end = resp->u.action.u.sa_query_req.variable;
192346981Scy#ifdef CONFIG_OCV
193346981Scy	if (oci_ie_len > 0) {
194346981Scy		os_memcpy(end, oci_ie, oci_ie_len);
195346981Scy		end += oci_ie_len;
196346981Scy	}
197346981Scy#endif /* CONFIG_OCV */
198346981Scy	if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0)
199281806Srpaulo		wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
200346981Scy
201346981Scy	os_free(resp);
202346981Scy	os_free(oci_ie);
203252190Srpaulo}
204252190Srpaulo
205252190Srpaulo
206346981Scyvoid ieee802_11_sa_query_action(struct hostapd_data *hapd,
207346981Scy				const struct ieee80211_mgmt *mgmt,
208346981Scy				size_t len)
209252190Srpaulo{
210252190Srpaulo	struct sta_info *sta;
211252190Srpaulo	int i;
212346981Scy	const u8 *sa = mgmt->sa;
213346981Scy	const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
214346981Scy	const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
215252190Srpaulo
216346981Scy	if (((const u8 *) mgmt) + len <
217346981Scy	    mgmt->u.action.u.sa_query_resp.variable) {
218346981Scy		wpa_printf(MSG_DEBUG,
219346981Scy			   "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
220346981Scy			   (unsigned long) len);
221346981Scy		return;
222346981Scy	}
223346981Scy
224346981Scy	sta = ap_get_sta(hapd, sa);
225346981Scy
226346981Scy#ifdef CONFIG_OCV
227346981Scy	if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
228346981Scy		struct ieee802_11_elems elems;
229346981Scy		struct wpa_channel_info ci;
230346981Scy		int tx_chanwidth;
231346981Scy		int tx_seg1_idx;
232346981Scy		size_t ies_len;
233346981Scy		const u8 *ies;
234346981Scy
235346981Scy		ies = mgmt->u.action.u.sa_query_resp.variable;
236346981Scy		ies_len = len - (ies - (u8 *) mgmt);
237346981Scy		if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
238346981Scy		    ParseFailed) {
239346981Scy			wpa_printf(MSG_DEBUG,
240346981Scy				   "SA Query: Failed to parse elements");
241346981Scy			return;
242346981Scy		}
243346981Scy
244346981Scy		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
245346981Scy			wpa_printf(MSG_WARNING,
246346981Scy				   "Failed to get channel info to validate received OCI in SA Query Action frame");
247346981Scy			return;
248346981Scy		}
249346981Scy
250346981Scy		if (get_sta_tx_parameters(sta->wpa_sm,
251346981Scy					  channel_width_to_int(ci.chanwidth),
252346981Scy					  ci.seg1_idx, &tx_chanwidth,
253346981Scy					  &tx_seg1_idx) < 0)
254346981Scy			return;
255346981Scy
256346981Scy		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
257346981Scy					 tx_chanwidth, tx_seg1_idx) != 0) {
258346981Scy			wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
259346981Scy			return;
260346981Scy		}
261346981Scy	}
262346981Scy#endif /* CONFIG_OCV */
263346981Scy
264252190Srpaulo	if (action_type == WLAN_SA_QUERY_REQUEST) {
265252190Srpaulo		ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
266252190Srpaulo		return;
267252190Srpaulo	}
268252190Srpaulo
269252190Srpaulo	if (action_type != WLAN_SA_QUERY_RESPONSE) {
270252190Srpaulo		wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
271252190Srpaulo			   "Action %d", action_type);
272252190Srpaulo		return;
273252190Srpaulo	}
274252190Srpaulo
275252190Srpaulo	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
276252190Srpaulo		   MACSTR, MAC2STR(sa));
277252190Srpaulo	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
278252190Srpaulo		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
279252190Srpaulo
280252190Srpaulo	/* MLME-SAQuery.confirm */
281252190Srpaulo
282252190Srpaulo	if (sta == NULL || sta->sa_query_trans_id == NULL) {
283252190Srpaulo		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
284252190Srpaulo			   "pending SA Query request found");
285252190Srpaulo		return;
286252190Srpaulo	}
287252190Srpaulo
288252190Srpaulo	for (i = 0; i < sta->sa_query_count; i++) {
289252190Srpaulo		if (os_memcmp(sta->sa_query_trans_id +
290252190Srpaulo			      i * WLAN_SA_QUERY_TR_ID_LEN,
291252190Srpaulo			      trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
292252190Srpaulo			break;
293252190Srpaulo	}
294252190Srpaulo
295252190Srpaulo	if (i >= sta->sa_query_count) {
296252190Srpaulo		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
297252190Srpaulo			   "transaction identifier found");
298252190Srpaulo		return;
299252190Srpaulo	}
300252190Srpaulo
301252190Srpaulo	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
302252190Srpaulo		       HOSTAPD_LEVEL_DEBUG,
303252190Srpaulo		       "Reply to pending SA Query received");
304252190Srpaulo	ap_sta_stop_sa_query(hapd, sta);
305252190Srpaulo}
306252190Srpaulo
307252190Srpaulo#endif /* CONFIG_IEEE80211W */
308252190Srpaulo
309252190Srpaulo
310281806Srpaulostatic void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
311281806Srpaulo{
312281806Srpaulo	*pos = 0x00;
313281806Srpaulo
314281806Srpaulo	switch (idx) {
315281806Srpaulo	case 0: /* Bits 0-7 */
316281806Srpaulo		if (hapd->iconf->obss_interval)
317281806Srpaulo			*pos |= 0x01; /* Bit 0 - Coexistence management */
318337817Scy		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
319337817Scy			*pos |= 0x04; /* Bit 2 - Extended Channel Switching */
320281806Srpaulo		break;
321281806Srpaulo	case 1: /* Bits 8-15 */
322281806Srpaulo		if (hapd->conf->proxy_arp)
323281806Srpaulo			*pos |= 0x10; /* Bit 12 - Proxy ARP */
324346981Scy		if (hapd->conf->coloc_intf_reporting) {
325346981Scy			/* Bit 13 - Collocated Interference Reporting */
326346981Scy			*pos |= 0x20;
327346981Scy		}
328281806Srpaulo		break;
329281806Srpaulo	case 2: /* Bits 16-23 */
330281806Srpaulo		if (hapd->conf->wnm_sleep_mode)
331281806Srpaulo			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
332281806Srpaulo		if (hapd->conf->bss_transition)
333281806Srpaulo			*pos |= 0x08; /* Bit 19 - BSS Transition */
334281806Srpaulo		break;
335281806Srpaulo	case 3: /* Bits 24-31 */
336346981Scy#ifdef CONFIG_WNM_AP
337281806Srpaulo		*pos |= 0x02; /* Bit 25 - SSID List */
338346981Scy#endif /* CONFIG_WNM_AP */
339281806Srpaulo		if (hapd->conf->time_advertisement == 2)
340281806Srpaulo			*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
341281806Srpaulo		if (hapd->conf->interworking)
342281806Srpaulo			*pos |= 0x80; /* Bit 31 - Interworking */
343281806Srpaulo		break;
344281806Srpaulo	case 4: /* Bits 32-39 */
345281806Srpaulo		if (hapd->conf->qos_map_set_len)
346281806Srpaulo			*pos |= 0x01; /* Bit 32 - QoS Map */
347281806Srpaulo		if (hapd->conf->tdls & TDLS_PROHIBIT)
348281806Srpaulo			*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
349281806Srpaulo		if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
350281806Srpaulo			/* Bit 39 - TDLS Channel Switching Prohibited */
351281806Srpaulo			*pos |= 0x80;
352281806Srpaulo		}
353281806Srpaulo		break;
354281806Srpaulo	case 5: /* Bits 40-47 */
355281806Srpaulo#ifdef CONFIG_HS20
356281806Srpaulo		if (hapd->conf->hs20)
357281806Srpaulo			*pos |= 0x40; /* Bit 46 - WNM-Notification */
358281806Srpaulo#endif /* CONFIG_HS20 */
359337817Scy#ifdef CONFIG_MBO
360337817Scy		if (hapd->conf->mbo_enabled)
361337817Scy			*pos |= 0x40; /* Bit 46 - WNM-Notification */
362337817Scy#endif /* CONFIG_MBO */
363281806Srpaulo		break;
364281806Srpaulo	case 6: /* Bits 48-55 */
365281806Srpaulo		if (hapd->conf->ssid.utf8_ssid)
366281806Srpaulo			*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
367281806Srpaulo		break;
368346981Scy	case 7: /* Bits 56-63 */
369346981Scy		break;
370337817Scy	case 8: /* Bits 64-71 */
371337817Scy		if (hapd->conf->ftm_responder)
372337817Scy			*pos |= 0x40; /* Bit 70 - FTM responder */
373337817Scy		if (hapd->conf->ftm_initiator)
374337817Scy			*pos |= 0x80; /* Bit 71 - FTM initiator */
375337817Scy		break;
376346981Scy	case 9: /* Bits 72-79 */
377346981Scy#ifdef CONFIG_FILS
378346981Scy		if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
379346981Scy		    wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
380346981Scy			*pos |= 0x01;
381346981Scy#endif /* CONFIG_FILS */
382346981Scy		break;
383346981Scy	case 10: /* Bits 80-87 */
384346981Scy#ifdef CONFIG_SAE
385346981Scy		if (hapd->conf->wpa &&
386346981Scy		    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
387346981Scy			int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
388346981Scy
389346981Scy			if (in_use)
390346981Scy				*pos |= 0x02; /* Bit 81 - SAE Password
391346981Scy					       * Identifiers In Use */
392346981Scy			if (in_use == 2)
393346981Scy				*pos |= 0x04; /* Bit 82 - SAE Password
394346981Scy					       * Identifiers Used Exclusively */
395346981Scy		}
396346981Scy#endif /* CONFIG_SAE */
397346981Scy		break;
398281806Srpaulo	}
399281806Srpaulo}
400281806Srpaulo
401281806Srpaulo
402252190Srpaulou8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
403252190Srpaulo{
404252190Srpaulo	u8 *pos = eid;
405281806Srpaulo	u8 len = 0, i;
406252190Srpaulo
407252190Srpaulo	if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
408252190Srpaulo		len = 5;
409252190Srpaulo	if (len < 4 && hapd->conf->interworking)
410252190Srpaulo		len = 4;
411252190Srpaulo	if (len < 3 && hapd->conf->wnm_sleep_mode)
412252190Srpaulo		len = 3;
413281806Srpaulo	if (len < 1 && hapd->iconf->obss_interval)
414281806Srpaulo		len = 1;
415252190Srpaulo	if (len < 7 && hapd->conf->ssid.utf8_ssid)
416252190Srpaulo		len = 7;
417337817Scy	if (len < 9 &&
418337817Scy	    (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
419337817Scy		len = 9;
420346981Scy#ifdef CONFIG_WNM_AP
421252190Srpaulo	if (len < 4)
422252190Srpaulo		len = 4;
423346981Scy#endif /* CONFIG_WNM_AP */
424281806Srpaulo#ifdef CONFIG_HS20
425281806Srpaulo	if (hapd->conf->hs20 && len < 6)
426281806Srpaulo		len = 6;
427281806Srpaulo#endif /* CONFIG_HS20 */
428337817Scy#ifdef CONFIG_MBO
429337817Scy	if (hapd->conf->mbo_enabled && len < 6)
430337817Scy		len = 6;
431337817Scy#endif /* CONFIG_MBO */
432346981Scy#ifdef CONFIG_FILS
433346981Scy	if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
434346981Scy	     !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
435346981Scy		len = 10;
436346981Scy#endif /* CONFIG_FILS */
437346981Scy#ifdef CONFIG_SAE
438346981Scy	if (len < 11 && hapd->conf->wpa &&
439346981Scy	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
440346981Scy	    hostapd_sae_pw_id_in_use(hapd->conf))
441346981Scy		len = 11;
442346981Scy#endif /* CONFIG_SAE */
443281806Srpaulo	if (len < hapd->iface->extended_capa_len)
444281806Srpaulo		len = hapd->iface->extended_capa_len;
445252190Srpaulo	if (len == 0)
446252190Srpaulo		return eid;
447252190Srpaulo
448252190Srpaulo	*pos++ = WLAN_EID_EXT_CAPAB;
449252190Srpaulo	*pos++ = len;
450281806Srpaulo	for (i = 0; i < len; i++, pos++) {
451281806Srpaulo		hostapd_ext_capab_byte(hapd, pos, i);
452252190Srpaulo
453281806Srpaulo		if (i < hapd->iface->extended_capa_len) {
454281806Srpaulo			*pos &= ~hapd->iface->extended_capa_mask[i];
455281806Srpaulo			*pos |= hapd->iface->extended_capa[i];
456281806Srpaulo		}
457281806Srpaulo	}
458252190Srpaulo
459281806Srpaulo	while (len > 0 && eid[1 + len] == 0) {
460281806Srpaulo		len--;
461281806Srpaulo		eid[1] = len;
462281806Srpaulo	}
463281806Srpaulo	if (len == 0)
464281806Srpaulo		return eid;
465252190Srpaulo
466281806Srpaulo	return eid + 2 + len;
467281806Srpaulo}
468252190Srpaulo
469252190Srpaulo
470281806Srpaulou8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
471281806Srpaulo{
472281806Srpaulo	u8 *pos = eid;
473281806Srpaulo	u8 len = hapd->conf->qos_map_set_len;
474252190Srpaulo
475281806Srpaulo	if (!len)
476281806Srpaulo		return eid;
477281806Srpaulo
478281806Srpaulo	*pos++ = WLAN_EID_QOS_MAP_SET;
479281806Srpaulo	*pos++ = len;
480281806Srpaulo	os_memcpy(pos, hapd->conf->qos_map_set, len);
481281806Srpaulo	pos += len;
482281806Srpaulo
483252190Srpaulo	return pos;
484252190Srpaulo}
485252190Srpaulo
486252190Srpaulo
487252190Srpaulou8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
488252190Srpaulo{
489252190Srpaulo	u8 *pos = eid;
490252190Srpaulo#ifdef CONFIG_INTERWORKING
491252190Srpaulo	u8 *len;
492252190Srpaulo
493252190Srpaulo	if (!hapd->conf->interworking)
494252190Srpaulo		return eid;
495252190Srpaulo
496252190Srpaulo	*pos++ = WLAN_EID_INTERWORKING;
497252190Srpaulo	len = pos++;
498252190Srpaulo
499252190Srpaulo	*pos = hapd->conf->access_network_type;
500252190Srpaulo	if (hapd->conf->internet)
501252190Srpaulo		*pos |= INTERWORKING_ANO_INTERNET;
502252190Srpaulo	if (hapd->conf->asra)
503252190Srpaulo		*pos |= INTERWORKING_ANO_ASRA;
504252190Srpaulo	if (hapd->conf->esr)
505252190Srpaulo		*pos |= INTERWORKING_ANO_ESR;
506252190Srpaulo	if (hapd->conf->uesa)
507252190Srpaulo		*pos |= INTERWORKING_ANO_UESA;
508252190Srpaulo	pos++;
509252190Srpaulo
510252190Srpaulo	if (hapd->conf->venue_info_set) {
511252190Srpaulo		*pos++ = hapd->conf->venue_group;
512252190Srpaulo		*pos++ = hapd->conf->venue_type;
513252190Srpaulo	}
514252190Srpaulo
515252190Srpaulo	if (!is_zero_ether_addr(hapd->conf->hessid)) {
516252190Srpaulo		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
517252190Srpaulo		pos += ETH_ALEN;
518252190Srpaulo	}
519252190Srpaulo
520252190Srpaulo	*len = pos - len - 1;
521252190Srpaulo#endif /* CONFIG_INTERWORKING */
522252190Srpaulo
523252190Srpaulo	return pos;
524252190Srpaulo}
525252190Srpaulo
526252190Srpaulo
527252190Srpaulou8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
528252190Srpaulo{
529252190Srpaulo	u8 *pos = eid;
530252190Srpaulo#ifdef CONFIG_INTERWORKING
531252190Srpaulo
532252190Srpaulo	/* TODO: Separate configuration for ANQP? */
533252190Srpaulo	if (!hapd->conf->interworking)
534252190Srpaulo		return eid;
535252190Srpaulo
536252190Srpaulo	*pos++ = WLAN_EID_ADV_PROTO;
537252190Srpaulo	*pos++ = 2;
538252190Srpaulo	*pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
539252190Srpaulo	*pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
540252190Srpaulo#endif /* CONFIG_INTERWORKING */
541252190Srpaulo
542252190Srpaulo	return pos;
543252190Srpaulo}
544252190Srpaulo
545252190Srpaulo
546252190Srpaulou8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
547252190Srpaulo{
548252190Srpaulo	u8 *pos = eid;
549252190Srpaulo#ifdef CONFIG_INTERWORKING
550252190Srpaulo	u8 *len;
551252190Srpaulo	unsigned int i, count;
552252190Srpaulo
553252190Srpaulo	if (!hapd->conf->interworking ||
554252190Srpaulo	    hapd->conf->roaming_consortium == NULL ||
555252190Srpaulo	    hapd->conf->roaming_consortium_count == 0)
556252190Srpaulo		return eid;
557252190Srpaulo
558252190Srpaulo	*pos++ = WLAN_EID_ROAMING_CONSORTIUM;
559252190Srpaulo	len = pos++;
560252190Srpaulo
561252190Srpaulo	/* Number of ANQP OIs (in addition to the max 3 listed here) */
562252190Srpaulo	if (hapd->conf->roaming_consortium_count > 3 + 255)
563252190Srpaulo		*pos++ = 255;
564252190Srpaulo	else if (hapd->conf->roaming_consortium_count > 3)
565252190Srpaulo		*pos++ = hapd->conf->roaming_consortium_count - 3;
566252190Srpaulo	else
567252190Srpaulo		*pos++ = 0;
568252190Srpaulo
569252190Srpaulo	/* OU #1 and #2 Lengths */
570252190Srpaulo	*pos = hapd->conf->roaming_consortium[0].len;
571252190Srpaulo	if (hapd->conf->roaming_consortium_count > 1)
572252190Srpaulo		*pos |= hapd->conf->roaming_consortium[1].len << 4;
573252190Srpaulo	pos++;
574252190Srpaulo
575252190Srpaulo	if (hapd->conf->roaming_consortium_count > 3)
576252190Srpaulo		count = 3;
577252190Srpaulo	else
578252190Srpaulo		count = hapd->conf->roaming_consortium_count;
579252190Srpaulo
580252190Srpaulo	for (i = 0; i < count; i++) {
581252190Srpaulo		os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
582252190Srpaulo			  hapd->conf->roaming_consortium[i].len);
583252190Srpaulo		pos += hapd->conf->roaming_consortium[i].len;
584252190Srpaulo	}
585252190Srpaulo
586252190Srpaulo	*len = pos - len - 1;
587252190Srpaulo#endif /* CONFIG_INTERWORKING */
588252190Srpaulo
589252190Srpaulo	return pos;
590252190Srpaulo}
591252190Srpaulo
592252190Srpaulo
593252190Srpaulou8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
594252190Srpaulo{
595252190Srpaulo	if (hapd->conf->time_advertisement != 2)
596252190Srpaulo		return eid;
597252190Srpaulo
598252190Srpaulo	if (hapd->time_adv == NULL &&
599252190Srpaulo	    hostapd_update_time_adv(hapd) < 0)
600252190Srpaulo		return eid;
601252190Srpaulo
602252190Srpaulo	if (hapd->time_adv == NULL)
603252190Srpaulo		return eid;
604252190Srpaulo
605252190Srpaulo	os_memcpy(eid, wpabuf_head(hapd->time_adv),
606252190Srpaulo		  wpabuf_len(hapd->time_adv));
607252190Srpaulo	eid += wpabuf_len(hapd->time_adv);
608252190Srpaulo
609252190Srpaulo	return eid;
610252190Srpaulo}
611252190Srpaulo
612252190Srpaulo
613252190Srpaulou8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
614252190Srpaulo{
615252190Srpaulo	size_t len;
616252190Srpaulo
617346981Scy	if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
618252190Srpaulo		return eid;
619252190Srpaulo
620252190Srpaulo	len = os_strlen(hapd->conf->time_zone);
621252190Srpaulo
622252190Srpaulo	*eid++ = WLAN_EID_TIME_ZONE;
623252190Srpaulo	*eid++ = len;
624252190Srpaulo	os_memcpy(eid, hapd->conf->time_zone, len);
625252190Srpaulo	eid += len;
626252190Srpaulo
627252190Srpaulo	return eid;
628252190Srpaulo}
629252190Srpaulo
630252190Srpaulo
631252190Srpauloint hostapd_update_time_adv(struct hostapd_data *hapd)
632252190Srpaulo{
633252190Srpaulo	const int elen = 2 + 1 + 10 + 5 + 1;
634252190Srpaulo	struct os_time t;
635252190Srpaulo	struct os_tm tm;
636252190Srpaulo	u8 *pos;
637252190Srpaulo
638252190Srpaulo	if (hapd->conf->time_advertisement != 2)
639252190Srpaulo		return 0;
640252190Srpaulo
641252190Srpaulo	if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
642252190Srpaulo		return -1;
643252190Srpaulo
644252190Srpaulo	if (!hapd->time_adv) {
645252190Srpaulo		hapd->time_adv = wpabuf_alloc(elen);
646252190Srpaulo		if (hapd->time_adv == NULL)
647252190Srpaulo			return -1;
648252190Srpaulo		pos = wpabuf_put(hapd->time_adv, elen);
649252190Srpaulo	} else
650252190Srpaulo		pos = wpabuf_mhead_u8(hapd->time_adv);
651252190Srpaulo
652252190Srpaulo	*pos++ = WLAN_EID_TIME_ADVERTISEMENT;
653252190Srpaulo	*pos++ = 1 + 10 + 5 + 1;
654252190Srpaulo
655252190Srpaulo	*pos++ = 2; /* UTC time at which the TSF timer is 0 */
656252190Srpaulo
657252190Srpaulo	/* Time Value at TSF 0 */
658252190Srpaulo	/* FIX: need to calculate this based on the current TSF value */
659252190Srpaulo	WPA_PUT_LE16(pos, tm.year); /* Year */
660252190Srpaulo	pos += 2;
661252190Srpaulo	*pos++ = tm.month; /* Month */
662252190Srpaulo	*pos++ = tm.day; /* Day of month */
663252190Srpaulo	*pos++ = tm.hour; /* Hours */
664252190Srpaulo	*pos++ = tm.min; /* Minutes */
665252190Srpaulo	*pos++ = tm.sec; /* Seconds */
666252190Srpaulo	WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
667252190Srpaulo	pos += 2;
668252190Srpaulo	*pos++ = 0; /* Reserved */
669252190Srpaulo
670252190Srpaulo	/* Time Error */
671252190Srpaulo	/* TODO: fill in an estimate on the error */
672252190Srpaulo	*pos++ = 0;
673252190Srpaulo	*pos++ = 0;
674252190Srpaulo	*pos++ = 0;
675252190Srpaulo	*pos++ = 0;
676252190Srpaulo	*pos++ = 0;
677252190Srpaulo
678252190Srpaulo	*pos++ = hapd->time_update_counter++;
679252190Srpaulo
680252190Srpaulo	return 0;
681252190Srpaulo}
682252190Srpaulo
683252190Srpaulo
684252190Srpaulou8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
685252190Srpaulo{
686252190Srpaulo	u8 *pos = eid;
687252190Srpaulo
688346981Scy#ifdef CONFIG_WNM_AP
689252190Srpaulo	if (hapd->conf->ap_max_inactivity > 0) {
690252190Srpaulo		unsigned int val;
691252190Srpaulo		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
692252190Srpaulo		*pos++ = 3;
693252190Srpaulo		val = hapd->conf->ap_max_inactivity;
694252190Srpaulo		if (val > 68000)
695252190Srpaulo			val = 68000;
696252190Srpaulo		val *= 1000;
697252190Srpaulo		val /= 1024;
698252190Srpaulo		if (val == 0)
699252190Srpaulo			val = 1;
700252190Srpaulo		if (val > 65535)
701252190Srpaulo			val = 65535;
702252190Srpaulo		WPA_PUT_LE16(pos, val);
703252190Srpaulo		pos += 2;
704252190Srpaulo		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
705252190Srpaulo	}
706346981Scy#endif /* CONFIG_WNM_AP */
707252190Srpaulo
708252190Srpaulo	return pos;
709252190Srpaulo}
710337817Scy
711337817Scy
712337817Scy#ifdef CONFIG_MBO
713337817Scy
714346981Scyu8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
715346981Scy				    size_t len, int delta)
716346981Scy{
717346981Scy	u8 mbo[4];
718346981Scy
719346981Scy	mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
720346981Scy	mbo[1] = 2;
721346981Scy	/* Delta RSSI */
722346981Scy	mbo[2] = delta;
723346981Scy	/* Retry delay */
724346981Scy	mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
725346981Scy
726346981Scy	return eid + mbo_add_ie(eid, len, mbo, 4);
727346981Scy}
728346981Scy
729346981Scy
730337817Scyu8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
731337817Scy{
732346981Scy	u8 mbo[9], *mbo_pos = mbo;
733337817Scy	u8 *pos = eid;
734337817Scy
735346981Scy	if (!hapd->conf->mbo_enabled &&
736346981Scy	    !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
737337817Scy		return eid;
738337817Scy
739346981Scy	if (hapd->conf->mbo_enabled) {
740346981Scy		*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
741346981Scy		*mbo_pos++ = 1;
742346981Scy		/* Not Cellular aware */
743346981Scy		*mbo_pos++ = 0;
744346981Scy	}
745337817Scy
746346981Scy	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
747337817Scy		*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
748337817Scy		*mbo_pos++ = 1;
749337817Scy		*mbo_pos++ = hapd->mbo_assoc_disallow;
750337817Scy	}
751337817Scy
752346981Scy	if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
753346981Scy		u8 ctrl;
754346981Scy
755346981Scy		ctrl = OCE_RELEASE;
756346981Scy		if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
757346981Scy			ctrl |= OCE_IS_STA_CFON;
758346981Scy
759346981Scy		*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
760346981Scy		*mbo_pos++ = 1;
761346981Scy		*mbo_pos++ = ctrl;
762346981Scy	}
763346981Scy
764337817Scy	pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
765337817Scy
766337817Scy	return pos;
767337817Scy}
768337817Scy
769337817Scy
770337817Scyu8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
771337817Scy{
772346981Scy	u8 len;
773346981Scy
774346981Scy	if (!hapd->conf->mbo_enabled &&
775346981Scy	    !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
776337817Scy		return 0;
777337817Scy
778337817Scy	/*
779337817Scy	 * MBO IE header (6) + Capability Indication attribute (3) +
780337817Scy	 * Association Disallowed attribute (3) = 12
781337817Scy	 */
782346981Scy	len = 6;
783346981Scy	if (hapd->conf->mbo_enabled)
784346981Scy		len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
785346981Scy
786346981Scy	/* OCE capability indication attribute (3) */
787346981Scy	if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
788346981Scy		len += 3;
789346981Scy
790346981Scy	return len;
791337817Scy}
792337817Scy
793337817Scy#endif /* CONFIG_MBO */
794337817Scy
795337817Scy
796346981Scy#ifdef CONFIG_OWE
797346981Scystatic int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
798346981Scy{
799346981Scy	return hapd->conf->owe_transition_ssid_len > 0 &&
800346981Scy		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
801346981Scy}
802346981Scy#endif /* CONFIG_OWE */
803346981Scy
804346981Scy
805346981Scysize_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
806346981Scy{
807346981Scy#ifdef CONFIG_OWE
808346981Scy	if (!hostapd_eid_owe_trans_enabled(hapd))
809346981Scy		return 0;
810346981Scy	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
811346981Scy#else /* CONFIG_OWE */
812346981Scy	return 0;
813346981Scy#endif /* CONFIG_OWE */
814346981Scy}
815346981Scy
816346981Scy
817346981Scyu8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
818346981Scy				  size_t len)
819346981Scy{
820346981Scy#ifdef CONFIG_OWE
821346981Scy	u8 *pos = eid;
822346981Scy	size_t elen;
823346981Scy
824346981Scy	if (hapd->conf->owe_transition_ifname[0] &&
825346981Scy	    !hostapd_eid_owe_trans_enabled(hapd))
826346981Scy		hostapd_owe_trans_get_info(hapd);
827346981Scy
828346981Scy	if (!hostapd_eid_owe_trans_enabled(hapd))
829346981Scy		return pos;
830346981Scy
831346981Scy	elen = hostapd_eid_owe_trans_len(hapd);
832346981Scy	if (len < elen) {
833346981Scy		wpa_printf(MSG_DEBUG,
834346981Scy			   "OWE: Not enough room in the buffer for OWE IE");
835346981Scy		return pos;
836346981Scy	}
837346981Scy
838346981Scy	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
839346981Scy	*pos++ = elen - 2;
840346981Scy	WPA_PUT_BE24(pos, OUI_WFA);
841346981Scy	pos += 3;
842346981Scy	*pos++ = OWE_OUI_TYPE;
843346981Scy	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
844346981Scy	pos += ETH_ALEN;
845346981Scy	*pos++ = hapd->conf->owe_transition_ssid_len;
846346981Scy	os_memcpy(pos, hapd->conf->owe_transition_ssid,
847346981Scy		  hapd->conf->owe_transition_ssid_len);
848346981Scy	pos += hapd->conf->owe_transition_ssid_len;
849346981Scy
850346981Scy	return pos;
851346981Scy#else /* CONFIG_OWE */
852346981Scy	return eid;
853346981Scy#endif /* CONFIG_OWE */
854346981Scy}
855346981Scy
856346981Scy
857337817Scyvoid ap_copy_sta_supp_op_classes(struct sta_info *sta,
858337817Scy				 const u8 *supp_op_classes,
859337817Scy				 size_t supp_op_classes_len)
860337817Scy{
861337817Scy	if (!supp_op_classes)
862337817Scy		return;
863337817Scy	os_free(sta->supp_op_classes);
864337817Scy	sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
865337817Scy	if (!sta->supp_op_classes)
866337817Scy		return;
867337817Scy
868337817Scy	sta->supp_op_classes[0] = supp_op_classes_len;
869337817Scy	os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
870337817Scy		  supp_op_classes_len);
871337817Scy}
872346981Scy
873346981Scy
874346981Scyu8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
875346981Scy{
876346981Scy	u8 *pos = eid;
877346981Scy#ifdef CONFIG_FILS
878346981Scy	u8 *len;
879346981Scy	u16 fils_info = 0;
880346981Scy	size_t realms;
881346981Scy	struct fils_realm *realm;
882346981Scy
883346981Scy	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
884346981Scy	    !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
885346981Scy		return pos;
886346981Scy
887346981Scy	realms = dl_list_len(&hapd->conf->fils_realms);
888346981Scy	if (realms > 7)
889346981Scy		realms = 7; /* 3 bit count field limits this to max 7 */
890346981Scy
891346981Scy	*pos++ = WLAN_EID_FILS_INDICATION;
892346981Scy	len = pos++;
893346981Scy	/* TODO: B0..B2: Number of Public Key Identifiers */
894346981Scy	if (hapd->conf->erp_domain) {
895346981Scy		/* B3..B5: Number of Realm Identifiers */
896346981Scy		fils_info |= realms << 3;
897346981Scy	}
898346981Scy	/* TODO: B6: FILS IP Address Configuration */
899346981Scy	if (hapd->conf->fils_cache_id_set)
900346981Scy		fils_info |= BIT(7);
901346981Scy	if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
902346981Scy		fils_info |= BIT(8); /* HESSID Included */
903346981Scy	/* FILS Shared Key Authentication without PFS Supported */
904346981Scy	fils_info |= BIT(9);
905346981Scy	if (hapd->conf->fils_dh_group) {
906346981Scy		/* FILS Shared Key Authentication with PFS Supported */
907346981Scy		fils_info |= BIT(10);
908346981Scy	}
909346981Scy	/* TODO: B11: FILS Public Key Authentication Supported */
910346981Scy	/* B12..B15: Reserved */
911346981Scy	WPA_PUT_LE16(pos, fils_info);
912346981Scy	pos += 2;
913346981Scy	if (hapd->conf->fils_cache_id_set) {
914346981Scy		os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
915346981Scy		pos += FILS_CACHE_ID_LEN;
916346981Scy	}
917346981Scy	if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
918346981Scy		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
919346981Scy		pos += ETH_ALEN;
920346981Scy	}
921346981Scy
922346981Scy	dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
923346981Scy			 list) {
924346981Scy		if (realms == 0)
925346981Scy			break;
926346981Scy		realms--;
927346981Scy		os_memcpy(pos, realm->hash, 2);
928346981Scy		pos += 2;
929346981Scy	}
930346981Scy	*len = pos - len - 1;
931346981Scy#endif /* CONFIG_FILS */
932346981Scy
933346981Scy	return pos;
934346981Scy}
935346981Scy
936346981Scy
937346981Scy#ifdef CONFIG_OCV
938346981Scyint get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
939346981Scy		      int ap_seg1_idx, int *bandwidth, int *seg1_idx)
940346981Scy{
941346981Scy	int ht_40mhz = 0;
942346981Scy	int vht_80p80 = 0;
943346981Scy	int requested_bw;
944346981Scy
945346981Scy	if (sta->ht_capabilities)
946346981Scy		ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
947346981Scy			      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
948346981Scy
949346981Scy	if (sta->vht_operation) {
950346981Scy		struct ieee80211_vht_operation *oper = sta->vht_operation;
951346981Scy
952346981Scy		/*
953346981Scy		 * If a VHT Operation element was present, use it to determine
954346981Scy		 * the supported channel bandwidth.
955346981Scy		 */
956346981Scy		if (oper->vht_op_info_chwidth == 0) {
957346981Scy			requested_bw = ht_40mhz ? 40 : 20;
958346981Scy		} else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
959346981Scy			requested_bw = 80;
960346981Scy		} else {
961346981Scy			int diff;
962346981Scy
963346981Scy			requested_bw = 160;
964346981Scy			diff = abs((int)
965346981Scy				   oper->vht_op_info_chan_center_freq_seg0_idx -
966346981Scy				   (int)
967346981Scy				   oper->vht_op_info_chan_center_freq_seg1_idx);
968346981Scy			vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
969346981Scy				!= 0 &&	diff > 16;
970346981Scy		}
971346981Scy	} else if (sta->vht_capabilities) {
972346981Scy		struct ieee80211_vht_capabilities *capab;
973346981Scy		int vht_chanwidth;
974346981Scy
975346981Scy		capab = sta->vht_capabilities;
976346981Scy
977346981Scy		/*
978346981Scy		 * If only the VHT Capabilities element is present (e.g., for
979346981Scy		 * normal clients), use it to determine the supported channel
980346981Scy		 * bandwidth.
981346981Scy		 */
982346981Scy		vht_chanwidth = capab->vht_capabilities_info &
983346981Scy			VHT_CAP_SUPP_CHAN_WIDTH_MASK;
984346981Scy		vht_80p80 = capab->vht_capabilities_info &
985346981Scy			VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
986346981Scy
987346981Scy		/* TODO: Also take into account Extended NSS BW Support field */
988346981Scy		requested_bw = vht_chanwidth ? 160 : 80;
989346981Scy	} else {
990346981Scy		requested_bw = ht_40mhz ? 40 : 20;
991346981Scy	}
992346981Scy
993346981Scy	*bandwidth = requested_bw < ap_max_chanwidth ?
994346981Scy		requested_bw : ap_max_chanwidth;
995346981Scy
996346981Scy	*seg1_idx = 0;
997346981Scy	if (ap_seg1_idx && vht_80p80)
998346981Scy		*seg1_idx = ap_seg1_idx;
999346981Scy
1000346981Scy	return 0;
1001346981Scy}
1002346981Scy#endif /* CONFIG_OCV */
1003