/src/hostap/src/ap/beacon.c
Line | Count | Source |
1 | | /* |
2 | | * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response |
3 | | * Copyright (c) 2002-2004, Instant802 Networks, Inc. |
4 | | * Copyright (c) 2005-2006, Devicescape Software, Inc. |
5 | | * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> |
6 | | * |
7 | | * This software may be distributed under the terms of the BSD license. |
8 | | * See README for more details. |
9 | | */ |
10 | | |
11 | | #include "utils/includes.h" |
12 | | |
13 | | #ifndef CONFIG_NATIVE_WINDOWS |
14 | | |
15 | | #include "utils/common.h" |
16 | | #include "common/ieee802_11_defs.h" |
17 | | #include "common/ieee802_11_common.h" |
18 | | #include "common/hw_features_common.h" |
19 | | #include "common/wpa_ctrl.h" |
20 | | #include "crypto/sha1.h" |
21 | | #include "wps/wps_defs.h" |
22 | | #include "p2p/p2p.h" |
23 | | #include "hostapd.h" |
24 | | #include "ieee802_11.h" |
25 | | #include "wpa_auth.h" |
26 | | #include "wmm.h" |
27 | | #include "ap_config.h" |
28 | | #include "sta_info.h" |
29 | | #include "p2p_hostapd.h" |
30 | | #include "ap_drv_ops.h" |
31 | | #include "beacon.h" |
32 | | #include "hs20.h" |
33 | | #include "dfs.h" |
34 | | #include "taxonomy.h" |
35 | | #include "ieee802_11_auth.h" |
36 | | |
37 | | |
38 | | #ifdef NEED_AP_MLME |
39 | | |
40 | | static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) |
41 | 0 | { |
42 | 0 | if (len < 2 + 5) |
43 | 0 | return eid; |
44 | | |
45 | | #ifdef CONFIG_TESTING_OPTIONS |
46 | | if (hapd->conf->bss_load_test_set) { |
47 | | *eid++ = WLAN_EID_BSS_LOAD; |
48 | | *eid++ = 5; |
49 | | os_memcpy(eid, hapd->conf->bss_load_test, 5); |
50 | | eid += 5; |
51 | | return eid; |
52 | | } |
53 | | #endif /* CONFIG_TESTING_OPTIONS */ |
54 | 0 | if (hapd->conf->bss_load_update_period) { |
55 | 0 | *eid++ = WLAN_EID_BSS_LOAD; |
56 | 0 | *eid++ = 5; |
57 | 0 | WPA_PUT_LE16(eid, hapd->num_sta); |
58 | 0 | eid += 2; |
59 | 0 | *eid++ = hapd->iface->channel_utilization; |
60 | 0 | WPA_PUT_LE16(eid, 0); /* no available admission capabity */ |
61 | 0 | eid += 2; |
62 | 0 | } |
63 | 0 | return eid; |
64 | 0 | } |
65 | | |
66 | | |
67 | | static u8 ieee802_11_erp_info(struct hostapd_data *hapd) |
68 | 0 | { |
69 | 0 | u8 erp = 0; |
70 | |
|
71 | 0 | if (hapd->iface->current_mode == NULL || |
72 | 0 | hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) |
73 | 0 | return 0; |
74 | | |
75 | 0 | if (hapd->iface->olbc) |
76 | 0 | erp |= ERP_INFO_USE_PROTECTION; |
77 | 0 | if (hapd->iface->num_sta_non_erp > 0) { |
78 | 0 | erp |= ERP_INFO_NON_ERP_PRESENT | |
79 | 0 | ERP_INFO_USE_PROTECTION; |
80 | 0 | } |
81 | 0 | if (hapd->iface->num_sta_no_short_preamble > 0 || |
82 | 0 | hapd->iconf->preamble == LONG_PREAMBLE) |
83 | 0 | erp |= ERP_INFO_BARKER_PREAMBLE_MODE; |
84 | |
|
85 | 0 | return erp; |
86 | 0 | } |
87 | | |
88 | | |
89 | | static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) |
90 | 0 | { |
91 | 0 | enum hostapd_hw_mode hw_mode = hapd->iconf->hw_mode; |
92 | |
|
93 | 0 | if (hw_mode != HOSTAPD_MODE_IEEE80211G && |
94 | 0 | hw_mode != HOSTAPD_MODE_IEEE80211B) |
95 | 0 | return eid; |
96 | | |
97 | 0 | *eid++ = WLAN_EID_DS_PARAMS; |
98 | 0 | *eid++ = 1; |
99 | 0 | *eid++ = hapd->iconf->channel; |
100 | 0 | return eid; |
101 | 0 | } |
102 | | |
103 | | |
104 | | static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) |
105 | 0 | { |
106 | 0 | if (hapd->iface->current_mode == NULL || |
107 | 0 | hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) |
108 | 0 | return eid; |
109 | | |
110 | | /* Set NonERP_present and use_protection bits if there |
111 | | * are any associated NonERP stations. */ |
112 | | /* TODO: use_protection bit can be set to zero even if |
113 | | * there are NonERP stations present. This optimization |
114 | | * might be useful if NonERP stations are "quiet". |
115 | | * See 802.11g/D6 E-1 for recommended practice. |
116 | | * In addition, Non ERP present might be set, if AP detects Non ERP |
117 | | * operation on other APs. */ |
118 | | |
119 | | /* Add ERP Information element */ |
120 | 0 | *eid++ = WLAN_EID_ERP_INFO; |
121 | 0 | *eid++ = 1; |
122 | 0 | *eid++ = ieee802_11_erp_info(hapd); |
123 | |
|
124 | 0 | return eid; |
125 | 0 | } |
126 | | |
127 | | |
128 | | static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid) |
129 | 0 | { |
130 | 0 | u8 *pos = eid; |
131 | 0 | u8 local_pwr_constraint = 0; |
132 | 0 | int dfs; |
133 | |
|
134 | 0 | if (hapd->iface->current_mode == NULL || |
135 | 0 | hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) |
136 | 0 | return eid; |
137 | | |
138 | | /* Let host drivers add this IE if DFS support is offloaded */ |
139 | 0 | if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) |
140 | 0 | return eid; |
141 | | |
142 | | /* |
143 | | * There is no DFS support and power constraint was not directly |
144 | | * requested by config option. |
145 | | */ |
146 | 0 | if (!hapd->iconf->ieee80211h && |
147 | 0 | hapd->iconf->local_pwr_constraint == -1) |
148 | 0 | return eid; |
149 | | |
150 | | /* Check if DFS is required by regulatory. */ |
151 | 0 | dfs = hostapd_is_dfs_required(hapd->iface); |
152 | 0 | if (dfs < 0) { |
153 | 0 | wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", |
154 | 0 | dfs); |
155 | 0 | dfs = 0; |
156 | 0 | } |
157 | |
|
158 | 0 | if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1) |
159 | 0 | return eid; |
160 | | |
161 | | /* |
162 | | * ieee80211h (DFS) is enabled so Power Constraint element shall |
163 | | * be added when running on DFS channel whenever local_pwr_constraint |
164 | | * is configured or not. In order to meet regulations when TPC is not |
165 | | * implemented using a transmit power that is below the legal maximum |
166 | | * (including any mitigation factor) should help. In this case, |
167 | | * indicate 3 dB below maximum allowed transmit power. |
168 | | */ |
169 | 0 | if (hapd->iconf->local_pwr_constraint == -1) |
170 | 0 | local_pwr_constraint = 3; |
171 | | |
172 | | /* |
173 | | * A STA that is not an AP shall use a transmit power less than or |
174 | | * equal to the local maximum transmit power level for the channel. |
175 | | * The local maximum transmit power can be calculated from the formula: |
176 | | * local max TX pwr = max TX pwr - local pwr constraint |
177 | | * Where max TX pwr is maximum transmit power level specified for |
178 | | * channel in Country element and local pwr constraint is specified |
179 | | * for channel in this Power Constraint element. |
180 | | */ |
181 | | |
182 | | /* Element ID */ |
183 | 0 | *pos++ = WLAN_EID_PWR_CONSTRAINT; |
184 | | /* Length */ |
185 | 0 | *pos++ = 1; |
186 | | /* Local Power Constraint */ |
187 | 0 | if (local_pwr_constraint) |
188 | 0 | *pos++ = local_pwr_constraint; |
189 | 0 | else |
190 | 0 | *pos++ = hapd->iconf->local_pwr_constraint; |
191 | |
|
192 | 0 | return pos; |
193 | 0 | } |
194 | | |
195 | | |
196 | | static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos, |
197 | | u8 *end, int chan_spacing, |
198 | | struct hostapd_channel_data *start, |
199 | | struct hostapd_channel_data *prev) |
200 | 0 | { |
201 | 0 | if (end - pos < 3) |
202 | 0 | return pos; |
203 | | |
204 | | /* first channel number */ |
205 | 0 | *pos++ = start->chan; |
206 | | /* number of channels */ |
207 | 0 | *pos++ = (prev->chan - start->chan) / chan_spacing + 1; |
208 | | /* maximum transmit power level */ |
209 | 0 | if (!is_6ghz_op_class(hapd->iconf->op_class)) |
210 | 0 | *pos++ = start->max_tx_power; |
211 | 0 | else |
212 | 0 | *pos++ = 0; /* Reserved when operating on the 6 GHz band */ |
213 | |
|
214 | 0 | return pos; |
215 | 0 | } |
216 | | |
217 | | |
218 | | static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos, |
219 | | u8 *end) |
220 | 0 | { |
221 | 0 | int i; |
222 | 0 | struct hostapd_hw_modes *mode; |
223 | 0 | struct hostapd_channel_data *start, *prev; |
224 | 0 | int chan_spacing = 1; |
225 | |
|
226 | 0 | mode = hapd->iface->current_mode; |
227 | 0 | if (mode->mode == HOSTAPD_MODE_IEEE80211A) |
228 | 0 | chan_spacing = 4; |
229 | |
|
230 | 0 | start = prev = NULL; |
231 | 0 | for (i = 0; i < mode->num_channels; i++) { |
232 | 0 | struct hostapd_channel_data *chan = &mode->channels[i]; |
233 | 0 | if (chan->flag & HOSTAPD_CHAN_DISABLED) |
234 | 0 | continue; |
235 | 0 | if (start && prev && |
236 | 0 | prev->chan + chan_spacing == chan->chan && |
237 | 0 | start->max_tx_power == chan->max_tx_power) { |
238 | 0 | prev = chan; |
239 | 0 | continue; /* can use same entry */ |
240 | 0 | } |
241 | | |
242 | 0 | if (start && prev) |
243 | 0 | pos = hostapd_eid_country_add(hapd, pos, end, |
244 | 0 | chan_spacing, |
245 | 0 | start, prev); |
246 | | |
247 | | /* Start new group */ |
248 | 0 | start = prev = chan; |
249 | 0 | } |
250 | |
|
251 | 0 | if (start) { |
252 | 0 | pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing, |
253 | 0 | start, prev); |
254 | 0 | } |
255 | |
|
256 | 0 | return pos; |
257 | 0 | } |
258 | | |
259 | | |
260 | | static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, |
261 | | int max_len) |
262 | 0 | { |
263 | 0 | u8 *pos = eid; |
264 | 0 | u8 *end = eid + max_len; |
265 | 0 | bool force_global; |
266 | |
|
267 | 0 | if (!hapd->iconf->ieee80211d || max_len < 6 || |
268 | 0 | hapd->iface->current_mode == NULL) |
269 | 0 | return eid; |
270 | | |
271 | 0 | *pos++ = WLAN_EID_COUNTRY; |
272 | 0 | pos++; /* length will be set later */ |
273 | 0 | os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ |
274 | 0 | pos += 3; |
275 | | |
276 | | /* The 6 GHz band uses global operating classes */ |
277 | 0 | force_global = is_6ghz_op_class(hapd->iconf->op_class); |
278 | |
|
279 | | #ifdef CONFIG_MBO |
280 | | /* Wi-Fi Agile Muiltiband AP is required to use a global operating |
281 | | * class. */ |
282 | | if (hapd->conf->mbo_enabled) |
283 | | force_global = true; |
284 | | #endif /* CONFIG_MBO */ |
285 | |
|
286 | 0 | if (force_global) { |
287 | | /* Force the third octet of the country string to indicate |
288 | | * Global Operating Class (Table E-4) */ |
289 | 0 | eid[4] = 0x04; |
290 | 0 | } |
291 | |
|
292 | 0 | if (is_6ghz_op_class(hapd->iconf->op_class)) { |
293 | | /* Operating Triplet field */ |
294 | | /* Operating Extension Identifier (>= 201 to indicate this is |
295 | | * not a Subband Triplet field) */ |
296 | 0 | *pos++ = 201; |
297 | | /* Operating Class */ |
298 | 0 | *pos++ = hapd->iconf->op_class; |
299 | | /* Coverage Class */ |
300 | 0 | *pos++ = 0; |
301 | | /* Subband Triplets are required only for the 20 MHz case */ |
302 | 0 | if (hapd->iconf->op_class == 131 || |
303 | 0 | hapd->iconf->op_class == 136) |
304 | 0 | pos = hostapd_fill_subband_triplets(hapd, pos, end); |
305 | 0 | } else { |
306 | 0 | pos = hostapd_fill_subband_triplets(hapd, pos, end); |
307 | 0 | } |
308 | |
|
309 | 0 | if ((pos - eid) & 1) { |
310 | 0 | if (end - pos < 1) |
311 | 0 | return eid; |
312 | 0 | *pos++ = 0; /* pad for 16-bit alignment */ |
313 | 0 | } |
314 | | |
315 | 0 | eid[1] = (pos - eid) - 2; |
316 | |
|
317 | 0 | return pos; |
318 | 0 | } |
319 | | |
320 | | |
321 | | const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid) |
322 | 0 | { |
323 | 0 | const u8 *ies; |
324 | 0 | size_t ies_len; |
325 | |
|
326 | 0 | ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len); |
327 | 0 | if (!ies) |
328 | 0 | return NULL; |
329 | | |
330 | 0 | return get_ie(ies, ies_len, eid); |
331 | 0 | } |
332 | | |
333 | | |
334 | | static const u8 * hostapd_vendor_wpa_ie(struct hostapd_data *hapd, |
335 | | u32 vendor_type) |
336 | 0 | { |
337 | 0 | const u8 *ies; |
338 | 0 | size_t ies_len; |
339 | |
|
340 | 0 | ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len); |
341 | 0 | if (!ies) |
342 | 0 | return NULL; |
343 | | |
344 | 0 | return get_vendor_ie(ies, ies_len, vendor_type); |
345 | 0 | } |
346 | | |
347 | | |
348 | | static u8 * hostapd_get_rsne(struct hostapd_data *hapd, u8 *pos, size_t len) |
349 | 0 | { |
350 | 0 | const u8 *ie; |
351 | |
|
352 | 0 | ie = hostapd_wpa_ie(hapd, WLAN_EID_RSN); |
353 | 0 | if (!ie || 2U + ie[1] > len) |
354 | 0 | return pos; |
355 | | |
356 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
357 | 0 | return pos + 2 + ie[1]; |
358 | 0 | } |
359 | | |
360 | | |
361 | | static u8 * hostapd_get_mde(struct hostapd_data *hapd, u8 *pos, size_t len) |
362 | 0 | { |
363 | 0 | const u8 *ie; |
364 | |
|
365 | 0 | ie = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN); |
366 | 0 | if (!ie || 2U + ie[1] > len) |
367 | 0 | return pos; |
368 | | |
369 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
370 | 0 | return pos + 2 + ie[1]; |
371 | 0 | } |
372 | | |
373 | | |
374 | | static u8 * hostapd_get_rsnxe(struct hostapd_data *hapd, u8 *pos, size_t len) |
375 | 0 | { |
376 | 0 | const u8 *ie; |
377 | |
|
378 | | #ifdef CONFIG_TESTING_OPTIONS |
379 | | if (hapd->conf->no_beacon_rsnxe) { |
380 | | wpa_printf(MSG_INFO, "TESTING: Do not add RSNXE into Beacon"); |
381 | | return pos; |
382 | | } |
383 | | #endif /* CONFIG_TESTING_OPTIONS */ |
384 | 0 | ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); |
385 | 0 | if (!ie || 2U + ie[1] > len) |
386 | 0 | return pos; |
387 | | |
388 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
389 | 0 | return pos + 2 + ie[1]; |
390 | 0 | } |
391 | | |
392 | | |
393 | | static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len) |
394 | 0 | { |
395 | 0 | const u8 *ie; |
396 | |
|
397 | 0 | ie = hostapd_vendor_wpa_ie(hapd, WPA_IE_VENDOR_TYPE); |
398 | 0 | if (!ie || 2U + ie[1] > len) |
399 | 0 | return pos; |
400 | | |
401 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
402 | 0 | return pos + 2 + ie[1]; |
403 | 0 | } |
404 | | |
405 | | |
406 | | static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos, |
407 | | size_t len) |
408 | 0 | { |
409 | 0 | const u8 *ie; |
410 | |
|
411 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE); |
412 | 0 | if (!ie || 2U + ie[1] > len) |
413 | 0 | return pos; |
414 | | |
415 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
416 | 0 | return pos + 2 + ie[1]; |
417 | 0 | } |
418 | | |
419 | | |
420 | | static u8 * hostapd_get_rsne_override_2(struct hostapd_data *hapd, u8 *pos, |
421 | | size_t len) |
422 | 0 | { |
423 | 0 | const u8 *ie; |
424 | |
|
425 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); |
426 | 0 | if (!ie || 2U + ie[1] > len) |
427 | 0 | return pos; |
428 | | |
429 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
430 | 0 | return pos + 2 + ie[1]; |
431 | 0 | } |
432 | | |
433 | | |
434 | | static u8 * hostapd_get_rsnxe_override(struct hostapd_data *hapd, u8 *pos, |
435 | | size_t len) |
436 | 0 | { |
437 | 0 | const u8 *ie; |
438 | |
|
439 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE); |
440 | 0 | if (!ie || 2U + ie[1] > len) |
441 | 0 | return pos; |
442 | | |
443 | 0 | os_memcpy(pos, ie, 2 + ie[1]); |
444 | 0 | return pos + 2 + ie[1]; |
445 | 0 | } |
446 | | |
447 | | |
448 | | static size_t hostapd_get_rsne_override_len(struct hostapd_data *hapd) |
449 | 0 | { |
450 | 0 | const u8 *ie; |
451 | |
|
452 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE); |
453 | 0 | if (!ie) |
454 | 0 | return 0; |
455 | 0 | return 2 + ie[1]; |
456 | 0 | } |
457 | | |
458 | | |
459 | | static size_t hostapd_get_rsne_override_2_len(struct hostapd_data *hapd) |
460 | 0 | { |
461 | 0 | const u8 *ie; |
462 | |
|
463 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); |
464 | 0 | if (!ie) |
465 | 0 | return 0; |
466 | 0 | return 2 + ie[1]; |
467 | 0 | } |
468 | | |
469 | | |
470 | | static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd) |
471 | 0 | { |
472 | 0 | const u8 *ie; |
473 | |
|
474 | 0 | ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE); |
475 | 0 | if (!ie) |
476 | 0 | return 0; |
477 | 0 | return 2 + ie[1]; |
478 | 0 | } |
479 | | |
480 | | |
481 | | static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) |
482 | 0 | { |
483 | | #ifdef CONFIG_TESTING_OPTIONS |
484 | | if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) |
485 | | return eid; |
486 | | #endif /* CONFIG_TESTING_OPTIONS */ |
487 | |
|
488 | 0 | if (!hapd->cs_freq_params.channel) |
489 | 0 | return eid; |
490 | | |
491 | 0 | *eid++ = WLAN_EID_CHANNEL_SWITCH; |
492 | 0 | *eid++ = 3; |
493 | 0 | *eid++ = hapd->cs_block_tx; |
494 | 0 | *eid++ = hapd->cs_freq_params.channel; |
495 | 0 | *eid++ = hapd->cs_count; |
496 | |
|
497 | 0 | return eid; |
498 | 0 | } |
499 | | |
500 | | |
501 | | static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) |
502 | 0 | { |
503 | 0 | if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) |
504 | 0 | return eid; |
505 | | |
506 | | #ifdef CONFIG_TESTING_OPTIONS |
507 | | if (hapd->iconf->csa_ie_only) |
508 | | return eid; |
509 | | #endif /* CONFIG_TESTING_OPTIONS */ |
510 | | |
511 | 0 | *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; |
512 | 0 | *eid++ = 4; |
513 | 0 | *eid++ = hapd->cs_block_tx; |
514 | 0 | *eid++ = hapd->iface->cs_oper_class; |
515 | 0 | *eid++ = hapd->cs_freq_params.channel; |
516 | 0 | *eid++ = hapd->cs_count; |
517 | |
|
518 | 0 | return eid; |
519 | 0 | } |
520 | | |
521 | | |
522 | | static u8 * hostapd_eid_max_cs_time(struct hostapd_data *hapd, u8 *eid) |
523 | 0 | { |
524 | | #ifdef CONFIG_IEEE80211BE |
525 | | u32 switch_time; |
526 | | |
527 | | /* Add Max Channel Switch Time element only if this AP is affiliated |
528 | | * with an AP MLD and channel switch is in process. */ |
529 | | if (!hapd->conf->mld_ap || !hapd->cs_freq_params.channel) |
530 | | return eid; |
531 | | |
532 | | /* Switch time is basically time between CSA count 1 and CSA count |
533 | | * 0 (1 beacon interval) + time for interface restart + time to |
534 | | * send a Beacon frame in the new channel (1 beacon interval). |
535 | | * |
536 | | * TODO: Use dynamic interface restart time. For now, assume 1 sec. |
537 | | */ |
538 | | switch_time = USEC_TO_TU(1000 * 1000) + 2 * hapd->iconf->beacon_int; |
539 | | |
540 | | *eid++ = WLAN_EID_EXTENSION; |
541 | | *eid++ = 4; |
542 | | *eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME; |
543 | | WPA_PUT_LE24(eid, switch_time); |
544 | | eid += 3; |
545 | | #endif /* CONFIG_IEEE80211BE */ |
546 | |
|
547 | 0 | return eid; |
548 | 0 | } |
549 | | |
550 | | |
551 | | static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) |
552 | 0 | { |
553 | 0 | u8 op_class, channel; |
554 | |
|
555 | 0 | if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || |
556 | 0 | !hapd->iface->freq) |
557 | 0 | return eid; |
558 | | |
559 | 0 | if (ieee80211_freq_to_channel_ext(hapd->iface->freq, |
560 | 0 | hapd->iconf->secondary_channel, |
561 | 0 | hostapd_get_oper_chwidth(hapd->iconf), |
562 | 0 | &op_class, &channel) == |
563 | 0 | NUM_HOSTAPD_MODES) |
564 | 0 | return eid; |
565 | | |
566 | 0 | *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; |
567 | 0 | *eid++ = 2; |
568 | | |
569 | | /* Current Operating Class */ |
570 | 0 | *eid++ = op_class; |
571 | | |
572 | | /* TODO: Advertise all the supported operating classes */ |
573 | 0 | *eid++ = 0; |
574 | |
|
575 | 0 | return eid; |
576 | 0 | } |
577 | | |
578 | | |
579 | | static int |
580 | | ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd, |
581 | | struct wpa_driver_ap_params *params) |
582 | 0 | { |
583 | 0 | struct hostapd_iface *iface = hapd->iface; |
584 | 0 | struct hostapd_data *tx_bss; |
585 | 0 | size_t len, rnr_len = 0; |
586 | 0 | u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end; |
587 | 0 | u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL; |
588 | |
|
589 | 0 | if (!iface->mbssid_max_interfaces || |
590 | 0 | iface->num_bss > iface->mbssid_max_interfaces || |
591 | 0 | (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED && |
592 | 0 | !iface->ema_max_periodicity)) |
593 | 0 | goto fail; |
594 | | |
595 | 0 | tx_bss = hostapd_mbssid_get_tx_bss(hapd); |
596 | 0 | len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count, |
597 | 0 | NULL, 0, &rnr_len); |
598 | 0 | if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED && |
599 | 0 | elem_count > iface->ema_max_periodicity)) |
600 | 0 | goto fail; |
601 | | |
602 | 0 | elem = os_zalloc(len); |
603 | 0 | if (!elem) |
604 | 0 | goto fail; |
605 | | |
606 | 0 | elem_offset = os_zalloc(elem_count * sizeof(u8 *)); |
607 | 0 | if (!elem_offset) |
608 | 0 | goto fail; |
609 | | |
610 | 0 | if (rnr_len) { |
611 | 0 | rnr_elem = os_zalloc(rnr_len); |
612 | 0 | if (!rnr_elem) |
613 | 0 | goto fail; |
614 | | |
615 | 0 | rnr_elem_offset = os_calloc(elem_count + 1, sizeof(u8 *)); |
616 | 0 | if (!rnr_elem_offset) |
617 | 0 | goto fail; |
618 | 0 | } |
619 | | |
620 | 0 | end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON, |
621 | 0 | elem_count, elem_offset, NULL, 0, rnr_elem, |
622 | 0 | &rnr_elem_count, rnr_elem_offset, rnr_len); |
623 | |
|
624 | 0 | params->mbssid.mbssid_tx_iface = tx_bss->conf->iface; |
625 | 0 | params->mbssid.mbssid_index = hostapd_mbssid_get_bss_index(hapd); |
626 | 0 | params->mbssid.mbssid_elem = elem; |
627 | 0 | params->mbssid.mbssid_elem_len = end - elem; |
628 | 0 | params->mbssid.mbssid_elem_count = elem_count; |
629 | 0 | params->mbssid.mbssid_elem_offset = elem_offset; |
630 | 0 | params->mbssid.rnr_elem = rnr_elem; |
631 | 0 | params->mbssid.rnr_elem_len = rnr_len; |
632 | 0 | params->mbssid.rnr_elem_count = rnr_elem_count; |
633 | 0 | params->mbssid.rnr_elem_offset = rnr_elem_offset; |
634 | 0 | if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) |
635 | 0 | params->mbssid.ema = true; |
636 | |
|
637 | 0 | params->mbssid.mbssid_tx_iface_linkid = -1; |
638 | | #ifdef CONFIG_IEEE80211BE |
639 | | if (tx_bss->conf->mld_ap) |
640 | | params->mbssid.mbssid_tx_iface_linkid = tx_bss->mld_link_id; |
641 | | #endif /* CONFIG_IEEE80211BE */ |
642 | |
|
643 | 0 | return 0; |
644 | | |
645 | 0 | fail: |
646 | 0 | os_free(rnr_elem); |
647 | 0 | os_free(rnr_elem_offset); |
648 | 0 | os_free(elem_offset); |
649 | 0 | os_free(elem); |
650 | 0 | wpa_printf(MSG_ERROR, "MBSSID: Configuration failed"); |
651 | 0 | return -1; |
652 | 0 | } |
653 | | |
654 | | |
655 | | static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid, |
656 | | u8 mbssid_elem_count) |
657 | 0 | { |
658 | 0 | struct hostapd_iface *iface = hapd->iface; |
659 | |
|
660 | 0 | if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) { |
661 | 0 | *eid++ = WLAN_EID_EXTENSION; |
662 | 0 | *eid++ = 3; |
663 | 0 | *eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION; |
664 | 0 | *eid++ = iface->num_bss; |
665 | 0 | *eid++ = mbssid_elem_count; |
666 | 0 | } |
667 | |
|
668 | 0 | return eid; |
669 | 0 | } |
670 | | |
671 | | |
672 | | static size_t he_elem_len(struct hostapd_data *hapd) |
673 | 0 | { |
674 | 0 | size_t len = 0; |
675 | |
|
676 | | #ifdef CONFIG_IEEE80211AX |
677 | | if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax) |
678 | | return len; |
679 | | |
680 | | len += 3 + sizeof(struct ieee80211_he_capabilities) + |
681 | | 3 + sizeof(struct ieee80211_he_operation) + |
682 | | 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) + |
683 | | 3 + sizeof(struct ieee80211_spatial_reuse); |
684 | | if (is_6ghz_op_class(hapd->iconf->op_class)) { |
685 | | len += sizeof(struct ieee80211_he_6ghz_oper_info) + |
686 | | 3 + sizeof(struct ieee80211_he_6ghz_band_cap); |
687 | | /* An additional Transmit Power Envelope element for |
688 | | * subordinate client */ |
689 | | if (he_reg_is_indoor(hapd->iconf->he_6ghz_reg_pwr_type)) |
690 | | len += 4; |
691 | | |
692 | | /* An additional Transmit Power Envelope element for |
693 | | * default client with unit interpretation of regulatory |
694 | | * client EIRP */ |
695 | | if (hapd->iconf->reg_def_cli_eirp != -1 && |
696 | | he_reg_is_sp(hapd->iconf->he_6ghz_reg_pwr_type)) |
697 | | len += 4; |
698 | | } |
699 | | #endif /* CONFIG_IEEE80211AX */ |
700 | |
|
701 | 0 | return len; |
702 | 0 | } |
703 | | |
704 | | |
705 | | struct probe_resp_params { |
706 | | const struct ieee80211_mgmt *req; |
707 | | bool is_p2p; |
708 | | |
709 | | /* Generated IEs will be included inside an ML element */ |
710 | | struct hostapd_data *mld_ap; |
711 | | struct mld_info *mld_info; |
712 | | |
713 | | struct ieee80211_mgmt *resp; |
714 | | size_t resp_len; |
715 | | u8 *csa_pos; |
716 | | u8 *ecsa_pos; |
717 | | const u8 *known_bss; |
718 | | u8 known_bss_len; |
719 | | |
720 | | #ifdef CONFIG_IEEE80211AX |
721 | | u8 *cca_pos; |
722 | | #endif /* CONFIG_IEEE80211AX */ |
723 | | }; |
724 | | |
725 | | |
726 | | static void hostapd_free_probe_resp_params(struct probe_resp_params *params) |
727 | 0 | { |
728 | | #ifdef CONFIG_IEEE80211BE |
729 | | if (!params) |
730 | | return; |
731 | | |
732 | | os_free(params->mld_info); |
733 | | params->mld_info = NULL; |
734 | | #endif /* CONFIG_IEEE80211BE */ |
735 | 0 | } |
736 | | |
737 | | |
738 | | static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd, |
739 | | struct probe_resp_params *params) |
740 | 0 | { |
741 | 0 | struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap : |
742 | 0 | hapd; |
743 | 0 | size_t buflen = 0; |
744 | |
|
745 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
746 | |
|
747 | 0 | #ifdef CONFIG_WPS |
748 | 0 | if (hapd->wps_probe_resp_ie) |
749 | 0 | buflen += wpabuf_len(hapd->wps_probe_resp_ie); |
750 | 0 | #endif /* CONFIG_WPS */ |
751 | | #ifdef CONFIG_P2P |
752 | | if (hapd->p2p_probe_resp_ie) |
753 | | buflen += wpabuf_len(hapd->p2p_probe_resp_ie); |
754 | | #endif /* CONFIG_P2P */ |
755 | | #ifdef CONFIG_FST |
756 | | if (hapd->iface->fst_ies) |
757 | | buflen += wpabuf_len(hapd->iface->fst_ies); |
758 | | #endif /* CONFIG_FST */ |
759 | 0 | if (hapd->conf->vendor_elements) |
760 | 0 | buflen += wpabuf_len(hapd->conf->vendor_elements); |
761 | | #ifdef CONFIG_TESTING_OPTIONS |
762 | | if (hapd->conf->presp_elements) |
763 | | buflen += wpabuf_len(hapd->conf->presp_elements); |
764 | | #endif /* CONFIG_TESTING_OPTIONS */ |
765 | 0 | if (hapd->conf->vendor_vht) { |
766 | 0 | buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + |
767 | 0 | 2 + sizeof(struct ieee80211_vht_operation); |
768 | 0 | } |
769 | |
|
770 | 0 | buflen += he_elem_len(hapd); |
771 | |
|
772 | | #ifdef CONFIG_IEEE80211BE |
773 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
774 | | |
775 | | buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); |
776 | | buflen += 3 + sizeof(struct ieee80211_eht_operation); |
777 | | if (hapd->iconf->punct_bitmap) |
778 | | buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; |
779 | | |
780 | | if (params->mld_ap && params->mld_ap->conf->mld_ap) { |
781 | | buflen += hostapd_eid_eht_ml_beacon_len( |
782 | | params->mld_ap, params->mld_info, |
783 | | !!params->mld_ap); |
784 | | |
785 | | if (hapd->conf->mld_ap) |
786 | | buflen += hostapd_eid_eht_ml_beacon_len( |
787 | | hapd, NULL, false); |
788 | | |
789 | | /* For Max Channel Switch Time element during channel |
790 | | * switch */ |
791 | | buflen += 6; |
792 | | } else if (hapd->conf->mld_ap) { |
793 | | buflen += hostapd_eid_eht_ml_beacon_len( |
794 | | hapd, params->mld_info, false); |
795 | | |
796 | | /* For Max Channel Switch Time element during channel |
797 | | * switch */ |
798 | | buflen += 6; |
799 | | } |
800 | | } |
801 | | #endif /* CONFIG_IEEE80211BE */ |
802 | |
|
803 | 0 | buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP, |
804 | 0 | NULL, |
805 | 0 | params->known_bss, |
806 | 0 | params->known_bss_len, NULL); |
807 | 0 | buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true); |
808 | 0 | buflen += hostapd_mbo_ie_len(hapd); |
809 | 0 | buflen += hostapd_eid_owe_trans_len(hapd); |
810 | 0 | buflen += hostapd_eid_dpp_cc_len(hapd); |
811 | 0 | buflen += hostapd_get_rsne_override_len(hapd); |
812 | 0 | buflen += hostapd_get_rsne_override_2_len(hapd); |
813 | 0 | buflen += hostapd_get_rsnxe_override_len(hapd); |
814 | |
|
815 | 0 | return buflen; |
816 | 0 | } |
817 | | |
818 | | |
819 | | static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, |
820 | | struct probe_resp_params *params, |
821 | | u8 *pos, size_t len) |
822 | 0 | { |
823 | 0 | struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap : |
824 | 0 | hapd; |
825 | 0 | u8 *csa_pos; |
826 | 0 | u8 *epos; |
827 | |
|
828 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
829 | 0 | epos = pos + len; |
830 | |
|
831 | 0 | *pos++ = WLAN_EID_SSID; |
832 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
833 | 0 | os_memcpy(pos, hapd->conf->ssid.ssid, |
834 | 0 | hapd->conf->ssid.ssid_len); |
835 | 0 | pos += hapd->conf->ssid.ssid_len; |
836 | | |
837 | | /* Supported rates */ |
838 | 0 | pos = hostapd_eid_supp_rates(hapd, pos); |
839 | | |
840 | | /* DS Params */ |
841 | 0 | pos = hostapd_eid_ds_params(hapd, pos); |
842 | |
|
843 | 0 | pos = hostapd_eid_country(hapd, pos, epos - pos); |
844 | | |
845 | | /* Power Constraint element */ |
846 | 0 | pos = hostapd_eid_pwr_constraint(hapd, pos); |
847 | | |
848 | | /* CSA element */ |
849 | 0 | csa_pos = hostapd_eid_csa(hapd, pos); |
850 | 0 | if (csa_pos != pos) |
851 | 0 | params->csa_pos = csa_pos - 1; |
852 | 0 | else |
853 | 0 | params->csa_pos = NULL; |
854 | 0 | pos = csa_pos; |
855 | | |
856 | | /* ERP Information element */ |
857 | 0 | pos = hostapd_eid_erp_info(hapd, pos); |
858 | | |
859 | | /* Extended supported rates */ |
860 | 0 | pos = hostapd_eid_ext_supp_rates(hapd, pos); |
861 | |
|
862 | 0 | pos = hostapd_get_rsne(hapd, pos, epos - pos); |
863 | 0 | pos = hostapd_eid_bss_load(hapd, pos, epos - pos); |
864 | 0 | pos = hostapd_eid_mbssid(hapd_probed, pos, epos, |
865 | 0 | WLAN_FC_STYPE_PROBE_RESP, 0, |
866 | 0 | NULL, params->known_bss, params->known_bss_len, |
867 | 0 | NULL, NULL, NULL, 0); |
868 | 0 | pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); |
869 | 0 | pos = hostapd_get_mde(hapd, pos, epos - pos); |
870 | | |
871 | | /* eCSA element */ |
872 | 0 | csa_pos = hostapd_eid_ecsa(hapd, pos); |
873 | 0 | if (csa_pos != pos) |
874 | 0 | params->ecsa_pos = csa_pos - 1; |
875 | 0 | else |
876 | 0 | params->ecsa_pos = NULL; |
877 | 0 | pos = csa_pos; |
878 | |
|
879 | 0 | pos = hostapd_eid_supported_op_classes(hapd, pos); |
880 | 0 | pos = hostapd_eid_ht_capabilities(hapd, pos); |
881 | 0 | pos = hostapd_eid_ht_operation(hapd, pos); |
882 | | |
883 | | /* Probe Response frames always include all non-TX profiles except |
884 | | * when a list of known BSSes is included in the Probe Request frame. */ |
885 | 0 | pos = hostapd_eid_ext_capab(hapd, pos, |
886 | 0 | hapd->iconf->mbssid >= MBSSID_ENABLED && |
887 | 0 | !params->known_bss_len); |
888 | |
|
889 | 0 | pos = hostapd_eid_time_adv(hapd, pos); |
890 | 0 | pos = hostapd_eid_time_zone(hapd, pos); |
891 | |
|
892 | 0 | pos = hostapd_eid_interworking(hapd, pos); |
893 | 0 | pos = hostapd_eid_adv_proto(hapd, pos); |
894 | 0 | pos = hostapd_eid_roaming_consortium(hapd, pos); |
895 | |
|
896 | | #ifdef CONFIG_FST |
897 | | if (hapd->iface->fst_ies) { |
898 | | os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), |
899 | | wpabuf_len(hapd->iface->fst_ies)); |
900 | | pos += wpabuf_len(hapd->iface->fst_ies); |
901 | | } |
902 | | #endif /* CONFIG_FST */ |
903 | |
|
904 | | #ifdef CONFIG_IEEE80211AC |
905 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && |
906 | | !is_6ghz_op_class(hapd->iconf->op_class)) { |
907 | | pos = hostapd_eid_vht_capabilities(hapd, pos, 0); |
908 | | pos = hostapd_eid_vht_operation(hapd, pos); |
909 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
910 | | } |
911 | | #endif /* CONFIG_IEEE80211AC */ |
912 | |
|
913 | | #ifdef CONFIG_IEEE80211AX |
914 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && |
915 | | is_6ghz_op_class(hapd->iconf->op_class)) |
916 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
917 | | #endif /* CONFIG_IEEE80211AX */ |
918 | |
|
919 | 0 | pos = hostapd_eid_chsw_wrapper(hapd, pos); |
920 | |
|
921 | 0 | pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true); |
922 | 0 | pos = hostapd_eid_fils_indic(hapd, pos, 0); |
923 | | |
924 | | /* Max Channel Switch Time element */ |
925 | 0 | pos = hostapd_eid_max_cs_time(hapd, pos); |
926 | |
|
927 | 0 | pos = hostapd_get_rsnxe(hapd, pos, epos - pos); |
928 | |
|
929 | | #ifdef CONFIG_IEEE80211AX |
930 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { |
931 | | u8 *cca_pos; |
932 | | |
933 | | pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP); |
934 | | pos = hostapd_eid_he_operation(hapd, pos); |
935 | | |
936 | | /* BSS Color Change Announcement element */ |
937 | | cca_pos = hostapd_eid_cca(hapd, pos); |
938 | | if (cca_pos != pos) |
939 | | params->cca_pos = cca_pos - 2; |
940 | | else |
941 | | params->cca_pos = NULL; |
942 | | pos = cca_pos; |
943 | | |
944 | | pos = hostapd_eid_spatial_reuse(hapd, pos); |
945 | | pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); |
946 | | pos = hostapd_eid_he_6ghz_band_cap(hapd, pos); |
947 | | } |
948 | | #endif /* CONFIG_IEEE80211AX */ |
949 | |
|
950 | | #ifdef CONFIG_IEEE80211BE |
951 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
952 | | if (params->mld_ap && params->mld_ap->conf->mld_ap) { |
953 | | pos = hostapd_eid_eht_ml_beacon( |
954 | | params->mld_ap, params->mld_info, |
955 | | pos, !!params->mld_ap); |
956 | | |
957 | | if (hapd->conf->mld_ap) |
958 | | pos = hostapd_eid_eht_ml_beacon( |
959 | | hapd, NULL, pos, false); |
960 | | |
961 | | } else if (hapd->conf->mld_ap) { |
962 | | pos = hostapd_eid_eht_ml_beacon(hapd, |
963 | | params->mld_info, |
964 | | pos, false); |
965 | | } |
966 | | |
967 | | pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP); |
968 | | pos = hostapd_eid_eht_operation(hapd, pos); |
969 | | } |
970 | | #endif /* CONFIG_IEEE80211BE */ |
971 | |
|
972 | | #ifdef CONFIG_IEEE80211AC |
973 | | if (hapd->conf->vendor_vht) |
974 | | pos = hostapd_eid_vendor_vht(hapd, pos); |
975 | | #endif /* CONFIG_IEEE80211AC */ |
976 | | |
977 | | /* WPA */ |
978 | 0 | pos = hostapd_get_wpa_ie(hapd, pos, epos - pos); |
979 | | |
980 | | /* Wi-Fi Alliance WMM */ |
981 | 0 | pos = hostapd_eid_wmm(hapd, pos); |
982 | |
|
983 | 0 | #ifdef CONFIG_WPS |
984 | 0 | if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { |
985 | 0 | os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), |
986 | 0 | wpabuf_len(hapd->wps_probe_resp_ie)); |
987 | 0 | pos += wpabuf_len(hapd->wps_probe_resp_ie); |
988 | 0 | } |
989 | 0 | #endif /* CONFIG_WPS */ |
990 | |
|
991 | | #ifdef CONFIG_P2P |
992 | | if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p && |
993 | | hapd->p2p_probe_resp_ie) { |
994 | | os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), |
995 | | wpabuf_len(hapd->p2p_probe_resp_ie)); |
996 | | pos += wpabuf_len(hapd->p2p_probe_resp_ie); |
997 | | } |
998 | | #endif /* CONFIG_P2P */ |
999 | | #ifdef CONFIG_P2P_MANAGER |
1000 | | if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == |
1001 | | P2P_MANAGE) |
1002 | | pos = hostapd_eid_p2p_manage(hapd, pos); |
1003 | | #endif /* CONFIG_P2P_MANAGER */ |
1004 | |
|
1005 | 0 | #ifdef CONFIG_HS20 |
1006 | 0 | pos = hostapd_eid_hs20_indication(hapd, pos); |
1007 | 0 | #endif /* CONFIG_HS20 */ |
1008 | |
|
1009 | 0 | pos = hostapd_eid_mbo(hapd, pos, epos - pos); |
1010 | 0 | pos = hostapd_eid_owe_trans(hapd, pos, epos - pos); |
1011 | 0 | pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos); |
1012 | |
|
1013 | 0 | pos = hostapd_get_rsne_override(hapd, pos, epos - pos); |
1014 | 0 | pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos); |
1015 | 0 | pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos); |
1016 | |
|
1017 | 0 | if (hapd->conf->vendor_elements) { |
1018 | 0 | os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), |
1019 | 0 | wpabuf_len(hapd->conf->vendor_elements)); |
1020 | 0 | pos += wpabuf_len(hapd->conf->vendor_elements); |
1021 | 0 | } |
1022 | |
|
1023 | | #ifdef CONFIG_TESTING_OPTIONS |
1024 | | if (hapd->conf->presp_elements) { |
1025 | | os_memcpy(pos, wpabuf_head(hapd->conf->presp_elements), |
1026 | | wpabuf_len(hapd->conf->presp_elements)); |
1027 | | pos += wpabuf_len(hapd->conf->presp_elements); |
1028 | | } |
1029 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1030 | |
|
1031 | 0 | return pos; |
1032 | 0 | } |
1033 | | |
1034 | | |
1035 | | static void hostapd_gen_probe_resp(struct hostapd_data *hapd, |
1036 | | struct probe_resp_params *params) |
1037 | 0 | { |
1038 | 0 | struct hostapd_data *hapd_probed = hapd; |
1039 | 0 | u8 *pos; |
1040 | 0 | size_t buflen; |
1041 | |
|
1042 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
1043 | |
|
1044 | 0 | #define MAX_PROBERESP_LEN 768 |
1045 | 0 | buflen = MAX_PROBERESP_LEN; |
1046 | 0 | buflen += hostapd_probe_resp_elems_len(hapd_probed, params); |
1047 | 0 | params->resp = os_zalloc(buflen); |
1048 | 0 | if (!params->resp) { |
1049 | 0 | params->resp_len = 0; |
1050 | 0 | return; |
1051 | 0 | } |
1052 | | |
1053 | 0 | params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
1054 | 0 | WLAN_FC_STYPE_PROBE_RESP); |
1055 | | /* Unicast the response to all requests on bands other than 6 GHz. For |
1056 | | * the 6 GHz, unicast is used only if the actual SSID is not included in |
1057 | | * the Beacon frames. Otherwise, broadcast response is used per IEEE |
1058 | | * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for |
1059 | | * the Probe Response frame template for the unsolicited (i.e., not as |
1060 | | * a response to a specific request) case. */ |
1061 | 0 | if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) || |
1062 | 0 | hapd->conf->ignore_broadcast_ssid)) |
1063 | 0 | os_memcpy(params->resp->da, params->req->sa, ETH_ALEN); |
1064 | 0 | else |
1065 | 0 | os_memset(params->resp->da, 0xff, ETH_ALEN); |
1066 | 0 | os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN); |
1067 | |
|
1068 | 0 | os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN); |
1069 | 0 | params->resp->u.probe_resp.beacon_int = |
1070 | 0 | host_to_le16(hapd->iconf->beacon_int); |
1071 | | |
1072 | | /* hardware or low-level driver will setup seq_ctrl and timestamp */ |
1073 | 0 | params->resp->u.probe_resp.capab_info = |
1074 | 0 | host_to_le16(hostapd_own_capab_info(hapd)); |
1075 | |
|
1076 | 0 | pos = hostapd_probe_resp_fill_elems(hapd_probed, params, |
1077 | 0 | params->resp->u.probe_resp.variable, |
1078 | 0 | buflen); |
1079 | |
|
1080 | 0 | params->resp_len = pos - (u8 *) params->resp; |
1081 | 0 | } |
1082 | | |
1083 | | |
1084 | | #ifdef CONFIG_IEEE80211BE |
1085 | | static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, |
1086 | | struct probe_resp_params *params, |
1087 | | const struct ieee80211_mgmt *mgmt, |
1088 | | int mld_id, u16 links) |
1089 | | { |
1090 | | struct hostapd_data *link; |
1091 | | |
1092 | | params->mld_ap = NULL; |
1093 | | params->mld_info = os_zalloc(sizeof(*params->mld_info)); |
1094 | | if (!params->mld_info) |
1095 | | return; |
1096 | | |
1097 | | wpa_printf(MSG_DEBUG, |
1098 | | "MLD: Got ML probe request with AP MLD ID %d for links %04x", |
1099 | | mld_id, links); |
1100 | | |
1101 | | /* |
1102 | | * Set mld_ap if the ML probe request explicitly requested a specific |
1103 | | * AP MLD ID. |
1104 | | */ |
1105 | | if (mld_id > 0) { |
1106 | | if (hapd == hostapd_mbssid_get_tx_bss(hapd)) { |
1107 | | hapd = hostapd_get_mbssid_bss_by_idx(hapd, mld_id); |
1108 | | if (!hapd) { |
1109 | | wpa_printf(MSG_INFO, |
1110 | | "Ignore Probe Request from " MACSTR |
1111 | | " since no matched non-TX BSS found for MBSSID Index %d", |
1112 | | MAC2STR(mgmt->sa), mld_id); |
1113 | | goto fail; |
1114 | | } |
1115 | | } |
1116 | | params->mld_ap = hapd; |
1117 | | } |
1118 | | |
1119 | | for_each_mld_link(link, hapd) { |
1120 | | struct mld_link_info *link_info; |
1121 | | u8 mld_link_id = link->mld_link_id; |
1122 | | |
1123 | | /* Never duplicate main Probe Response frame body */ |
1124 | | if (link == hapd) |
1125 | | continue; |
1126 | | |
1127 | | /* Only include requested links */ |
1128 | | if (!(BIT(mld_link_id) & links)) |
1129 | | continue; |
1130 | | |
1131 | | link_info = ¶ms->mld_info->links[mld_link_id]; |
1132 | | os_memcpy(link_info, &hapd->partner_links[mld_link_id], |
1133 | | sizeof(hapd->partner_links[mld_link_id])); |
1134 | | |
1135 | | wpa_printf(MSG_DEBUG, |
1136 | | "MLD: ML probe response includes link STA info for %d: %u bytes", |
1137 | | mld_link_id, link_info->resp_sta_profile_len); |
1138 | | } |
1139 | | |
1140 | | if (mld_id > 0 && !params->mld_ap) { |
1141 | | wpa_printf(MSG_DEBUG, |
1142 | | "MLD: No nontransmitted BSSID for MLD ID %d", |
1143 | | mld_id); |
1144 | | goto fail; |
1145 | | } |
1146 | | |
1147 | | return; |
1148 | | |
1149 | | fail: |
1150 | | hostapd_free_probe_resp_params(params); |
1151 | | params->mld_ap = NULL; |
1152 | | params->mld_info = NULL; |
1153 | | } |
1154 | | #endif /* CONFIG_IEEE80211BE */ |
1155 | | |
1156 | | |
1157 | | enum ssid_match_result { |
1158 | | NO_SSID_MATCH, |
1159 | | EXACT_SSID_MATCH, |
1160 | | WILDCARD_SSID_MATCH, |
1161 | | CO_LOCATED_SSID_MATCH, |
1162 | | }; |
1163 | | |
1164 | | static enum ssid_match_result ssid_match(struct hostapd_data *hapd, |
1165 | | const u8 *ssid, size_t ssid_len, |
1166 | | const u8 *ssid_list, |
1167 | | size_t ssid_list_len, |
1168 | | const u8 *short_ssid_list, |
1169 | | size_t short_ssid_list_len) |
1170 | 0 | { |
1171 | 0 | const u8 *pos, *end; |
1172 | 0 | struct hostapd_iface *iface = hapd->iface; |
1173 | 0 | int wildcard = 0; |
1174 | 0 | size_t i, j; |
1175 | |
|
1176 | 0 | if (ssid_len == 0) |
1177 | 0 | wildcard = 1; |
1178 | 0 | if (ssid_len == hapd->conf->ssid.ssid_len && |
1179 | 0 | os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) |
1180 | 0 | return EXACT_SSID_MATCH; |
1181 | | |
1182 | 0 | if (ssid_list) { |
1183 | 0 | pos = ssid_list; |
1184 | 0 | end = ssid_list + ssid_list_len; |
1185 | 0 | while (end - pos >= 2) { |
1186 | 0 | if (2 + pos[1] > end - pos) |
1187 | 0 | break; |
1188 | 0 | if (pos[1] == 0) |
1189 | 0 | wildcard = 1; |
1190 | 0 | if (pos[1] == hapd->conf->ssid.ssid_len && |
1191 | 0 | os_memcmp(pos + 2, hapd->conf->ssid.ssid, |
1192 | 0 | pos[1]) == 0) |
1193 | 0 | return EXACT_SSID_MATCH; |
1194 | 0 | pos += 2 + pos[1]; |
1195 | 0 | } |
1196 | 0 | } |
1197 | | |
1198 | 0 | if (short_ssid_list) { |
1199 | 0 | pos = short_ssid_list; |
1200 | 0 | end = short_ssid_list + short_ssid_list_len; |
1201 | 0 | while (end - pos >= 4) { |
1202 | 0 | if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos)) |
1203 | 0 | return EXACT_SSID_MATCH; |
1204 | 0 | pos += 4; |
1205 | 0 | } |
1206 | 0 | } |
1207 | | |
1208 | 0 | if (wildcard) |
1209 | 0 | return WILDCARD_SSID_MATCH; |
1210 | | |
1211 | 0 | if (!iface->interfaces || iface->interfaces->count <= 1 || |
1212 | 0 | is_6ghz_op_class(hapd->iconf->op_class)) |
1213 | 0 | return NO_SSID_MATCH; |
1214 | | |
1215 | 0 | for (i = 0; i < iface->interfaces->count; i++) { |
1216 | 0 | struct hostapd_iface *colocated; |
1217 | |
|
1218 | 0 | colocated = iface->interfaces->iface[i]; |
1219 | |
|
1220 | 0 | if (colocated == iface || |
1221 | 0 | !is_6ghz_op_class(colocated->conf->op_class)) |
1222 | 0 | continue; |
1223 | | |
1224 | 0 | for (j = 0; j < colocated->num_bss; j++) { |
1225 | 0 | struct hostapd_bss_config *conf; |
1226 | |
|
1227 | 0 | conf = colocated->bss[j]->conf; |
1228 | 0 | if (ssid_len == conf->ssid.ssid_len && |
1229 | 0 | os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0) |
1230 | 0 | return CO_LOCATED_SSID_MATCH; |
1231 | 0 | } |
1232 | 0 | } |
1233 | | |
1234 | 0 | return NO_SSID_MATCH; |
1235 | 0 | } |
1236 | | |
1237 | | |
1238 | | void sta_track_expire(struct hostapd_iface *iface, int force) |
1239 | 0 | { |
1240 | 0 | struct os_reltime now; |
1241 | 0 | struct hostapd_sta_info *info; |
1242 | |
|
1243 | 0 | if (!iface->num_sta_seen) |
1244 | 0 | return; |
1245 | | |
1246 | 0 | os_get_reltime(&now); |
1247 | 0 | while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, |
1248 | 0 | list))) { |
1249 | 0 | if (!force && |
1250 | 0 | !os_reltime_expired(&now, &info->last_seen, |
1251 | 0 | iface->conf->track_sta_max_age)) |
1252 | 0 | break; |
1253 | 0 | force = 0; |
1254 | |
|
1255 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " |
1256 | 0 | MACSTR, iface->bss[0]->conf->iface, |
1257 | 0 | MAC2STR(info->addr)); |
1258 | 0 | dl_list_del(&info->list); |
1259 | 0 | iface->num_sta_seen--; |
1260 | 0 | sta_track_del(info); |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | | |
1265 | | static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, |
1266 | | const u8 *addr) |
1267 | 0 | { |
1268 | 0 | struct hostapd_sta_info *info; |
1269 | |
|
1270 | 0 | dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) |
1271 | 0 | if (ether_addr_equal(addr, info->addr)) |
1272 | 0 | return info; |
1273 | | |
1274 | 0 | return NULL; |
1275 | 0 | } |
1276 | | |
1277 | | |
1278 | | void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) |
1279 | 0 | { |
1280 | 0 | struct hostapd_sta_info *info; |
1281 | |
|
1282 | 0 | info = sta_track_get(iface, addr); |
1283 | 0 | if (info) { |
1284 | | /* Move the most recent entry to the end of the list */ |
1285 | 0 | dl_list_del(&info->list); |
1286 | 0 | dl_list_add_tail(&iface->sta_seen, &info->list); |
1287 | 0 | os_get_reltime(&info->last_seen); |
1288 | 0 | info->ssi_signal = ssi_signal; |
1289 | 0 | return; |
1290 | 0 | } |
1291 | | |
1292 | | /* Add a new entry */ |
1293 | 0 | info = os_zalloc(sizeof(*info)); |
1294 | 0 | if (info == NULL) |
1295 | 0 | return; |
1296 | 0 | os_memcpy(info->addr, addr, ETH_ALEN); |
1297 | 0 | os_get_reltime(&info->last_seen); |
1298 | 0 | info->ssi_signal = ssi_signal; |
1299 | |
|
1300 | 0 | if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { |
1301 | | /* Expire oldest entry to make room for a new one */ |
1302 | 0 | sta_track_expire(iface, 1); |
1303 | 0 | } |
1304 | |
|
1305 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " |
1306 | 0 | MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); |
1307 | 0 | dl_list_add_tail(&iface->sta_seen, &info->list); |
1308 | 0 | iface->num_sta_seen++; |
1309 | 0 | } |
1310 | | |
1311 | | |
1312 | | struct hostapd_data * |
1313 | | sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, |
1314 | | const char *ifname) |
1315 | 0 | { |
1316 | 0 | struct hapd_interfaces *interfaces = iface->interfaces; |
1317 | 0 | size_t i, j; |
1318 | |
|
1319 | 0 | for (i = 0; i < interfaces->count; i++) { |
1320 | 0 | struct hostapd_data *hapd = NULL; |
1321 | |
|
1322 | 0 | iface = interfaces->iface[i]; |
1323 | 0 | for (j = 0; j < iface->num_bss; j++) { |
1324 | 0 | hapd = iface->bss[j]; |
1325 | 0 | if (os_strcmp(ifname, hapd->conf->iface) == 0) |
1326 | 0 | break; |
1327 | 0 | hapd = NULL; |
1328 | 0 | } |
1329 | |
|
1330 | 0 | if (hapd && sta_track_get(iface, addr)) |
1331 | 0 | return hapd; |
1332 | 0 | } |
1333 | | |
1334 | 0 | return NULL; |
1335 | 0 | } |
1336 | | |
1337 | | |
1338 | | #ifdef CONFIG_TAXONOMY |
1339 | | void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, |
1340 | | struct wpabuf **probe_ie_taxonomy) |
1341 | | { |
1342 | | struct hostapd_sta_info *info; |
1343 | | |
1344 | | info = sta_track_get(iface, addr); |
1345 | | if (!info) |
1346 | | return; |
1347 | | |
1348 | | wpabuf_free(*probe_ie_taxonomy); |
1349 | | *probe_ie_taxonomy = info->probe_ie_taxonomy; |
1350 | | info->probe_ie_taxonomy = NULL; |
1351 | | } |
1352 | | #endif /* CONFIG_TAXONOMY */ |
1353 | | |
1354 | | |
1355 | | #ifdef CONFIG_IEEE80211BE |
1356 | | static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len, |
1357 | | int *mld_id, u16 *links) |
1358 | | { |
1359 | | u16 ml_control; |
1360 | | const struct element *sub; |
1361 | | const u8 *pos; |
1362 | | size_t len; |
1363 | | |
1364 | | *mld_id = -1; |
1365 | | *links = 0xffff; |
1366 | | |
1367 | | if (ml_len < sizeof(struct ieee80211_eht_ml)) |
1368 | | return false; |
1369 | | |
1370 | | ml_control = le_to_host16(ml->ml_control); |
1371 | | if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) != |
1372 | | MULTI_LINK_CONTROL_TYPE_PROBE_REQ) { |
1373 | | wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req"); |
1374 | | return false; |
1375 | | } |
1376 | | |
1377 | | if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) { |
1378 | | wpa_printf(MSG_DEBUG, "MLD: ML probe req too short"); |
1379 | | return false; |
1380 | | } |
1381 | | |
1382 | | pos = ml->variable; |
1383 | | len = pos[0]; |
1384 | | if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) { |
1385 | | wpa_printf(MSG_DEBUG, |
1386 | | "MLD: ML probe request with invalid length"); |
1387 | | return false; |
1388 | | } |
1389 | | |
1390 | | if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) { |
1391 | | if (len < 2) { |
1392 | | wpa_printf(MSG_DEBUG, |
1393 | | "MLD: ML probe req too short for MLD ID"); |
1394 | | return false; |
1395 | | } |
1396 | | |
1397 | | *mld_id = pos[1]; |
1398 | | } |
1399 | | pos += len; |
1400 | | |
1401 | | /* Parse subelements (if there are any) */ |
1402 | | len = ml_len - len - sizeof(struct ieee80211_eht_ml); |
1403 | | for_each_element_id(sub, 0, pos, len) { |
1404 | | const struct ieee80211_eht_per_sta_profile *sta; |
1405 | | u16 sta_control; |
1406 | | |
1407 | | if (*links == 0xffff) |
1408 | | *links = 0; |
1409 | | |
1410 | | if (sub->datalen < |
1411 | | sizeof(struct ieee80211_eht_per_sta_profile)) { |
1412 | | wpa_printf(MSG_DEBUG, |
1413 | | "MLD: ML probe req %d too short for sta profile", |
1414 | | sub->datalen); |
1415 | | return false; |
1416 | | } |
1417 | | |
1418 | | sta = (struct ieee80211_eht_per_sta_profile *) sub->data; |
1419 | | |
1420 | | /* |
1421 | | * Extract the link ID, do not return whether a complete or |
1422 | | * partial profile was requested. |
1423 | | */ |
1424 | | sta_control = le_to_host16(sta->sta_control); |
1425 | | *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK); |
1426 | | } |
1427 | | |
1428 | | if (!for_each_element_completed(sub, pos, len)) { |
1429 | | wpa_printf(MSG_DEBUG, |
1430 | | "MLD: ML probe req sub-elements parsing error"); |
1431 | | return false; |
1432 | | } |
1433 | | |
1434 | | return true; |
1435 | | } |
1436 | | #endif /* CONFIG_IEEE80211BE */ |
1437 | | |
1438 | | |
1439 | | void handle_probe_req(struct hostapd_data *hapd, |
1440 | | const struct ieee80211_mgmt *mgmt, size_t len, |
1441 | | int ssi_signal) |
1442 | 0 | { |
1443 | 0 | struct ieee802_11_elems elems; |
1444 | 0 | const u8 *ie; |
1445 | 0 | size_t ie_len; |
1446 | 0 | size_t i; |
1447 | 0 | int noack; |
1448 | 0 | enum ssid_match_result res; |
1449 | 0 | int ret; |
1450 | 0 | u16 csa_offs[2]; |
1451 | 0 | size_t csa_offs_len; |
1452 | 0 | struct radius_sta rad_info; |
1453 | 0 | struct probe_resp_params params; |
1454 | 0 | char *hex = NULL; |
1455 | | #ifdef CONFIG_IEEE80211BE |
1456 | | int mld_id; |
1457 | | u16 links; |
1458 | | #endif /* CONFIG_IEEE80211BE */ |
1459 | |
|
1460 | 0 | if (hapd->iconf->rssi_ignore_probe_request && ssi_signal && |
1461 | 0 | ssi_signal < hapd->iconf->rssi_ignore_probe_request) |
1462 | 0 | return; |
1463 | | |
1464 | 0 | if (len < IEEE80211_HDRLEN) |
1465 | 0 | return; |
1466 | 0 | ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; |
1467 | 0 | if (hapd->iconf->track_sta_max_num) |
1468 | 0 | sta_track_add(hapd->iface, mgmt->sa, ssi_signal); |
1469 | 0 | ie_len = len - IEEE80211_HDRLEN; |
1470 | |
|
1471 | 0 | ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, |
1472 | 0 | &rad_info, 1); |
1473 | 0 | if (ret == HOSTAPD_ACL_REJECT) { |
1474 | 0 | wpa_msg(hapd->msg_ctx, MSG_DEBUG, |
1475 | 0 | "Ignore Probe Request frame from " MACSTR |
1476 | 0 | " due to ACL reject ", MAC2STR(mgmt->sa)); |
1477 | 0 | return; |
1478 | 0 | } |
1479 | | |
1480 | 0 | for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) |
1481 | 0 | if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, |
1482 | 0 | mgmt->sa, mgmt->da, mgmt->bssid, |
1483 | 0 | ie, ie_len, ssi_signal) > 0) |
1484 | 0 | return; |
1485 | | |
1486 | 0 | if (!hapd->conf->send_probe_response) |
1487 | 0 | return; |
1488 | | |
1489 | 0 | if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { |
1490 | 0 | wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, |
1491 | 0 | MAC2STR(mgmt->sa)); |
1492 | 0 | return; |
1493 | 0 | } |
1494 | | |
1495 | 0 | if ((!elems.ssid || !elems.supp_rates)) { |
1496 | 0 | wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " |
1497 | 0 | "without SSID or supported rates element", |
1498 | 0 | MAC2STR(mgmt->sa)); |
1499 | 0 | return; |
1500 | 0 | } |
1501 | | |
1502 | | /* |
1503 | | * No need to reply if the Probe Request frame was sent on an adjacent |
1504 | | * channel. IEEE Std 802.11-2012 describes this as a requirement for an |
1505 | | * AP with dot11RadioMeasurementActivated set to true, but strictly |
1506 | | * speaking does not allow such ignoring of Probe Request frames if |
1507 | | * dot11RadioMeasurementActivated is false. Anyway, this can help reduce |
1508 | | * number of unnecessary Probe Response frames for cases where the STA |
1509 | | * is less likely to see them (Probe Request frame sent on a |
1510 | | * neighboring, but partially overlapping, channel). |
1511 | | */ |
1512 | 0 | if (elems.ds_params && |
1513 | 0 | hapd->iface->current_mode && |
1514 | 0 | (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || |
1515 | 0 | hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && |
1516 | 0 | hapd->iconf->channel != elems.ds_params[0]) { |
1517 | 0 | wpa_printf(MSG_DEBUG, |
1518 | 0 | "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u", |
1519 | 0 | hapd->iconf->channel, elems.ds_params[0]); |
1520 | 0 | return; |
1521 | 0 | } |
1522 | | |
1523 | | #ifdef CONFIG_P2P |
1524 | | if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { |
1525 | | struct wpabuf *wps; |
1526 | | wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); |
1527 | | if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { |
1528 | | wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " |
1529 | | "due to mismatch with Requested Device " |
1530 | | "Type"); |
1531 | | wpabuf_free(wps); |
1532 | | return; |
1533 | | } |
1534 | | wpabuf_free(wps); |
1535 | | } |
1536 | | |
1537 | | if (hapd->p2p && hapd->p2p_group && elems.p2p) { |
1538 | | struct wpabuf *p2p; |
1539 | | p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); |
1540 | | if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { |
1541 | | wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " |
1542 | | "due to mismatch with Device ID"); |
1543 | | wpabuf_free(p2p); |
1544 | | return; |
1545 | | } |
1546 | | wpabuf_free(p2p); |
1547 | | } |
1548 | | #endif /* CONFIG_P2P */ |
1549 | | |
1550 | 0 | if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && |
1551 | 0 | elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) { |
1552 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " |
1553 | 0 | "broadcast SSID ignored", MAC2STR(mgmt->sa)); |
1554 | 0 | return; |
1555 | 0 | } |
1556 | | |
1557 | | #ifdef CONFIG_P2P |
1558 | | if ((hapd->conf->p2p & P2P_GROUP_OWNER) && |
1559 | | elems.ssid_len == P2P_WILDCARD_SSID_LEN && |
1560 | | os_memcmp(elems.ssid, P2P_WILDCARD_SSID, |
1561 | | P2P_WILDCARD_SSID_LEN) == 0) { |
1562 | | /* Process P2P Wildcard SSID like Wildcard SSID */ |
1563 | | elems.ssid_len = 0; |
1564 | | } |
1565 | | #endif /* CONFIG_P2P */ |
1566 | | |
1567 | | #ifdef CONFIG_TAXONOMY |
1568 | | { |
1569 | | struct sta_info *sta; |
1570 | | struct hostapd_sta_info *info; |
1571 | | |
1572 | | if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { |
1573 | | taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); |
1574 | | } else if ((info = sta_track_get(hapd->iface, |
1575 | | mgmt->sa)) != NULL) { |
1576 | | taxonomy_hostapd_sta_info_probe_req(hapd, info, |
1577 | | ie, ie_len); |
1578 | | } |
1579 | | } |
1580 | | #endif /* CONFIG_TAXONOMY */ |
1581 | | |
1582 | 0 | res = ssid_match(hapd, elems.ssid, elems.ssid_len, |
1583 | 0 | elems.ssid_list, elems.ssid_list_len, |
1584 | 0 | elems.short_ssid_list, elems.short_ssid_list_len); |
1585 | 0 | if (res == NO_SSID_MATCH) { |
1586 | 0 | if (!(mgmt->da[0] & 0x01)) { |
1587 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1588 | 0 | " for foreign SSID '%s' (DA " MACSTR ")%s", |
1589 | 0 | MAC2STR(mgmt->sa), |
1590 | 0 | wpa_ssid_txt(elems.ssid, elems.ssid_len), |
1591 | 0 | MAC2STR(mgmt->da), |
1592 | 0 | elems.ssid_list ? " (SSID list)" : ""); |
1593 | 0 | } |
1594 | 0 | return; |
1595 | 0 | } |
1596 | | |
1597 | 0 | if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) { |
1598 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " |
1599 | 0 | "broadcast SSID ignored", MAC2STR(mgmt->sa)); |
1600 | 0 | return; |
1601 | 0 | } |
1602 | | |
1603 | 0 | #ifdef CONFIG_INTERWORKING |
1604 | 0 | if (hapd->conf->interworking && |
1605 | 0 | elems.interworking && elems.interworking_len >= 1) { |
1606 | 0 | u8 ant = elems.interworking[0] & 0x0f; |
1607 | 0 | if (ant != INTERWORKING_ANT_WILDCARD && |
1608 | 0 | ant != hapd->conf->access_network_type) { |
1609 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1610 | 0 | " for mismatching ANT %u ignored", |
1611 | 0 | MAC2STR(mgmt->sa), ant); |
1612 | 0 | return; |
1613 | 0 | } |
1614 | 0 | } |
1615 | | |
1616 | 0 | if (hapd->conf->interworking && elems.interworking && |
1617 | 0 | (elems.interworking_len == 7 || elems.interworking_len == 9)) { |
1618 | 0 | const u8 *hessid; |
1619 | 0 | if (elems.interworking_len == 7) |
1620 | 0 | hessid = elems.interworking + 1; |
1621 | 0 | else |
1622 | 0 | hessid = elems.interworking + 1 + 2; |
1623 | 0 | if (!is_broadcast_ether_addr(hessid) && |
1624 | 0 | !ether_addr_equal(hessid, hapd->conf->hessid)) { |
1625 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1626 | 0 | " for mismatching HESSID " MACSTR |
1627 | 0 | " ignored", |
1628 | 0 | MAC2STR(mgmt->sa), MAC2STR(hessid)); |
1629 | 0 | return; |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | #endif /* CONFIG_INTERWORKING */ |
1633 | | |
1634 | | #ifdef CONFIG_P2P |
1635 | | if ((hapd->conf->p2p & P2P_GROUP_OWNER) && |
1636 | | supp_rates_11b_only(&elems)) { |
1637 | | /* Indicates support for 11b rates only */ |
1638 | | wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " |
1639 | | MACSTR " with only 802.11b rates", |
1640 | | MAC2STR(mgmt->sa)); |
1641 | | return; |
1642 | | } |
1643 | | #endif /* CONFIG_P2P */ |
1644 | | |
1645 | | /* TODO: verify that supp_rates contains at least one matching rate |
1646 | | * with AP configuration */ |
1647 | | |
1648 | 0 | if (hapd->conf->no_probe_resp_if_seen_on && |
1649 | 0 | is_multicast_ether_addr(mgmt->da) && |
1650 | 0 | is_multicast_ether_addr(mgmt->bssid) && |
1651 | 0 | sta_track_seen_on(hapd->iface, mgmt->sa, |
1652 | 0 | hapd->conf->no_probe_resp_if_seen_on)) { |
1653 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR |
1654 | 0 | " since STA has been seen on %s", |
1655 | 0 | hapd->conf->iface, MAC2STR(mgmt->sa), |
1656 | 0 | hapd->conf->no_probe_resp_if_seen_on); |
1657 | 0 | return; |
1658 | 0 | } |
1659 | | |
1660 | 0 | if (hapd->conf->no_probe_resp_if_max_sta && |
1661 | 0 | is_multicast_ether_addr(mgmt->da) && |
1662 | 0 | is_multicast_ether_addr(mgmt->bssid) && |
1663 | 0 | hapd->num_sta >= hapd->conf->max_num_sta && |
1664 | 0 | !ap_get_sta(hapd, mgmt->sa)) { |
1665 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR |
1666 | 0 | " since no room for additional STA", |
1667 | 0 | hapd->conf->iface, MAC2STR(mgmt->sa)); |
1668 | 0 | return; |
1669 | 0 | } |
1670 | | |
1671 | | #ifdef CONFIG_TESTING_OPTIONS |
1672 | | if (hapd->iconf->ignore_probe_probability > 0.0 && |
1673 | | drand48() < hapd->iconf->ignore_probe_probability) { |
1674 | | wpa_printf(MSG_INFO, |
1675 | | "TESTING: ignoring probe request from " MACSTR, |
1676 | | MAC2STR(mgmt->sa)); |
1677 | | return; |
1678 | | } |
1679 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1680 | | |
1681 | | /* Do not send Probe Response frame from a non-transmitting multiple |
1682 | | * BSSID profile unless the Probe Request frame is directed at that |
1683 | | * particular BSS. */ |
1684 | 0 | if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH) |
1685 | 0 | return; |
1686 | | |
1687 | 0 | if (hapd->conf->notify_mgmt_frames) { |
1688 | 0 | size_t hex_len; |
1689 | |
|
1690 | 0 | hex_len = len * 2 + 1; |
1691 | 0 | hex = os_malloc(hex_len); |
1692 | 0 | if (hex) |
1693 | 0 | wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len); |
1694 | 0 | } |
1695 | |
|
1696 | 0 | wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR |
1697 | 0 | " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal, |
1698 | 0 | hex ? " buf=" : "", hex ? hex : ""); |
1699 | |
|
1700 | 0 | os_free(hex); |
1701 | |
|
1702 | 0 | os_memset(¶ms, 0, sizeof(params)); |
1703 | |
|
1704 | | #ifdef CONFIG_IEEE80211BE |
1705 | | if (hapd->conf->mld_ap && elems.probe_req_mle && |
1706 | | parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle, |
1707 | | elems.probe_req_mle_len, &mld_id, &links)) { |
1708 | | hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt, |
1709 | | mld_id, links); |
1710 | | } |
1711 | | #endif /* CONFIG_IEEE80211BE */ |
1712 | |
|
1713 | 0 | params.req = mgmt; |
1714 | 0 | params.is_p2p = !!elems.p2p; |
1715 | 0 | params.known_bss = elems.mbssid_known_bss; |
1716 | 0 | params.known_bss_len = elems.mbssid_known_bss_len; |
1717 | |
|
1718 | 0 | hostapd_gen_probe_resp(hapd, ¶ms); |
1719 | |
|
1720 | 0 | hostapd_free_probe_resp_params(¶ms); |
1721 | |
|
1722 | 0 | if (!params.resp) |
1723 | 0 | return; |
1724 | | |
1725 | | /* |
1726 | | * If this is a broadcast probe request, apply no ack policy to avoid |
1727 | | * excessive retries. |
1728 | | */ |
1729 | 0 | noack = !!(res == WILDCARD_SSID_MATCH && |
1730 | 0 | is_broadcast_ether_addr(mgmt->da)); |
1731 | |
|
1732 | 0 | csa_offs_len = 0; |
1733 | 0 | if (hapd->csa_in_progress) { |
1734 | 0 | if (params.csa_pos) |
1735 | 0 | csa_offs[csa_offs_len++] = |
1736 | 0 | params.csa_pos - (u8 *) params.resp; |
1737 | |
|
1738 | 0 | if (params.ecsa_pos) |
1739 | 0 | csa_offs[csa_offs_len++] = |
1740 | 0 | params.ecsa_pos - (u8 *) params.resp; |
1741 | 0 | } |
1742 | |
|
1743 | 0 | ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), |
1744 | 0 | params.resp, params.resp_len, noack, |
1745 | 0 | csa_offs_len ? csa_offs : NULL, |
1746 | 0 | csa_offs_len, 0); |
1747 | |
|
1748 | 0 | if (ret < 0) |
1749 | 0 | wpa_printf(MSG_INFO, "handle_probe_req: send failed"); |
1750 | |
|
1751 | 0 | os_free(params.resp); |
1752 | |
|
1753 | 0 | wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " |
1754 | 0 | "SSID", MAC2STR(mgmt->sa), |
1755 | 0 | elems.ssid_len == 0 ? "broadcast" : "our"); |
1756 | 0 | } |
1757 | | |
1758 | | |
1759 | | static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, |
1760 | | size_t *resp_len) |
1761 | 0 | { |
1762 | 0 | struct probe_resp_params params; |
1763 | | |
1764 | | /* check probe response offloading caps and print warnings */ |
1765 | 0 | if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) |
1766 | 0 | return NULL; |
1767 | | |
1768 | 0 | #ifdef CONFIG_WPS |
1769 | 0 | if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && |
1770 | 0 | (!(hapd->iface->probe_resp_offloads & |
1771 | 0 | (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | |
1772 | 0 | WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) |
1773 | 0 | wpa_printf(MSG_WARNING, "Device is trying to offload WPS " |
1774 | 0 | "Probe Response while not supporting this"); |
1775 | 0 | #endif /* CONFIG_WPS */ |
1776 | |
|
1777 | | #ifdef CONFIG_P2P |
1778 | | if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && |
1779 | | !(hapd->iface->probe_resp_offloads & |
1780 | | WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) |
1781 | | wpa_printf(MSG_WARNING, "Device is trying to offload P2P " |
1782 | | "Probe Response while not supporting this"); |
1783 | | #endif /* CONFIG_P2P */ |
1784 | |
|
1785 | 0 | if (hapd->conf->interworking && |
1786 | 0 | !(hapd->iface->probe_resp_offloads & |
1787 | 0 | WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) |
1788 | 0 | wpa_printf(MSG_WARNING, "Device is trying to offload " |
1789 | 0 | "Interworking Probe Response while not supporting " |
1790 | 0 | "this"); |
1791 | | |
1792 | | /* Generate a Probe Response template for the non-P2P case */ |
1793 | 0 | os_memset(¶ms, 0, sizeof(params)); |
1794 | 0 | params.req = NULL; |
1795 | 0 | params.is_p2p = false; |
1796 | 0 | params.known_bss = NULL; |
1797 | 0 | params.known_bss_len = 0; |
1798 | 0 | params.mld_ap = NULL; |
1799 | 0 | params.mld_info = NULL; |
1800 | |
|
1801 | 0 | hostapd_gen_probe_resp(hapd, ¶ms); |
1802 | 0 | *resp_len = params.resp_len; |
1803 | 0 | if (!params.resp) |
1804 | 0 | return NULL; |
1805 | | |
1806 | | /* TODO: Avoid passing these through struct hostapd_data */ |
1807 | 0 | if (params.csa_pos) |
1808 | 0 | hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp; |
1809 | 0 | if (params.ecsa_pos) |
1810 | 0 | hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos - |
1811 | 0 | (u8 *) params.resp; |
1812 | | #ifdef CONFIG_IEEE80211AX |
1813 | | if (params.cca_pos) |
1814 | | hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp; |
1815 | | #endif /* CONFIG_IEEE80211AX */ |
1816 | |
|
1817 | 0 | return (u8 *) params.resp; |
1818 | 0 | } |
1819 | | |
1820 | | #endif /* NEED_AP_MLME */ |
1821 | | |
1822 | | |
1823 | | #ifdef CONFIG_IEEE80211AX |
1824 | | /* Unsolicited broadcast Probe Response transmission, 6 GHz only */ |
1825 | | u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd, |
1826 | | struct unsol_bcast_probe_resp *ubpr) |
1827 | | { |
1828 | | struct probe_resp_params probe_params; |
1829 | | |
1830 | | if (!is_6ghz_op_class(hapd->iconf->op_class)) |
1831 | | return NULL; |
1832 | | |
1833 | | ubpr->unsol_bcast_probe_resp_interval = |
1834 | | hapd->conf->unsol_bcast_probe_resp_interval; |
1835 | | |
1836 | | os_memset(&probe_params, 0, sizeof(probe_params)); |
1837 | | probe_params.req = NULL; |
1838 | | probe_params.is_p2p = false; |
1839 | | probe_params.known_bss = NULL; |
1840 | | probe_params.known_bss_len = 0; |
1841 | | probe_params.mld_ap = NULL; |
1842 | | probe_params.mld_info = NULL; |
1843 | | |
1844 | | hostapd_gen_probe_resp(hapd, &probe_params); |
1845 | | ubpr->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len; |
1846 | | return (u8 *) probe_params.resp; |
1847 | | } |
1848 | | #endif /* CONFIG_IEEE80211AX */ |
1849 | | |
1850 | | |
1851 | | void sta_track_del(struct hostapd_sta_info *info) |
1852 | 0 | { |
1853 | | #ifdef CONFIG_TAXONOMY |
1854 | | wpabuf_free(info->probe_ie_taxonomy); |
1855 | | info->probe_ie_taxonomy = NULL; |
1856 | | #endif /* CONFIG_TAXONOMY */ |
1857 | 0 | os_free(info); |
1858 | 0 | } |
1859 | | |
1860 | | |
1861 | | #ifdef CONFIG_FILS |
1862 | | |
1863 | | static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd) |
1864 | | { |
1865 | | #ifdef CONFIG_IEEE80211BE |
1866 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) |
1867 | | return FD_CAP_PHY_INDEX_EHT; |
1868 | | #endif /* CONFIG_IEEE80211BE */ |
1869 | | |
1870 | | #ifdef CONFIG_IEEE80211AX |
1871 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) |
1872 | | return FD_CAP_PHY_INDEX_HE; |
1873 | | #endif /* CONFIG_IEEE80211AX */ |
1874 | | |
1875 | | #ifdef CONFIG_IEEE80211AC |
1876 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) |
1877 | | return FD_CAP_PHY_INDEX_VHT; |
1878 | | #endif /* CONFIG_IEEE80211AC */ |
1879 | | |
1880 | | if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n) |
1881 | | return FD_CAP_PHY_INDEX_HT; |
1882 | | |
1883 | | return 0; |
1884 | | } |
1885 | | |
1886 | | |
1887 | | static u16 hostapd_gen_fils_discovery_nss(struct hostapd_hw_modes *mode, |
1888 | | u16 phy_index, u8 he_mcs_nss_size) |
1889 | | { |
1890 | | u16 nss = 0; |
1891 | | |
1892 | | if (!mode) |
1893 | | return 0; |
1894 | | |
1895 | | if (phy_index == FD_CAP_PHY_INDEX_HE) { |
1896 | | const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs; |
1897 | | int i; |
1898 | | u16 mcs[6]; |
1899 | | |
1900 | | os_memset(mcs, 0xff, 6 * sizeof(u16)); |
1901 | | |
1902 | | if (he_mcs_nss_size == 4) { |
1903 | | mcs[0] = WPA_GET_LE16(&he_mcs[0]); |
1904 | | mcs[1] = WPA_GET_LE16(&he_mcs[2]); |
1905 | | } |
1906 | | |
1907 | | if (he_mcs_nss_size == 8) { |
1908 | | mcs[2] = WPA_GET_LE16(&he_mcs[4]); |
1909 | | mcs[3] = WPA_GET_LE16(&he_mcs[6]); |
1910 | | } |
1911 | | |
1912 | | if (he_mcs_nss_size == 12) { |
1913 | | mcs[4] = WPA_GET_LE16(&he_mcs[8]); |
1914 | | mcs[5] = WPA_GET_LE16(&he_mcs[10]); |
1915 | | } |
1916 | | |
1917 | | for (i = 0; i < HE_NSS_MAX_STREAMS; i++) { |
1918 | | u16 nss_mask = 0x3 << (i * 2); |
1919 | | |
1920 | | /* |
1921 | | * If Tx and/or Rx indicate support for a given NSS, |
1922 | | * count it towards the maximum NSS. |
1923 | | */ |
1924 | | if (he_mcs_nss_size == 4 && |
1925 | | (((mcs[0] & nss_mask) != nss_mask) || |
1926 | | ((mcs[1] & nss_mask) != nss_mask))) { |
1927 | | nss++; |
1928 | | continue; |
1929 | | } |
1930 | | |
1931 | | if (he_mcs_nss_size == 8 && |
1932 | | (((mcs[2] & nss_mask) != nss_mask) || |
1933 | | ((mcs[3] & nss_mask) != nss_mask))) { |
1934 | | nss++; |
1935 | | continue; |
1936 | | } |
1937 | | |
1938 | | if (he_mcs_nss_size == 12 && |
1939 | | (((mcs[4] & nss_mask) != nss_mask) || |
1940 | | ((mcs[5] & nss_mask) != nss_mask))) { |
1941 | | nss++; |
1942 | | continue; |
1943 | | } |
1944 | | } |
1945 | | } else if (phy_index == FD_CAP_PHY_INDEX_EHT) { |
1946 | | u8 rx_nss, tx_nss, max_nss = 0, i; |
1947 | | u8 *mcs = mode->eht_capab[IEEE80211_MODE_AP].mcs; |
1948 | | |
1949 | | /* |
1950 | | * The Supported EHT-MCS And NSS Set field for the AP contains |
1951 | | * one to three EHT-MCS Map fields based on the supported |
1952 | | * bandwidth. Check the first byte (max NSS for Rx/Tx that |
1953 | | * supports EHT-MCS 0-9) for each bandwidth (<= 80, |
1954 | | * 160, 320) to find the maximum NSS. This assumes that |
1955 | | * the lowest MCS rates support the largest number of spatial |
1956 | | * streams. If values are different between Tx, Rx or the |
1957 | | * bandwidths, choose the highest value. |
1958 | | */ |
1959 | | for (i = 0; i < 3; i++) { |
1960 | | rx_nss = mcs[3 * i] & 0x0F; |
1961 | | if (rx_nss > max_nss) |
1962 | | max_nss = rx_nss; |
1963 | | |
1964 | | tx_nss = (mcs[3 * i] & 0xF0) >> 4; |
1965 | | if (tx_nss > max_nss) |
1966 | | max_nss = tx_nss; |
1967 | | } |
1968 | | |
1969 | | nss = max_nss; |
1970 | | } |
1971 | | |
1972 | | if (nss > 4) |
1973 | | return FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT; |
1974 | | if (nss) |
1975 | | return (nss - 1) << FD_CAP_NSS_SHIFT; |
1976 | | |
1977 | | return 0; |
1978 | | } |
1979 | | |
1980 | | |
1981 | | static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd) |
1982 | | { |
1983 | | u16 cap_info, phy_index; |
1984 | | u8 chwidth = FD_CAP_BSS_CHWIDTH_20, he_mcs_nss_size = 4; |
1985 | | struct hostapd_hw_modes *mode = hapd->iface->current_mode; |
1986 | | |
1987 | | cap_info = FD_CAP_ESS; |
1988 | | if (hapd->conf->wpa) |
1989 | | cap_info |= FD_CAP_PRIVACY; |
1990 | | |
1991 | | if (is_6ghz_op_class(hapd->iconf->op_class)) { |
1992 | | switch (hapd->iconf->op_class) { |
1993 | | case 137: |
1994 | | chwidth = FD_CAP_BSS_CHWIDTH_320; |
1995 | | break; |
1996 | | case 135: |
1997 | | he_mcs_nss_size += 4; |
1998 | | /* fallthrough */ |
1999 | | case 134: |
2000 | | he_mcs_nss_size += 4; |
2001 | | chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; |
2002 | | break; |
2003 | | case 133: |
2004 | | chwidth = FD_CAP_BSS_CHWIDTH_80; |
2005 | | break; |
2006 | | case 132: |
2007 | | chwidth = FD_CAP_BSS_CHWIDTH_40; |
2008 | | break; |
2009 | | } |
2010 | | } else { |
2011 | | switch (hostapd_get_oper_chwidth(hapd->iconf)) { |
2012 | | case CONF_OPER_CHWIDTH_80P80MHZ: |
2013 | | he_mcs_nss_size += 4; |
2014 | | /* fallthrough */ |
2015 | | case CONF_OPER_CHWIDTH_160MHZ: |
2016 | | he_mcs_nss_size += 4; |
2017 | | chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; |
2018 | | break; |
2019 | | case CONF_OPER_CHWIDTH_80MHZ: |
2020 | | chwidth = FD_CAP_BSS_CHWIDTH_80; |
2021 | | break; |
2022 | | case CONF_OPER_CHWIDTH_USE_HT: |
2023 | | if (hapd->iconf->secondary_channel) |
2024 | | chwidth = FD_CAP_BSS_CHWIDTH_40; |
2025 | | else |
2026 | | chwidth = FD_CAP_BSS_CHWIDTH_20; |
2027 | | break; |
2028 | | default: |
2029 | | break; |
2030 | | } |
2031 | | } |
2032 | | |
2033 | | phy_index = hostapd_gen_fils_discovery_phy_index(hapd); |
2034 | | cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT; |
2035 | | cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT; |
2036 | | cap_info |= hostapd_gen_fils_discovery_nss(mode, phy_index, |
2037 | | he_mcs_nss_size); |
2038 | | return cap_info; |
2039 | | } |
2040 | | |
2041 | | |
2042 | | static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len) |
2043 | | { |
2044 | | struct ieee80211_mgmt *head; |
2045 | | const u8 *mobility_domain; |
2046 | | u8 *pos, *length_pos, buf[200]; |
2047 | | u16 ctl = 0; |
2048 | | u8 fd_rsn_info[5]; |
2049 | | size_t total_len, buf_len; |
2050 | | |
2051 | | total_len = 24 + 2 + 12; |
2052 | | |
2053 | | /* FILS Discovery Frame Control */ |
2054 | | ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) | |
2055 | | FD_FRAME_CTL_SHORT_SSID_PRESENT | |
2056 | | FD_FRAME_CTL_LENGTH_PRESENT | |
2057 | | FD_FRAME_CTL_CAP_PRESENT; |
2058 | | total_len += 4 + 1 + 2; |
2059 | | |
2060 | | /* Fill primary channel information for 6 GHz channels with over 20 MHz |
2061 | | * bandwidth, if the primary channel is not a PSC */ |
2062 | | if (is_6ghz_op_class(hapd->iconf->op_class) && |
2063 | | !is_6ghz_psc_frequency(ieee80211_chan_to_freq( |
2064 | | NULL, hapd->iconf->op_class, |
2065 | | hapd->iconf->channel)) && |
2066 | | op_class_to_bandwidth(hapd->iconf->op_class) > 20) { |
2067 | | ctl |= FD_FRAME_CTL_PRI_CHAN_PRESENT; |
2068 | | total_len += 2; |
2069 | | } |
2070 | | |
2071 | | /* Check for optional subfields and calculate length */ |
2072 | | if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) { |
2073 | | ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT; |
2074 | | total_len += sizeof(fd_rsn_info); |
2075 | | } |
2076 | | |
2077 | | mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN); |
2078 | | if (mobility_domain) { |
2079 | | ctl |= FD_FRAME_CTL_MD_PRESENT; |
2080 | | total_len += 3; |
2081 | | } |
2082 | | |
2083 | | total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true); |
2084 | | |
2085 | | pos = hostapd_eid_fils_indic(hapd, buf, 0); |
2086 | | buf_len = pos - buf; |
2087 | | total_len += buf_len; |
2088 | | |
2089 | | /* he_elem_len() may return too large a value for FD frame, but that is |
2090 | | * fine here since this is used as the maximum length of the buffer. */ |
2091 | | total_len += he_elem_len(hapd); |
2092 | | |
2093 | | head = os_zalloc(total_len); |
2094 | | if (!head) |
2095 | | return NULL; |
2096 | | |
2097 | | head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
2098 | | WLAN_FC_STYPE_ACTION); |
2099 | | os_memset(head->da, 0xff, ETH_ALEN); |
2100 | | os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); |
2101 | | os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); |
2102 | | |
2103 | | head->u.action.category = WLAN_ACTION_PUBLIC; |
2104 | | head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY; |
2105 | | |
2106 | | pos = &head->u.action.u.public_action.variable[0]; |
2107 | | |
2108 | | /* FILS Discovery Information field */ |
2109 | | |
2110 | | /* FILS Discovery Frame Control */ |
2111 | | WPA_PUT_LE16(pos, ctl); |
2112 | | pos += 2; |
2113 | | |
2114 | | /* Hardware or low-level driver will fill in the Timestamp value */ |
2115 | | pos += 8; |
2116 | | |
2117 | | /* Beacon Interval */ |
2118 | | WPA_PUT_LE16(pos, hapd->iconf->beacon_int); |
2119 | | pos += 2; |
2120 | | |
2121 | | /* Short SSID */ |
2122 | | WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid); |
2123 | | pos += sizeof(hapd->conf->ssid.short_ssid); |
2124 | | |
2125 | | /* Store position of FILS discovery information element Length field */ |
2126 | | length_pos = pos++; |
2127 | | |
2128 | | /* FD Capability */ |
2129 | | WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd)); |
2130 | | pos += 2; |
2131 | | |
2132 | | /* Operating Class and Primary Channel - if a 6 GHz chan is non PSC */ |
2133 | | if (ctl & FD_FRAME_CTL_PRI_CHAN_PRESENT) { |
2134 | | *pos++ = hapd->iconf->op_class; |
2135 | | *pos++ = hapd->iconf->channel; |
2136 | | } |
2137 | | |
2138 | | /* AP Configuration Sequence Number - not present */ |
2139 | | |
2140 | | /* Access Network Options - not present */ |
2141 | | |
2142 | | /* FD RSN Information */ |
2143 | | if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) { |
2144 | | os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info)); |
2145 | | pos += sizeof(fd_rsn_info); |
2146 | | } |
2147 | | |
2148 | | /* Channel Center Frequency Segment 1 - not present */ |
2149 | | |
2150 | | /* Mobility Domain */ |
2151 | | if (ctl & FD_FRAME_CTL_MD_PRESENT) { |
2152 | | os_memcpy(pos, &mobility_domain[2], 3); |
2153 | | pos += 3; |
2154 | | } |
2155 | | |
2156 | | /* Fill in the Length field value */ |
2157 | | *length_pos = pos - (length_pos + 1); |
2158 | | |
2159 | | pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true); |
2160 | | |
2161 | | /* FILS Indication element */ |
2162 | | if (buf_len) { |
2163 | | os_memcpy(pos, buf, buf_len); |
2164 | | pos += buf_len; |
2165 | | } |
2166 | | |
2167 | | if (is_6ghz_op_class(hapd->iconf->op_class)) |
2168 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
2169 | | |
2170 | | *len = pos - (u8 *) head; |
2171 | | wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template", |
2172 | | head, pos - (u8 *) head); |
2173 | | return (u8 *) head; |
2174 | | } |
2175 | | |
2176 | | |
2177 | | /* Configure FILS Discovery frame transmission parameters */ |
2178 | | static u8 * hostapd_fils_discovery(struct hostapd_data *hapd, |
2179 | | struct wpa_driver_ap_params *params) |
2180 | | { |
2181 | | params->fd_max_int = hapd->conf->fils_discovery_max_int; |
2182 | | if (is_6ghz_op_class(hapd->iconf->op_class) && |
2183 | | params->fd_max_int > FD_MAX_INTERVAL_6GHZ) |
2184 | | params->fd_max_int = FD_MAX_INTERVAL_6GHZ; |
2185 | | |
2186 | | params->fd_min_int = hapd->conf->fils_discovery_min_int; |
2187 | | if (params->fd_min_int > params->fd_max_int) |
2188 | | params->fd_min_int = params->fd_max_int; |
2189 | | |
2190 | | if (params->fd_max_int) |
2191 | | return hostapd_gen_fils_discovery(hapd, |
2192 | | ¶ms->fd_frame_tmpl_len); |
2193 | | |
2194 | | return NULL; |
2195 | | } |
2196 | | |
2197 | | #endif /* CONFIG_FILS */ |
2198 | | |
2199 | | |
2200 | | int ieee802_11_build_ap_params(struct hostapd_data *hapd, |
2201 | | struct wpa_driver_ap_params *params) |
2202 | 0 | { |
2203 | 0 | struct ieee80211_mgmt *head = NULL; |
2204 | 0 | u8 *tail = NULL; |
2205 | 0 | size_t head_len = 0, tail_len = 0; |
2206 | 0 | u8 *resp = NULL; |
2207 | 0 | size_t resp_len = 0; |
2208 | 0 | #ifdef NEED_AP_MLME |
2209 | 0 | u16 capab_info; |
2210 | 0 | u8 *pos, *tailpos, *tailend, *csa_pos; |
2211 | 0 | bool complete = false; |
2212 | 0 | #endif /* NEED_AP_MLME */ |
2213 | |
|
2214 | 0 | os_memset(params, 0, sizeof(*params)); |
2215 | |
|
2216 | 0 | #ifdef NEED_AP_MLME |
2217 | 0 | #define BEACON_HEAD_BUF_SIZE 256 |
2218 | 0 | #define BEACON_TAIL_BUF_SIZE 1500 |
2219 | 0 | head = os_zalloc(BEACON_HEAD_BUF_SIZE); |
2220 | 0 | tail_len = BEACON_TAIL_BUF_SIZE; |
2221 | 0 | #ifdef CONFIG_WPS |
2222 | 0 | if (hapd->conf->wps_state && hapd->wps_beacon_ie) |
2223 | 0 | tail_len += wpabuf_len(hapd->wps_beacon_ie); |
2224 | 0 | #endif /* CONFIG_WPS */ |
2225 | | #ifdef CONFIG_P2P |
2226 | | if (hapd->p2p_beacon_ie) |
2227 | | tail_len += wpabuf_len(hapd->p2p_beacon_ie); |
2228 | | #endif /* CONFIG_P2P */ |
2229 | | #ifdef CONFIG_FST |
2230 | | if (hapd->iface->fst_ies) |
2231 | | tail_len += wpabuf_len(hapd->iface->fst_ies); |
2232 | | #endif /* CONFIG_FST */ |
2233 | 0 | if (hapd->conf->vendor_elements) |
2234 | 0 | tail_len += wpabuf_len(hapd->conf->vendor_elements); |
2235 | |
|
2236 | | #ifdef CONFIG_IEEE80211AC |
2237 | | if (hapd->conf->vendor_vht) { |
2238 | | tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + |
2239 | | 2 + sizeof(struct ieee80211_vht_operation); |
2240 | | } |
2241 | | #endif /* CONFIG_IEEE80211AC */ |
2242 | |
|
2243 | 0 | tail_len += he_elem_len(hapd); |
2244 | |
|
2245 | | #ifdef CONFIG_IEEE80211BE |
2246 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
2247 | | tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); |
2248 | | tail_len += 3 + sizeof(struct ieee80211_eht_operation); |
2249 | | if (hapd->iconf->punct_bitmap) |
2250 | | tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; |
2251 | | |
2252 | | /* |
2253 | | * TODO: Multi-Link element has variable length and can be |
2254 | | * long based on the common info and number of per |
2255 | | * station profiles. For now use 256. |
2256 | | */ |
2257 | | if (hapd->conf->mld_ap) { |
2258 | | tail_len += 256; |
2259 | | |
2260 | | /* for Max Channel Switch Time element during channel |
2261 | | * switch */ |
2262 | | tail_len += 6; |
2263 | | } |
2264 | | } |
2265 | | #endif /* CONFIG_IEEE80211BE */ |
2266 | |
|
2267 | 0 | if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && |
2268 | 0 | hapd == hostapd_mbssid_get_tx_bss(hapd)) |
2269 | 0 | tail_len += 5; /* Multiple BSSID Configuration element */ |
2270 | 0 | tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true); |
2271 | 0 | tail_len += hostapd_mbo_ie_len(hapd); |
2272 | 0 | tail_len += hostapd_eid_owe_trans_len(hapd); |
2273 | 0 | tail_len += hostapd_eid_dpp_cc_len(hapd); |
2274 | 0 | tail_len += hostapd_get_rsne_override_len(hapd); |
2275 | 0 | tail_len += hostapd_get_rsne_override_2_len(hapd); |
2276 | 0 | tail_len += hostapd_get_rsnxe_override_len(hapd); |
2277 | |
|
2278 | 0 | tailpos = tail = os_malloc(tail_len); |
2279 | 0 | if (head == NULL || tail == NULL) { |
2280 | 0 | wpa_printf(MSG_ERROR, "Failed to set beacon data"); |
2281 | 0 | os_free(head); |
2282 | 0 | os_free(tail); |
2283 | 0 | return -1; |
2284 | 0 | } |
2285 | 0 | tailend = tail + tail_len; |
2286 | |
|
2287 | 0 | head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
2288 | 0 | WLAN_FC_STYPE_BEACON); |
2289 | 0 | head->duration = host_to_le16(0); |
2290 | 0 | os_memset(head->da, 0xff, ETH_ALEN); |
2291 | |
|
2292 | 0 | os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); |
2293 | 0 | os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); |
2294 | 0 | head->u.beacon.beacon_int = |
2295 | 0 | host_to_le16(hapd->iconf->beacon_int); |
2296 | | |
2297 | | /* hardware or low-level driver will setup seq_ctrl and timestamp */ |
2298 | 0 | capab_info = hostapd_own_capab_info(hapd); |
2299 | 0 | head->u.beacon.capab_info = host_to_le16(capab_info); |
2300 | 0 | pos = &head->u.beacon.variable[0]; |
2301 | | |
2302 | | /* SSID */ |
2303 | 0 | *pos++ = WLAN_EID_SSID; |
2304 | 0 | if (hapd->conf->ignore_broadcast_ssid == 2) { |
2305 | | /* clear the data, but keep the correct length of the SSID */ |
2306 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
2307 | 0 | os_memset(pos, 0, hapd->conf->ssid.ssid_len); |
2308 | 0 | pos += hapd->conf->ssid.ssid_len; |
2309 | 0 | } else if (hapd->conf->ignore_broadcast_ssid) { |
2310 | 0 | *pos++ = 0; /* empty SSID */ |
2311 | 0 | } else { |
2312 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
2313 | 0 | os_memcpy(pos, hapd->conf->ssid.ssid, |
2314 | 0 | hapd->conf->ssid.ssid_len); |
2315 | 0 | pos += hapd->conf->ssid.ssid_len; |
2316 | 0 | } |
2317 | | |
2318 | | /* Supported rates */ |
2319 | 0 | pos = hostapd_eid_supp_rates(hapd, pos); |
2320 | | |
2321 | | /* DS Params */ |
2322 | 0 | pos = hostapd_eid_ds_params(hapd, pos); |
2323 | |
|
2324 | 0 | head_len = pos - (u8 *) head; |
2325 | |
|
2326 | 0 | tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos); |
2327 | | |
2328 | | /* Power Constraint element */ |
2329 | 0 | tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); |
2330 | | |
2331 | | /* CSA IE */ |
2332 | 0 | csa_pos = hostapd_eid_csa(hapd, tailpos); |
2333 | 0 | if (csa_pos != tailpos) |
2334 | 0 | hapd->cs_c_off_beacon = csa_pos - tail - 1; |
2335 | 0 | tailpos = csa_pos; |
2336 | | |
2337 | | /* ERP Information element */ |
2338 | 0 | tailpos = hostapd_eid_erp_info(hapd, tailpos); |
2339 | | |
2340 | | /* Extended supported rates */ |
2341 | 0 | tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); |
2342 | |
|
2343 | 0 | tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos); |
2344 | 0 | tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos); |
2345 | 0 | tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, |
2346 | 0 | tailend - tailpos); |
2347 | 0 | tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos); |
2348 | | |
2349 | | /* eCSA IE */ |
2350 | 0 | csa_pos = hostapd_eid_ecsa(hapd, tailpos); |
2351 | 0 | if (csa_pos != tailpos) |
2352 | 0 | hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; |
2353 | 0 | tailpos = csa_pos; |
2354 | |
|
2355 | 0 | tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); |
2356 | 0 | tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); |
2357 | 0 | tailpos = hostapd_eid_ht_operation(hapd, tailpos); |
2358 | |
|
2359 | 0 | if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) { |
2360 | 0 | if (ieee802_11_build_ap_params_mbssid(hapd, params)) { |
2361 | 0 | os_free(head); |
2362 | 0 | os_free(tail); |
2363 | 0 | wpa_printf(MSG_ERROR, |
2364 | 0 | "MBSSID: Failed to set beacon data"); |
2365 | 0 | return -1; |
2366 | 0 | } |
2367 | 0 | complete = hapd->iconf->mbssid == MBSSID_ENABLED || |
2368 | 0 | (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && |
2369 | 0 | params->mbssid.mbssid_elem_count == 1); |
2370 | 0 | } |
2371 | | |
2372 | 0 | tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete); |
2373 | | |
2374 | | /* |
2375 | | * TODO: Time Advertisement element should only be included in some |
2376 | | * DTIM Beacon frames. |
2377 | | */ |
2378 | 0 | tailpos = hostapd_eid_time_adv(hapd, tailpos); |
2379 | |
|
2380 | 0 | tailpos = hostapd_eid_interworking(hapd, tailpos); |
2381 | 0 | tailpos = hostapd_eid_adv_proto(hapd, tailpos); |
2382 | 0 | tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); |
2383 | |
|
2384 | | #ifdef CONFIG_FST |
2385 | | if (hapd->iface->fst_ies) { |
2386 | | os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), |
2387 | | wpabuf_len(hapd->iface->fst_ies)); |
2388 | | tailpos += wpabuf_len(hapd->iface->fst_ies); |
2389 | | } |
2390 | | #endif /* CONFIG_FST */ |
2391 | |
|
2392 | | #ifdef CONFIG_IEEE80211AC |
2393 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && |
2394 | | !is_6ghz_op_class(hapd->iconf->op_class)) { |
2395 | | tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); |
2396 | | tailpos = hostapd_eid_vht_operation(hapd, tailpos); |
2397 | | tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); |
2398 | | } |
2399 | | #endif /* CONFIG_IEEE80211AC */ |
2400 | |
|
2401 | | #ifdef CONFIG_IEEE80211AX |
2402 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && |
2403 | | is_6ghz_op_class(hapd->iconf->op_class)) |
2404 | | tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); |
2405 | | #endif /* CONFIG_IEEE80211AX */ |
2406 | |
|
2407 | 0 | tailpos = hostapd_eid_chsw_wrapper(hapd, tailpos); |
2408 | |
|
2409 | 0 | tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true); |
2410 | 0 | tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); |
2411 | | |
2412 | | /* Max Channel Switch Time element */ |
2413 | 0 | tailpos = hostapd_eid_max_cs_time(hapd, tailpos); |
2414 | |
|
2415 | 0 | tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos); |
2416 | 0 | tailpos = hostapd_eid_mbssid_config(hapd, tailpos, |
2417 | 0 | params->mbssid.mbssid_elem_count); |
2418 | |
|
2419 | | #ifdef CONFIG_IEEE80211AX |
2420 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { |
2421 | | u8 *cca_pos; |
2422 | | |
2423 | | tailpos = hostapd_eid_he_capab(hapd, tailpos, |
2424 | | IEEE80211_MODE_AP); |
2425 | | tailpos = hostapd_eid_he_operation(hapd, tailpos); |
2426 | | |
2427 | | /* BSS Color Change Announcement element */ |
2428 | | cca_pos = hostapd_eid_cca(hapd, tailpos); |
2429 | | if (cca_pos != tailpos) |
2430 | | hapd->cca_c_off_beacon = cca_pos - tail - 2; |
2431 | | tailpos = cca_pos; |
2432 | | |
2433 | | tailpos = hostapd_eid_spatial_reuse(hapd, tailpos); |
2434 | | tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); |
2435 | | tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos); |
2436 | | } |
2437 | | #endif /* CONFIG_IEEE80211AX */ |
2438 | |
|
2439 | | #ifdef CONFIG_IEEE80211BE |
2440 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
2441 | | if (hapd->conf->mld_ap) |
2442 | | tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL, |
2443 | | tailpos, false); |
2444 | | tailpos = hostapd_eid_eht_capab(hapd, tailpos, |
2445 | | IEEE80211_MODE_AP); |
2446 | | tailpos = hostapd_eid_eht_operation(hapd, tailpos); |
2447 | | } |
2448 | | #endif /* CONFIG_IEEE80211BE */ |
2449 | |
|
2450 | | #ifdef CONFIG_IEEE80211AC |
2451 | | if (hapd->conf->vendor_vht) |
2452 | | tailpos = hostapd_eid_vendor_vht(hapd, tailpos); |
2453 | | #endif /* CONFIG_IEEE80211AC */ |
2454 | | |
2455 | | /* WPA */ |
2456 | 0 | tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos); |
2457 | | |
2458 | | /* Wi-Fi Alliance WMM */ |
2459 | 0 | tailpos = hostapd_eid_wmm(hapd, tailpos); |
2460 | |
|
2461 | 0 | #ifdef CONFIG_WPS |
2462 | 0 | if (hapd->conf->wps_state && hapd->wps_beacon_ie) { |
2463 | 0 | os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie), |
2464 | 0 | wpabuf_len(hapd->wps_beacon_ie)); |
2465 | 0 | tailpos += wpabuf_len(hapd->wps_beacon_ie); |
2466 | 0 | } |
2467 | 0 | #endif /* CONFIG_WPS */ |
2468 | |
|
2469 | | #ifdef CONFIG_P2P |
2470 | | if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { |
2471 | | os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), |
2472 | | wpabuf_len(hapd->p2p_beacon_ie)); |
2473 | | tailpos += wpabuf_len(hapd->p2p_beacon_ie); |
2474 | | } |
2475 | | #endif /* CONFIG_P2P */ |
2476 | | #ifdef CONFIG_P2P_MANAGER |
2477 | | if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == |
2478 | | P2P_MANAGE) |
2479 | | tailpos = hostapd_eid_p2p_manage(hapd, tailpos); |
2480 | | #endif /* CONFIG_P2P_MANAGER */ |
2481 | |
|
2482 | 0 | #ifdef CONFIG_HS20 |
2483 | 0 | tailpos = hostapd_eid_hs20_indication(hapd, tailpos); |
2484 | 0 | #endif /* CONFIG_HS20 */ |
2485 | |
|
2486 | 0 | tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); |
2487 | 0 | tailpos = hostapd_eid_owe_trans(hapd, tailpos, |
2488 | 0 | tail + tail_len - tailpos); |
2489 | 0 | tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos); |
2490 | |
|
2491 | 0 | tailpos = hostapd_get_rsne_override(hapd, tailpos, |
2492 | 0 | tail + tail_len - tailpos); |
2493 | 0 | tailpos = hostapd_get_rsne_override_2(hapd, tailpos, |
2494 | 0 | tail + tail_len - tailpos); |
2495 | 0 | tailpos = hostapd_get_rsnxe_override(hapd, tailpos, |
2496 | 0 | tail + tail_len - tailpos); |
2497 | |
|
2498 | 0 | if (hapd->conf->vendor_elements) { |
2499 | 0 | os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), |
2500 | 0 | wpabuf_len(hapd->conf->vendor_elements)); |
2501 | 0 | tailpos += wpabuf_len(hapd->conf->vendor_elements); |
2502 | 0 | } |
2503 | |
|
2504 | 0 | tail_len = tailpos > tail ? tailpos - tail : 0; |
2505 | |
|
2506 | 0 | resp = hostapd_probe_resp_offloads(hapd, &resp_len); |
2507 | 0 | #endif /* NEED_AP_MLME */ |
2508 | | |
2509 | | /* If key management offload is enabled, configure PSK to the driver. */ |
2510 | 0 | if (wpa_key_mgmt_wpa_psk_no_sae(hapd->conf->wpa_key_mgmt) && |
2511 | 0 | (hapd->iface->drv_flags2 & |
2512 | 0 | WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) { |
2513 | 0 | if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) { |
2514 | 0 | os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk, |
2515 | 0 | PMK_LEN); |
2516 | 0 | params->psk_len = PMK_LEN; |
2517 | 0 | } else if (hapd->conf->ssid.wpa_passphrase && |
2518 | 0 | pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase, |
2519 | 0 | hapd->conf->ssid.ssid, |
2520 | 0 | hapd->conf->ssid.ssid_len, 4096, |
2521 | 0 | params->psk, PMK_LEN) == 0) { |
2522 | 0 | params->psk_len = PMK_LEN; |
2523 | 0 | } |
2524 | 0 | } |
2525 | |
|
2526 | | #ifdef CONFIG_SAE |
2527 | | /* If SAE offload is enabled, provide password to lower layer for |
2528 | | * SAE authentication and PMK generation. |
2529 | | */ |
2530 | | if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt | |
2531 | | hapd->conf->rsn_override_key_mgmt | |
2532 | | hapd->conf->rsn_override_key_mgmt_2) && |
2533 | | (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) { |
2534 | | if (hostapd_sae_pk_in_use(hapd->conf)) { |
2535 | | wpa_printf(MSG_ERROR, |
2536 | | "SAE PK not supported with SAE offload"); |
2537 | | return -1; |
2538 | | } |
2539 | | |
2540 | | if (hostapd_sae_pw_id_in_use(hapd->conf)) { |
2541 | | wpa_printf(MSG_ERROR, |
2542 | | "SAE Password Identifiers not supported with SAE offload"); |
2543 | | return -1; |
2544 | | } |
2545 | | |
2546 | | params->sae_password = sae_get_password(hapd, NULL, NULL, 0, |
2547 | | NULL, NULL, NULL); |
2548 | | if (!params->sae_password) { |
2549 | | wpa_printf(MSG_ERROR, "SAE password not configured for offload"); |
2550 | | return -1; |
2551 | | } |
2552 | | } |
2553 | | #endif /* CONFIG_SAE */ |
2554 | |
|
2555 | 0 | params->head = (u8 *) head; |
2556 | 0 | params->head_len = head_len; |
2557 | 0 | params->tail = tail; |
2558 | 0 | params->tail_len = tail_len; |
2559 | 0 | params->proberesp = resp; |
2560 | 0 | params->proberesp_len = resp_len; |
2561 | 0 | params->dtim_period = hapd->conf->dtim_period; |
2562 | 0 | params->beacon_int = hapd->iconf->beacon_int; |
2563 | 0 | params->basic_rates = hapd->iface->basic_rates; |
2564 | 0 | params->beacon_rate = hapd->iconf->beacon_rate; |
2565 | 0 | params->rate_type = hapd->iconf->rate_type; |
2566 | 0 | params->ssid = hapd->conf->ssid.ssid; |
2567 | 0 | params->ssid_len = hapd->conf->ssid.ssid_len; |
2568 | 0 | if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == |
2569 | 0 | (WPA_PROTO_WPA | WPA_PROTO_RSN)) |
2570 | 0 | params->pairwise_ciphers = hapd->conf->wpa_pairwise | |
2571 | 0 | hapd->conf->rsn_pairwise; |
2572 | 0 | else if (hapd->conf->wpa & WPA_PROTO_RSN) |
2573 | 0 | params->pairwise_ciphers = hapd->conf->rsn_pairwise; |
2574 | 0 | else if (hapd->conf->wpa & WPA_PROTO_WPA) |
2575 | 0 | params->pairwise_ciphers = hapd->conf->wpa_pairwise; |
2576 | 0 | params->group_cipher = hapd->conf->wpa_group; |
2577 | 0 | params->key_mgmt_suites = hapd->conf->wpa_key_mgmt | |
2578 | 0 | hapd->conf->rsn_override_key_mgmt | |
2579 | 0 | hapd->conf->rsn_override_key_mgmt_2; |
2580 | 0 | params->auth_algs = hapd->conf->auth_algs; |
2581 | 0 | params->wpa_version = hapd->conf->wpa; |
2582 | 0 | params->privacy = hapd->conf->wpa; |
2583 | | #ifdef CONFIG_WEP |
2584 | | params->privacy |= hapd->conf->ssid.wep.keys_set || |
2585 | | (hapd->conf->ieee802_1x && |
2586 | | (hapd->conf->default_wep_key_len || |
2587 | | hapd->conf->individual_wep_key_len)); |
2588 | | #endif /* CONFIG_WEP */ |
2589 | 0 | switch (hapd->conf->ignore_broadcast_ssid) { |
2590 | 0 | case 0: |
2591 | 0 | params->hide_ssid = NO_SSID_HIDING; |
2592 | 0 | break; |
2593 | 0 | case 1: |
2594 | 0 | params->hide_ssid = HIDDEN_SSID_ZERO_LEN; |
2595 | 0 | break; |
2596 | 0 | case 2: |
2597 | 0 | params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; |
2598 | 0 | break; |
2599 | 0 | } |
2600 | 0 | params->isolate = hapd->conf->isolate; |
2601 | 0 | #ifdef NEED_AP_MLME |
2602 | 0 | params->cts_protect = !!(ieee802_11_erp_info(hapd) & |
2603 | 0 | ERP_INFO_USE_PROTECTION); |
2604 | 0 | params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && |
2605 | 0 | hapd->iconf->preamble == SHORT_PREAMBLE; |
2606 | 0 | if (hapd->iface->current_mode && |
2607 | 0 | hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) |
2608 | 0 | params->short_slot_time = |
2609 | 0 | hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; |
2610 | 0 | else |
2611 | 0 | params->short_slot_time = -1; |
2612 | 0 | if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) |
2613 | 0 | params->ht_opmode = -1; |
2614 | 0 | else |
2615 | 0 | params->ht_opmode = hapd->iface->ht_op_mode; |
2616 | 0 | #endif /* NEED_AP_MLME */ |
2617 | 0 | params->interworking = hapd->conf->interworking; |
2618 | 0 | if (hapd->conf->interworking && |
2619 | 0 | !is_zero_ether_addr(hapd->conf->hessid)) |
2620 | 0 | params->hessid = hapd->conf->hessid; |
2621 | 0 | params->access_network_type = hapd->conf->access_network_type; |
2622 | 0 | params->ap_max_inactivity = hapd->conf->ap_max_inactivity; |
2623 | | #ifdef CONFIG_P2P |
2624 | | params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow; |
2625 | | #endif /* CONFIG_P2P */ |
2626 | 0 | #ifdef CONFIG_HS20 |
2627 | 0 | params->disable_dgaf = hapd->conf->disable_dgaf; |
2628 | 0 | #endif /* CONFIG_HS20 */ |
2629 | 0 | params->multicast_to_unicast = hapd->conf->multicast_to_unicast; |
2630 | 0 | params->pbss = hapd->conf->pbss; |
2631 | |
|
2632 | 0 | if (hapd->conf->ftm_responder) { |
2633 | 0 | if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { |
2634 | 0 | params->ftm_responder = 1; |
2635 | 0 | params->lci = hapd->iface->conf->lci; |
2636 | 0 | params->civic = hapd->iface->conf->civic; |
2637 | 0 | } else { |
2638 | 0 | wpa_printf(MSG_WARNING, |
2639 | 0 | "Not configuring FTM responder as the driver doesn't advertise support for it"); |
2640 | 0 | } |
2641 | 0 | } |
2642 | |
|
2643 | | #ifdef CONFIG_IEEE80211BE |
2644 | | if (hapd->conf->mld_ap && hapd->iconf->ieee80211be && |
2645 | | !hapd->conf->disable_11be) { |
2646 | | params->mld_ap = true; |
2647 | | params->mld_link_id = hapd->mld_link_id; |
2648 | | } |
2649 | | #endif /* CONFIG_IEEE80211BE */ |
2650 | |
|
2651 | 0 | return 0; |
2652 | 0 | } |
2653 | | |
2654 | | |
2655 | | void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) |
2656 | 0 | { |
2657 | 0 | os_free(params->tail); |
2658 | 0 | params->tail = NULL; |
2659 | 0 | os_free(params->head); |
2660 | 0 | params->head = NULL; |
2661 | 0 | os_free(params->proberesp); |
2662 | 0 | params->proberesp = NULL; |
2663 | 0 | os_free(params->mbssid.mbssid_elem); |
2664 | 0 | params->mbssid.mbssid_elem = NULL; |
2665 | 0 | os_free(params->mbssid.mbssid_elem_offset); |
2666 | 0 | params->mbssid.mbssid_elem_offset = NULL; |
2667 | 0 | os_free(params->mbssid.rnr_elem); |
2668 | 0 | params->mbssid.rnr_elem = NULL; |
2669 | 0 | os_free(params->mbssid.rnr_elem_offset); |
2670 | 0 | params->mbssid.rnr_elem_offset = NULL; |
2671 | | #ifdef CONFIG_FILS |
2672 | | os_free(params->fd_frame_tmpl); |
2673 | | params->fd_frame_tmpl = NULL; |
2674 | | #endif /* CONFIG_FILS */ |
2675 | | #ifdef CONFIG_IEEE80211AX |
2676 | | os_free(params->ubpr.unsol_bcast_probe_resp_tmpl); |
2677 | | params->ubpr.unsol_bcast_probe_resp_tmpl = NULL; |
2678 | | #endif /* CONFIG_IEEE80211AX */ |
2679 | 0 | os_free(params->allowed_freqs); |
2680 | 0 | params->allowed_freqs = NULL; |
2681 | 0 | } |
2682 | | |
2683 | | |
2684 | | static int __ieee802_11_set_beacon(struct hostapd_data *hapd) |
2685 | 0 | { |
2686 | 0 | struct wpa_driver_ap_params params; |
2687 | 0 | struct hostapd_freq_params freq; |
2688 | 0 | struct hostapd_iface *iface = hapd->iface; |
2689 | 0 | struct hostapd_config *iconf = iface->conf; |
2690 | 0 | struct hostapd_hw_modes *cmode = iface->current_mode; |
2691 | 0 | struct wpabuf *beacon, *proberesp, *assocresp; |
2692 | 0 | bool twt_he_responder = false; |
2693 | 0 | int res, ret = -1, i; |
2694 | 0 | struct hostapd_hw_modes *mode; |
2695 | |
|
2696 | 0 | if (!hapd->drv_priv) { |
2697 | 0 | wpa_printf(MSG_ERROR, "Interface is disabled"); |
2698 | 0 | return -1; |
2699 | 0 | } |
2700 | | |
2701 | 0 | if (hapd->csa_in_progress) { |
2702 | 0 | wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); |
2703 | 0 | return -1; |
2704 | 0 | } |
2705 | | |
2706 | | #ifdef CONFIG_IEEE80211AX |
2707 | | if (hapd->cca_in_progress) { |
2708 | | wpa_printf(MSG_ERROR, |
2709 | | "Cannot set beacons during CCA period"); |
2710 | | return -1; |
2711 | | } |
2712 | | #endif /* CONFIG_IEEE80211AX */ |
2713 | | |
2714 | 0 | hapd->beacon_set_done = 1; |
2715 | |
|
2716 | 0 | if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) |
2717 | 0 | return -1; |
2718 | | |
2719 | 0 | if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < |
2720 | 0 | 0) |
2721 | 0 | goto fail; |
2722 | | |
2723 | 0 | params.beacon_ies = beacon; |
2724 | 0 | params.proberesp_ies = proberesp; |
2725 | 0 | params.assocresp_ies = assocresp; |
2726 | 0 | params.reenable = hapd->reenable_beacon; |
2727 | | #ifdef CONFIG_IEEE80211AX |
2728 | | params.he_spr_ctrl = hapd->iface->conf->spr.sr_control; |
2729 | | params.he_spr_non_srg_obss_pd_max_offset = |
2730 | | hapd->iface->conf->spr.non_srg_obss_pd_max_offset; |
2731 | | params.he_spr_srg_obss_pd_min_offset = |
2732 | | hapd->iface->conf->spr.srg_obss_pd_min_offset; |
2733 | | params.he_spr_srg_obss_pd_max_offset = |
2734 | | hapd->iface->conf->spr.srg_obss_pd_max_offset; |
2735 | | os_memcpy(params.he_spr_bss_color_bitmap, |
2736 | | hapd->iface->conf->spr.srg_bss_color_bitmap, 8); |
2737 | | os_memcpy(params.he_spr_partial_bssid_bitmap, |
2738 | | hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8); |
2739 | | params.he_bss_color_disabled = |
2740 | | hapd->iface->conf->he_op.he_bss_color_disabled; |
2741 | | params.he_bss_color_partial = |
2742 | | hapd->iface->conf->he_op.he_bss_color_partial; |
2743 | | params.he_bss_color = hapd->iface->conf->he_op.he_bss_color; |
2744 | | twt_he_responder = hostapd_get_he_twt_responder(hapd, |
2745 | | IEEE80211_MODE_AP); |
2746 | | params.ubpr.unsol_bcast_probe_resp_tmpl = |
2747 | | hostapd_unsol_bcast_probe_resp(hapd, ¶ms.ubpr); |
2748 | | #endif /* CONFIG_IEEE80211AX */ |
2749 | 0 | params.twt_responder = |
2750 | 0 | twt_he_responder || hostapd_get_ht_vht_twt_responder(hapd); |
2751 | 0 | hapd->reenable_beacon = 0; |
2752 | | #ifdef CONFIG_SAE |
2753 | | params.sae_pwe = hapd->conf->sae_pwe; |
2754 | | #endif /* CONFIG_SAE */ |
2755 | |
|
2756 | | #ifdef CONFIG_FILS |
2757 | | params.fd_frame_tmpl = hostapd_fils_discovery(hapd, ¶ms); |
2758 | | #endif /* CONFIG_FILS */ |
2759 | |
|
2760 | | #ifdef CONFIG_IEEE80211BE |
2761 | | params.punct_bitmap = iconf->punct_bitmap; |
2762 | | #endif /* CONFIG_IEEE80211BE */ |
2763 | |
|
2764 | 0 | if (cmode && |
2765 | 0 | hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, |
2766 | 0 | iconf->channel, iconf->enable_edmg, |
2767 | 0 | iconf->edmg_channel, iconf->ieee80211n, |
2768 | 0 | iconf->ieee80211ac, iconf->ieee80211ax, |
2769 | 0 | iconf->ieee80211be, |
2770 | 0 | iconf->secondary_channel, |
2771 | 0 | hostapd_get_oper_chwidth(iconf), |
2772 | 0 | hostapd_get_oper_centr_freq_seg0_idx(iconf), |
2773 | 0 | hostapd_get_oper_centr_freq_seg1_idx(iconf), |
2774 | 0 | cmode->vht_capab, |
2775 | 0 | &cmode->he_capab[IEEE80211_MODE_AP], |
2776 | 0 | &cmode->eht_capab[IEEE80211_MODE_AP], |
2777 | 0 | hostapd_get_punct_bitmap(hapd)) == 0) { |
2778 | 0 | freq.link_id = -1; |
2779 | | #ifdef CONFIG_IEEE80211BE |
2780 | | if (hapd->conf->mld_ap) |
2781 | | freq.link_id = hapd->mld_link_id; |
2782 | | #endif /* CONFIG_IEEE80211BE */ |
2783 | 0 | params.freq = &freq; |
2784 | 0 | } |
2785 | |
|
2786 | 0 | for (i = 0; i < hapd->iface->num_hw_features; i++) { |
2787 | 0 | mode = &hapd->iface->hw_features[i]; |
2788 | |
|
2789 | 0 | if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY && |
2790 | 0 | iconf->hw_mode != mode->mode) |
2791 | 0 | continue; |
2792 | | |
2793 | 0 | hostapd_get_hw_mode_any_channels(hapd, mode, |
2794 | 0 | !(iconf->acs_freq_list.num || |
2795 | 0 | iconf->acs_ch_list.num), |
2796 | 0 | true, ¶ms.allowed_freqs); |
2797 | 0 | } |
2798 | |
|
2799 | 0 | res = hostapd_drv_set_ap(hapd, ¶ms); |
2800 | 0 | hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); |
2801 | 0 | if (res) |
2802 | 0 | wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); |
2803 | 0 | else |
2804 | 0 | ret = 0; |
2805 | 0 | fail: |
2806 | 0 | ieee802_11_free_ap_params(¶ms); |
2807 | 0 | return ret; |
2808 | 0 | } |
2809 | | |
2810 | | |
2811 | | void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd) |
2812 | 0 | { |
2813 | 0 | __ieee802_11_set_beacon(hapd); |
2814 | 0 | } |
2815 | | |
2816 | | |
2817 | | #ifdef CONFIG_IEEE80211BE |
2818 | | |
2819 | | static int hostapd_get_probe_resp_tmpl(struct hostapd_data *hapd, |
2820 | | struct probe_resp_params *params, |
2821 | | bool is_ml_sta_info) |
2822 | | { |
2823 | | os_memset(params, 0, sizeof(*params)); |
2824 | | hostapd_gen_probe_resp(hapd, params); |
2825 | | if (!params->resp) |
2826 | | return -1; |
2827 | | |
2828 | | /* The caller takes care of freeing params->resp. */ |
2829 | | return 0; |
2830 | | } |
2831 | | |
2832 | | |
2833 | | static bool is_restricted_eid_in_sta_profile(u8 eid, bool tx_vap) |
2834 | | { |
2835 | | switch (eid) { |
2836 | | case WLAN_EID_TIM: |
2837 | | case WLAN_EID_BSS_MAX_IDLE_PERIOD: |
2838 | | case WLAN_EID_MULTIPLE_BSSID: |
2839 | | case WLAN_EID_REDUCED_NEIGHBOR_REPORT: |
2840 | | case WLAN_EID_NEIGHBOR_REPORT: |
2841 | | return true; |
2842 | | case WLAN_EID_SSID: |
2843 | | /* SSID is not restricted for non-transmitted BSSID */ |
2844 | | return tx_vap; |
2845 | | default: |
2846 | | return false; |
2847 | | } |
2848 | | } |
2849 | | |
2850 | | |
2851 | | static bool is_restricted_ext_eid_in_sta_profile(u8 ext_id) |
2852 | | { |
2853 | | switch (ext_id) { |
2854 | | case WLAN_EID_EXT_MULTI_LINK: |
2855 | | return true; |
2856 | | default: |
2857 | | return false; |
2858 | | } |
2859 | | } |
2860 | | |
2861 | | |
2862 | | /* Create the link STA profiles based on inheritance from the reporting |
2863 | | * profile. |
2864 | | * |
2865 | | * NOTE: The same function is used for length calculation as well as filling |
2866 | | * data in the given buffer. This avoids risk of not updating the length |
2867 | | * function but filling function or vice versa. |
2868 | | */ |
2869 | | static size_t hostapd_add_sta_profile(struct ieee80211_mgmt *link_fdata, |
2870 | | size_t link_data_len, |
2871 | | struct ieee80211_mgmt *own_fdata, |
2872 | | size_t own_data_len, |
2873 | | u8 *sta_profile, bool tx_vap) |
2874 | | { |
2875 | | const struct element *link_elem; |
2876 | | size_t sta_profile_len = 0; |
2877 | | const u8 *link_elem_data; |
2878 | | u8 link_ele_len; |
2879 | | u8 *link_data; |
2880 | | const struct element *own_elem; |
2881 | | u8 link_eid, own_eid, own_ele_len; |
2882 | | const u8 *own_elem_data; |
2883 | | u8 *own_data; |
2884 | | bool is_ext; |
2885 | | bool ie_found; |
2886 | | u8 non_inherit_ele_ext_list[256] = { 0 }; |
2887 | | u8 non_inherit_ele_ext_list_len = 0; |
2888 | | u8 non_inherit_ele_list[256] = { 0 }; |
2889 | | u8 non_inherit_ele_list_len = 0; |
2890 | | u8 num_link_elem_vendor_ies = 0, num_own_elem_vendor_ies = 0; |
2891 | | bool add_vendor_ies = false, is_identical_vendor_ies = true; |
2892 | | /* The bitmap of parsed EIDs. There are 256 EIDs and ext EIDs, so 32 |
2893 | | * bytes to store the bitmaps. */ |
2894 | | u8 parsed_eid_bmap[32] = { 0 }, parsed_ext_eid_bmap[32] = { 0 }; |
2895 | | /* extra len used in the logic includes the element id and len */ |
2896 | | u8 extra_len = 2; |
2897 | | |
2898 | | /* Include len for capab info */ |
2899 | | sta_profile_len += sizeof(le16); |
2900 | | if (sta_profile) { |
2901 | | os_memcpy(sta_profile, &link_fdata->u.probe_resp.capab_info, |
2902 | | sizeof(le16)); |
2903 | | sta_profile += sizeof(le16); |
2904 | | } |
2905 | | |
2906 | | own_data = own_fdata->u.probe_resp.variable; |
2907 | | link_data = link_fdata->u.probe_resp.variable; |
2908 | | |
2909 | | /* The below logic takes the reporting BSS data and reported BSS data |
2910 | | * and performs intersection to build the STA profile of the reported |
2911 | | * BSS. Certain elements are not added to the STA profile as |
2912 | | * recommended in standard. Matching element information in the |
2913 | | * reporting BSS profile are ignored in the STA profile. Remaining |
2914 | | * elements pertaining to the STA profile are appended at the end. */ |
2915 | | for_each_element(own_elem, own_data, own_data_len) { |
2916 | | is_ext = false; |
2917 | | ie_found = false; |
2918 | | |
2919 | | /* Pick one of own elements and get its EID and length */ |
2920 | | own_elem_data = own_elem->data; |
2921 | | own_ele_len = own_elem->datalen; |
2922 | | |
2923 | | if (own_elem->id == WLAN_EID_EXTENSION) { |
2924 | | is_ext = true; |
2925 | | own_eid = *(own_elem_data); |
2926 | | if (is_restricted_ext_eid_in_sta_profile(own_eid)) |
2927 | | continue; |
2928 | | } else { |
2929 | | own_eid = own_elem->id; |
2930 | | if (is_restricted_eid_in_sta_profile(own_eid, tx_vap)) |
2931 | | continue; |
2932 | | } |
2933 | | |
2934 | | for_each_element(link_elem, link_data, link_data_len) { |
2935 | | /* If the element type mismatches, do not consider |
2936 | | * this link element for comparison. */ |
2937 | | if ((link_elem->id == WLAN_EID_EXTENSION && |
2938 | | !is_ext) || |
2939 | | (is_ext && link_elem->id != WLAN_EID_EXTENSION)) |
2940 | | continue; |
2941 | | |
2942 | | /* Comparison can be done so get the link element and |
2943 | | * its EID and length. */ |
2944 | | link_elem_data = link_elem->data; |
2945 | | link_ele_len = link_elem->datalen; |
2946 | | |
2947 | | if (link_elem->id == WLAN_EID_EXTENSION) |
2948 | | link_eid = *(link_elem_data); |
2949 | | else |
2950 | | link_eid = link_elem->id; |
2951 | | |
2952 | | /* Ignore if EID does not match */ |
2953 | | if (own_eid != link_eid) |
2954 | | continue; |
2955 | | |
2956 | | ie_found = true; |
2957 | | |
2958 | | /* Ignore if the contents is identical. */ |
2959 | | if (own_ele_len == link_ele_len && |
2960 | | os_memcmp(own_elem->data, link_elem->data, |
2961 | | own_ele_len) == 0) { |
2962 | | if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { |
2963 | | is_identical_vendor_ies = true; |
2964 | | num_own_elem_vendor_ies++; |
2965 | | } |
2966 | | |
2967 | | /* Update the parsed EIDs bitmap */ |
2968 | | if (is_ext) |
2969 | | parsed_ext_eid_bmap[own_eid / 8] |= |
2970 | | BIT(own_eid % 8); |
2971 | | else |
2972 | | parsed_eid_bmap[own_eid / 8] |= |
2973 | | BIT(own_eid % 8); |
2974 | | break; |
2975 | | } |
2976 | | |
2977 | | /* No need to include this non-matching Vendor Specific |
2978 | | * element explicitly at this point. */ |
2979 | | if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { |
2980 | | is_identical_vendor_ies = false; |
2981 | | continue; |
2982 | | } |
2983 | | |
2984 | | /* This element is present in the reported profile |
2985 | | * as well as present in the reporting profile. |
2986 | | * However, there is a mismatch in the contents and |
2987 | | * hence, include this in the per STA profile. */ |
2988 | | sta_profile_len += link_ele_len + extra_len; |
2989 | | if (sta_profile) { |
2990 | | os_memcpy(sta_profile, |
2991 | | link_elem->data - extra_len, |
2992 | | link_ele_len + extra_len); |
2993 | | sta_profile += link_ele_len + extra_len; |
2994 | | } |
2995 | | |
2996 | | /* Update the parsed EIDs bitmap */ |
2997 | | if (is_ext) |
2998 | | parsed_ext_eid_bmap[own_eid / 8] |= |
2999 | | BIT(own_eid % 8); |
3000 | | else |
3001 | | parsed_eid_bmap[own_eid / 8] |= |
3002 | | BIT(own_eid % 8); |
3003 | | break; |
3004 | | } |
3005 | | |
3006 | | /* We found at least one Vendor Specific element in reporting |
3007 | | * link which is not same (or present) in the reported link. We |
3008 | | * need to include all Vendor Specific elements from the |
3009 | | * reported link. */ |
3010 | | if (!is_identical_vendor_ies) |
3011 | | add_vendor_ies = true; |
3012 | | |
3013 | | /* This is a unique element in the reporting profile which is |
3014 | | * not present in the reported profile. Update the |
3015 | | * non-inheritance list. */ |
3016 | | if (!ie_found) { |
3017 | | u8 idx; |
3018 | | |
3019 | | if (is_ext) { |
3020 | | idx = non_inherit_ele_ext_list_len++; |
3021 | | non_inherit_ele_ext_list[idx] = own_eid; |
3022 | | } else { |
3023 | | idx = non_inherit_ele_list_len++; |
3024 | | non_inherit_ele_list[idx] = own_eid; |
3025 | | } |
3026 | | } |
3027 | | } |
3028 | | |
3029 | | /* Parse the remaining elements in the reported profile */ |
3030 | | for_each_element(link_elem, link_data, link_data_len) { |
3031 | | link_elem_data = link_elem->data; |
3032 | | link_ele_len = link_elem->datalen; |
3033 | | |
3034 | | /* No need to check this Vendor Specific element at this point. |
3035 | | * Just take the count and continue. */ |
3036 | | if (link_elem->id == WLAN_EID_VENDOR_SPECIFIC) { |
3037 | | num_link_elem_vendor_ies++; |
3038 | | continue; |
3039 | | } |
3040 | | |
3041 | | if (link_elem->id == WLAN_EID_EXTENSION) { |
3042 | | link_eid = *(link_elem_data); |
3043 | | |
3044 | | if ((parsed_ext_eid_bmap[link_eid / 8] & |
3045 | | BIT(link_eid % 8)) || |
3046 | | is_restricted_ext_eid_in_sta_profile(link_eid)) |
3047 | | continue; |
3048 | | } else { |
3049 | | link_eid = link_elem->id; |
3050 | | |
3051 | | if ((parsed_eid_bmap[link_eid / 8] & |
3052 | | BIT(link_eid % 8)) || |
3053 | | is_restricted_eid_in_sta_profile(link_eid, tx_vap)) |
3054 | | continue; |
3055 | | } |
3056 | | |
3057 | | sta_profile_len += link_ele_len + extra_len; |
3058 | | if (sta_profile) { |
3059 | | os_memcpy(sta_profile, link_elem_data - extra_len, |
3060 | | link_ele_len + extra_len); |
3061 | | sta_profile += link_ele_len + extra_len; |
3062 | | } |
3063 | | } |
3064 | | |
3065 | | /* Handle Vendor Specific elements |
3066 | | * Add all the Vendor Specific elements of the reported link if |
3067 | | * a. There is at least one non-matching Vendor Specific element, or |
3068 | | * b. The number of Vendor Specific elements in reporting and reported |
3069 | | * link is not same. */ |
3070 | | if (add_vendor_ies || |
3071 | | num_own_elem_vendor_ies != num_link_elem_vendor_ies) { |
3072 | | for_each_element(link_elem, link_data, link_data_len) { |
3073 | | link_elem_data = link_elem->data; |
3074 | | link_ele_len = link_elem->datalen; |
3075 | | |
3076 | | if (link_elem->id != WLAN_EID_VENDOR_SPECIFIC) |
3077 | | continue; |
3078 | | |
3079 | | sta_profile_len += link_ele_len + extra_len; |
3080 | | if (sta_profile) { |
3081 | | os_memcpy(sta_profile, |
3082 | | link_elem_data - extra_len, |
3083 | | link_ele_len + extra_len); |
3084 | | sta_profile += link_ele_len + extra_len; |
3085 | | } |
3086 | | } |
3087 | | } |
3088 | | |
3089 | | /* Handle non-inheritance |
3090 | | * Non-Inheritance element: |
3091 | | * Element ID Ext: 1 octet |
3092 | | * Length: 1 octet |
3093 | | * Ext tag number: 1 octet |
3094 | | * Length of Elements ID list: 1 octet |
3095 | | * Elements ID list: variable |
3096 | | * Length of Elements ID Extension list: 1 octet |
3097 | | * Elements ID extensions list: variable |
3098 | | */ |
3099 | | if (non_inherit_ele_list_len || non_inherit_ele_ext_list_len) |
3100 | | sta_profile_len += 3 + 2 + non_inherit_ele_list_len + |
3101 | | non_inherit_ele_ext_list_len; |
3102 | | |
3103 | | if (sta_profile && |
3104 | | (non_inherit_ele_list_len || non_inherit_ele_ext_list_len)) { |
3105 | | *sta_profile++ = WLAN_EID_EXTENSION; |
3106 | | *sta_profile++ = non_inherit_ele_list_len + |
3107 | | non_inherit_ele_ext_list_len + 3; |
3108 | | *sta_profile++ = WLAN_EID_EXT_NON_INHERITANCE; |
3109 | | *sta_profile++ = non_inherit_ele_list_len; |
3110 | | os_memcpy(sta_profile, non_inherit_ele_list, |
3111 | | non_inherit_ele_list_len); |
3112 | | sta_profile += non_inherit_ele_list_len; |
3113 | | *sta_profile++ = non_inherit_ele_ext_list_len; |
3114 | | os_memcpy(sta_profile, non_inherit_ele_ext_list, |
3115 | | non_inherit_ele_ext_list_len); |
3116 | | sta_profile += non_inherit_ele_ext_list_len; |
3117 | | } |
3118 | | |
3119 | | return sta_profile_len; |
3120 | | } |
3121 | | |
3122 | | |
3123 | | static u8 * hostapd_gen_sta_profile(struct ieee80211_mgmt *link_data, |
3124 | | size_t link_data_len, |
3125 | | struct ieee80211_mgmt *own_data, |
3126 | | size_t own_data_len, |
3127 | | size_t *sta_profile_len, bool tx_vap) |
3128 | | { |
3129 | | u8 *sta_profile; |
3130 | | |
3131 | | /* Get the length first */ |
3132 | | *sta_profile_len = hostapd_add_sta_profile(link_data, link_data_len, |
3133 | | own_data, own_data_len, |
3134 | | NULL, tx_vap); |
3135 | | if (!(*sta_profile_len) || *sta_profile_len > EHT_ML_MAX_STA_PROF_LEN) |
3136 | | return NULL; |
3137 | | |
3138 | | sta_profile = os_zalloc(*sta_profile_len); |
3139 | | if (!sta_profile) |
3140 | | return NULL; |
3141 | | |
3142 | | /* Now fill in the data */ |
3143 | | hostapd_add_sta_profile(link_data, link_data_len, own_data, |
3144 | | own_data_len, sta_profile, tx_vap); |
3145 | | |
3146 | | /* The caller takes care of freeing the returned sta_profile */ |
3147 | | return sta_profile; |
3148 | | } |
3149 | | |
3150 | | |
3151 | | static void hostapd_gen_per_sta_profiles(struct hostapd_data *hapd) |
3152 | | { |
3153 | | bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd); |
3154 | | size_t link_data_len, sta_profile_len; |
3155 | | size_t own_data_len, fixed; |
3156 | | struct probe_resp_params link_params; |
3157 | | struct probe_resp_params own_params; |
3158 | | struct ieee80211_mgmt *link_data; |
3159 | | struct ieee80211_mgmt *own_data; |
3160 | | struct mld_link_info *link_info; |
3161 | | struct hostapd_data *link_bss; |
3162 | | u8 link_id, *sta_profile; |
3163 | | |
3164 | | if (!hapd->conf->mld_ap || !hapd->started) |
3165 | | return; |
3166 | | |
3167 | | wpa_printf(MSG_DEBUG, "MLD: Generating per STA profiles for MLD %s", |
3168 | | hapd->conf->iface); |
3169 | | |
3170 | | wpa_printf(MSG_DEBUG, "MLD: Reporting link %d", hapd->mld_link_id); |
3171 | | |
3172 | | /* Generate a Probe Response template for self */ |
3173 | | if (hostapd_get_probe_resp_tmpl(hapd, &own_params, false)) { |
3174 | | wpa_printf(MSG_ERROR, |
3175 | | "MLD: Error in building per STA profiles"); |
3176 | | return; |
3177 | | } |
3178 | | |
3179 | | own_data = own_params.resp; |
3180 | | own_data_len = own_params.resp_len; |
3181 | | |
3182 | | /* Consider the length of the variable fields */ |
3183 | | fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
3184 | | if (own_data_len < fixed) |
3185 | | goto fail; |
3186 | | own_data_len -= fixed; |
3187 | | |
3188 | | for_each_mld_link(link_bss, hapd) { |
3189 | | if (link_bss == hapd || !link_bss->started) |
3190 | | continue; |
3191 | | |
3192 | | link_id = link_bss->mld_link_id; |
3193 | | if (link_id >= MAX_NUM_MLD_LINKS) |
3194 | | continue; |
3195 | | |
3196 | | sta_profile = NULL; |
3197 | | sta_profile_len = 0; |
3198 | | |
3199 | | /* Generate a Probe Response frame template for partner link */ |
3200 | | if (hostapd_get_probe_resp_tmpl(link_bss, &link_params, true)) { |
3201 | | wpa_printf(MSG_ERROR, |
3202 | | "MLD: Could not get link STA probe response template for link %d", |
3203 | | link_id); |
3204 | | continue; |
3205 | | } |
3206 | | |
3207 | | link_data = link_params.resp; |
3208 | | link_data_len = link_params.resp_len; |
3209 | | |
3210 | | /* Consider length of the variable fields */ |
3211 | | fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
3212 | | if (link_data_len < fixed) |
3213 | | continue; |
3214 | | link_data_len -= fixed; |
3215 | | |
3216 | | sta_profile = hostapd_gen_sta_profile(link_data, link_data_len, |
3217 | | own_data, own_data_len, |
3218 | | &sta_profile_len, tx_vap); |
3219 | | if (!sta_profile) { |
3220 | | wpa_printf(MSG_ERROR, |
3221 | | "MLD: Could not generate link STA profile for link %d", |
3222 | | link_id); |
3223 | | continue; |
3224 | | } |
3225 | | |
3226 | | link_info = &hapd->partner_links[link_id]; |
3227 | | link_info->valid = true; |
3228 | | |
3229 | | os_free(link_info->resp_sta_profile); |
3230 | | link_info->resp_sta_profile_len = sta_profile_len; |
3231 | | |
3232 | | link_info->resp_sta_profile = os_memdup(sta_profile, |
3233 | | sta_profile_len); |
3234 | | if (!link_info->resp_sta_profile) |
3235 | | link_info->resp_sta_profile_len = 0; |
3236 | | |
3237 | | os_memcpy(link_info->local_addr, link_bss->own_addr, ETH_ALEN); |
3238 | | |
3239 | | wpa_printf(MSG_DEBUG, |
3240 | | "MLD: Reported link STA info for %d: %u bytes", |
3241 | | link_id, link_info->resp_sta_profile_len); |
3242 | | |
3243 | | os_free(sta_profile); |
3244 | | os_free(link_params.resp); |
3245 | | } |
3246 | | |
3247 | | fail: |
3248 | | os_free(own_params.resp); |
3249 | | } |
3250 | | |
3251 | | #endif /* CONFIG_IEEE80211BE */ |
3252 | | |
3253 | | |
3254 | | int ieee802_11_set_beacon(struct hostapd_data *hapd) |
3255 | 0 | { |
3256 | 0 | struct hostapd_iface *iface = hapd->iface; |
3257 | 0 | int ret; |
3258 | 0 | size_t i, j; |
3259 | 0 | bool is_6g, hapd_mld = false; |
3260 | | #ifdef CONFIG_IEEE80211BE |
3261 | | struct hostapd_data *link_bss; |
3262 | | #endif /* CONFIG_IEEE80211BE */ |
3263 | |
|
3264 | 0 | ret = __ieee802_11_set_beacon(hapd); |
3265 | 0 | if (ret != 0) |
3266 | 0 | return ret; |
3267 | | |
3268 | 0 | if (!iface->interfaces || iface->interfaces->count <= 1) |
3269 | 0 | return 0; |
3270 | | |
3271 | | #ifdef CONFIG_IEEE80211BE |
3272 | | hapd_mld = hapd->conf->mld_ap; |
3273 | | #endif /* CONFIG_IEEE80211BE */ |
3274 | | |
3275 | | /* Update Beacon frames in case of 6 GHz colocation or AP MLD */ |
3276 | 0 | is_6g = is_6ghz_op_class(iface->conf->op_class); |
3277 | 0 | for (j = 0; j < iface->interfaces->count; j++) { |
3278 | 0 | struct hostapd_iface *other; |
3279 | 0 | bool other_iface_6g; |
3280 | |
|
3281 | 0 | other = iface->interfaces->iface[j]; |
3282 | 0 | if (other == iface || !other || !other->conf) |
3283 | 0 | continue; |
3284 | | |
3285 | 0 | other_iface_6g = is_6ghz_op_class(other->conf->op_class); |
3286 | |
|
3287 | 0 | if (is_6g == other_iface_6g && !hapd_mld) |
3288 | 0 | continue; |
3289 | | |
3290 | 0 | for (i = 0; i < other->num_bss; i++) { |
3291 | | #ifdef CONFIG_IEEE80211BE |
3292 | | if (is_6g == other_iface_6g && |
3293 | | !(hapd_mld && other->bss[i]->conf->mld_ap && |
3294 | | hostapd_is_ml_partner(hapd, other->bss[i]))) |
3295 | | continue; |
3296 | | #endif /* CONFIG_IEEE80211BE */ |
3297 | |
|
3298 | 0 | if (other->bss[i] && other->bss[i]->started && |
3299 | 0 | other->bss[i]->beacon_set_done) |
3300 | 0 | __ieee802_11_set_beacon(other->bss[i]); |
3301 | 0 | } |
3302 | 0 | } |
3303 | |
|
3304 | | #ifdef CONFIG_IEEE80211BE |
3305 | | if (!hapd_mld) |
3306 | | return 0; |
3307 | | |
3308 | | /* Generate per STA profiles for each affiliated APs */ |
3309 | | for_each_mld_link(link_bss, hapd) |
3310 | | hostapd_gen_per_sta_profiles(link_bss); |
3311 | | #endif /* CONFIG_IEEE80211BE */ |
3312 | |
|
3313 | 0 | return 0; |
3314 | 0 | } |
3315 | | |
3316 | | |
3317 | | int ieee802_11_set_beacons(struct hostapd_iface *iface) |
3318 | 0 | { |
3319 | 0 | size_t i; |
3320 | 0 | int ret = 0; |
3321 | |
|
3322 | 0 | for (i = 0; i < iface->num_bss; i++) { |
3323 | 0 | if (iface->bss[i]->started && |
3324 | 0 | ieee802_11_set_beacon(iface->bss[i]) < 0) |
3325 | 0 | ret = -1; |
3326 | 0 | } |
3327 | |
|
3328 | 0 | return ret; |
3329 | 0 | } |
3330 | | |
3331 | | |
3332 | | /* only update beacons if started */ |
3333 | | int ieee802_11_update_beacons(struct hostapd_iface *iface) |
3334 | 0 | { |
3335 | 0 | size_t i; |
3336 | 0 | int ret = 0; |
3337 | |
|
3338 | 0 | for (i = 0; i < iface->num_bss; i++) { |
3339 | 0 | if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && |
3340 | 0 | ieee802_11_set_beacon(iface->bss[i]) < 0) |
3341 | 0 | ret = -1; |
3342 | 0 | } |
3343 | |
|
3344 | 0 | return ret; |
3345 | 0 | } |
3346 | | |
3347 | | #endif /* CONFIG_NATIVE_WINDOWS */ |