/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 = hapd; |
742 | 0 | size_t buflen = 0; |
743 | |
|
744 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
745 | |
|
746 | 0 | #ifdef CONFIG_WPS |
747 | 0 | if (hapd->wps_probe_resp_ie) |
748 | 0 | buflen += wpabuf_len(hapd->wps_probe_resp_ie); |
749 | 0 | #endif /* CONFIG_WPS */ |
750 | | #ifdef CONFIG_P2P |
751 | | if (hapd->p2p_probe_resp_ie) |
752 | | buflen += wpabuf_len(hapd->p2p_probe_resp_ie); |
753 | | #endif /* CONFIG_P2P */ |
754 | | #ifdef CONFIG_FST |
755 | | if (hapd->iface->fst_ies) |
756 | | buflen += wpabuf_len(hapd->iface->fst_ies); |
757 | | #endif /* CONFIG_FST */ |
758 | 0 | if (hapd->conf->vendor_elements) |
759 | 0 | buflen += wpabuf_len(hapd->conf->vendor_elements); |
760 | | #ifdef CONFIG_TESTING_OPTIONS |
761 | | if (hapd->conf->presp_elements) |
762 | | buflen += wpabuf_len(hapd->conf->presp_elements); |
763 | | #endif /* CONFIG_TESTING_OPTIONS */ |
764 | 0 | if (hapd->conf->vendor_vht) { |
765 | 0 | buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + |
766 | 0 | 2 + sizeof(struct ieee80211_vht_operation); |
767 | 0 | } |
768 | |
|
769 | 0 | buflen += he_elem_len(hapd); |
770 | |
|
771 | | #ifdef CONFIG_IEEE80211BE |
772 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
773 | | struct hostapd_data *ml_elem_ap = |
774 | | params->mld_ap ? params->mld_ap : hapd; |
775 | | |
776 | | buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); |
777 | | buflen += 3 + sizeof(struct ieee80211_eht_operation); |
778 | | if (hapd->iconf->punct_bitmap) |
779 | | buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; |
780 | | |
781 | | if (ml_elem_ap->conf->mld_ap) { |
782 | | buflen += hostapd_eid_eht_ml_beacon_len( |
783 | | ml_elem_ap, params->mld_info, !!params->mld_ap); |
784 | | |
785 | | /* For Max Channel Switch Time element during channel |
786 | | * switch */ |
787 | | buflen += 6; |
788 | | } |
789 | | |
790 | | if (hapd_probed != hapd && hapd_probed->conf->mld_ap) |
791 | | buflen += hostapd_eid_eht_basic_ml_len(hapd_probed, |
792 | | NULL, true, |
793 | | false); |
794 | | } |
795 | | #endif /* CONFIG_IEEE80211BE */ |
796 | |
|
797 | 0 | buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP, |
798 | 0 | NULL, |
799 | 0 | params->known_bss, |
800 | 0 | params->known_bss_len, NULL); |
801 | 0 | buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true); |
802 | 0 | buflen += hostapd_mbo_ie_len(hapd); |
803 | 0 | buflen += hostapd_eid_owe_trans_len(hapd); |
804 | 0 | buflen += hostapd_eid_dpp_cc_len(hapd); |
805 | 0 | buflen += hostapd_get_rsne_override_len(hapd); |
806 | 0 | buflen += hostapd_get_rsne_override_2_len(hapd); |
807 | 0 | buflen += hostapd_get_rsnxe_override_len(hapd); |
808 | |
|
809 | 0 | return buflen; |
810 | 0 | } |
811 | | |
812 | | |
813 | | static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, |
814 | | struct probe_resp_params *params, |
815 | | u8 *pos, size_t len) |
816 | 0 | { |
817 | 0 | struct hostapd_data *hapd_probed = hapd; |
818 | 0 | u8 *csa_pos; |
819 | 0 | u8 *epos; |
820 | |
|
821 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
822 | 0 | epos = pos + len; |
823 | |
|
824 | 0 | *pos++ = WLAN_EID_SSID; |
825 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
826 | 0 | os_memcpy(pos, hapd->conf->ssid.ssid, |
827 | 0 | hapd->conf->ssid.ssid_len); |
828 | 0 | pos += hapd->conf->ssid.ssid_len; |
829 | | |
830 | | /* Supported rates */ |
831 | 0 | pos = hostapd_eid_supp_rates(hapd, pos); |
832 | | |
833 | | /* DS Params */ |
834 | 0 | pos = hostapd_eid_ds_params(hapd, pos); |
835 | |
|
836 | 0 | pos = hostapd_eid_country(hapd, pos, epos - pos); |
837 | | |
838 | | /* Power Constraint element */ |
839 | 0 | pos = hostapd_eid_pwr_constraint(hapd, pos); |
840 | | |
841 | | /* CSA element */ |
842 | 0 | csa_pos = hostapd_eid_csa(hapd, pos); |
843 | 0 | if (csa_pos != pos) |
844 | 0 | params->csa_pos = csa_pos - 1; |
845 | 0 | else |
846 | 0 | params->csa_pos = NULL; |
847 | 0 | pos = csa_pos; |
848 | | |
849 | | /* ERP Information element */ |
850 | 0 | pos = hostapd_eid_erp_info(hapd, pos); |
851 | | |
852 | | /* Extended supported rates */ |
853 | 0 | pos = hostapd_eid_ext_supp_rates(hapd, pos); |
854 | |
|
855 | 0 | pos = hostapd_get_rsne(hapd, pos, epos - pos); |
856 | 0 | pos = hostapd_eid_bss_load(hapd, pos, epos - pos); |
857 | 0 | pos = hostapd_eid_mbssid(hapd_probed, pos, epos, |
858 | 0 | WLAN_FC_STYPE_PROBE_RESP, 0, |
859 | 0 | NULL, params->known_bss, params->known_bss_len, |
860 | 0 | NULL, NULL, NULL, 0); |
861 | 0 | pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); |
862 | 0 | pos = hostapd_get_mde(hapd, pos, epos - pos); |
863 | | |
864 | | /* eCSA element */ |
865 | 0 | csa_pos = hostapd_eid_ecsa(hapd, pos); |
866 | 0 | if (csa_pos != pos) |
867 | 0 | params->ecsa_pos = csa_pos - 1; |
868 | 0 | else |
869 | 0 | params->ecsa_pos = NULL; |
870 | 0 | pos = csa_pos; |
871 | |
|
872 | 0 | pos = hostapd_eid_supported_op_classes(hapd, pos); |
873 | 0 | pos = hostapd_eid_ht_capabilities(hapd, pos); |
874 | 0 | pos = hostapd_eid_ht_operation(hapd, pos); |
875 | | |
876 | | /* Probe Response frames always include all non-TX profiles except |
877 | | * when a list of known BSSes is included in the Probe Request frame. */ |
878 | 0 | pos = hostapd_eid_ext_capab(hapd, pos, |
879 | 0 | hapd->iconf->mbssid >= MBSSID_ENABLED && |
880 | 0 | !params->known_bss_len); |
881 | |
|
882 | 0 | pos = hostapd_eid_time_adv(hapd, pos); |
883 | 0 | pos = hostapd_eid_time_zone(hapd, pos); |
884 | |
|
885 | 0 | pos = hostapd_eid_interworking(hapd, pos); |
886 | 0 | pos = hostapd_eid_adv_proto(hapd, pos); |
887 | 0 | pos = hostapd_eid_roaming_consortium(hapd, pos); |
888 | |
|
889 | | #ifdef CONFIG_FST |
890 | | if (hapd->iface->fst_ies) { |
891 | | os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), |
892 | | wpabuf_len(hapd->iface->fst_ies)); |
893 | | pos += wpabuf_len(hapd->iface->fst_ies); |
894 | | } |
895 | | #endif /* CONFIG_FST */ |
896 | |
|
897 | | #ifdef CONFIG_IEEE80211AC |
898 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && |
899 | | !is_6ghz_op_class(hapd->iconf->op_class)) { |
900 | | pos = hostapd_eid_vht_capabilities(hapd, pos, 0); |
901 | | pos = hostapd_eid_vht_operation(hapd, pos); |
902 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
903 | | } |
904 | | #endif /* CONFIG_IEEE80211AC */ |
905 | |
|
906 | | #ifdef CONFIG_IEEE80211AX |
907 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && |
908 | | is_6ghz_op_class(hapd->iconf->op_class)) |
909 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
910 | | #endif /* CONFIG_IEEE80211AX */ |
911 | |
|
912 | 0 | pos = hostapd_eid_chsw_wrapper(hapd, pos); |
913 | |
|
914 | 0 | pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true); |
915 | 0 | pos = hostapd_eid_fils_indic(hapd, pos, 0); |
916 | | |
917 | | /* Max Channel Switch Time element */ |
918 | 0 | pos = hostapd_eid_max_cs_time(hapd, pos); |
919 | |
|
920 | 0 | pos = hostapd_get_rsnxe(hapd, pos, epos - pos); |
921 | |
|
922 | | #ifdef CONFIG_IEEE80211AX |
923 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { |
924 | | u8 *cca_pos; |
925 | | |
926 | | pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP); |
927 | | pos = hostapd_eid_he_operation(hapd, pos); |
928 | | |
929 | | /* BSS Color Change Announcement element */ |
930 | | cca_pos = hostapd_eid_cca(hapd, pos); |
931 | | if (cca_pos != pos) |
932 | | params->cca_pos = cca_pos - 2; |
933 | | else |
934 | | params->cca_pos = NULL; |
935 | | pos = cca_pos; |
936 | | |
937 | | pos = hostapd_eid_spatial_reuse(hapd, pos); |
938 | | pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); |
939 | | pos = hostapd_eid_he_6ghz_band_cap(hapd, pos); |
940 | | } |
941 | | #endif /* CONFIG_IEEE80211AX */ |
942 | |
|
943 | | #ifdef CONFIG_IEEE80211BE |
944 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
945 | | struct hostapd_data *ml_elem_ap = |
946 | | params->mld_ap ? params->mld_ap : hapd; |
947 | | |
948 | | if (ml_elem_ap->conf->mld_ap) |
949 | | pos = hostapd_eid_eht_ml_beacon( |
950 | | ml_elem_ap, params->mld_info, |
951 | | pos, !!params->mld_ap); |
952 | | |
953 | | pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP); |
954 | | pos = hostapd_eid_eht_operation(hapd, pos); |
955 | | } |
956 | | |
957 | | if (hapd_probed != hapd && hapd_probed->conf->mld_ap) |
958 | | pos = hostapd_eid_eht_basic_ml_common(hapd_probed, pos, NULL, |
959 | | true, false); |
960 | | #endif /* CONFIG_IEEE80211BE */ |
961 | |
|
962 | | #ifdef CONFIG_IEEE80211AC |
963 | | if (hapd->conf->vendor_vht) |
964 | | pos = hostapd_eid_vendor_vht(hapd, pos); |
965 | | #endif /* CONFIG_IEEE80211AC */ |
966 | | |
967 | | /* WPA */ |
968 | 0 | pos = hostapd_get_wpa_ie(hapd, pos, epos - pos); |
969 | | |
970 | | /* Wi-Fi Alliance WMM */ |
971 | 0 | pos = hostapd_eid_wmm(hapd, pos); |
972 | |
|
973 | 0 | #ifdef CONFIG_WPS |
974 | 0 | if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { |
975 | 0 | os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), |
976 | 0 | wpabuf_len(hapd->wps_probe_resp_ie)); |
977 | 0 | pos += wpabuf_len(hapd->wps_probe_resp_ie); |
978 | 0 | } |
979 | 0 | #endif /* CONFIG_WPS */ |
980 | |
|
981 | | #ifdef CONFIG_P2P |
982 | | if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p && |
983 | | hapd->p2p_probe_resp_ie) { |
984 | | os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), |
985 | | wpabuf_len(hapd->p2p_probe_resp_ie)); |
986 | | pos += wpabuf_len(hapd->p2p_probe_resp_ie); |
987 | | } |
988 | | #endif /* CONFIG_P2P */ |
989 | | #ifdef CONFIG_P2P_MANAGER |
990 | | if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == |
991 | | P2P_MANAGE) |
992 | | pos = hostapd_eid_p2p_manage(hapd, pos); |
993 | | #endif /* CONFIG_P2P_MANAGER */ |
994 | |
|
995 | 0 | #ifdef CONFIG_HS20 |
996 | 0 | pos = hostapd_eid_hs20_indication(hapd, pos); |
997 | 0 | #endif /* CONFIG_HS20 */ |
998 | |
|
999 | 0 | pos = hostapd_eid_mbo(hapd, pos, epos - pos); |
1000 | 0 | pos = hostapd_eid_owe_trans(hapd, pos, epos - pos); |
1001 | 0 | pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos); |
1002 | |
|
1003 | 0 | pos = hostapd_get_rsne_override(hapd, pos, epos - pos); |
1004 | 0 | pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos); |
1005 | 0 | pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos); |
1006 | |
|
1007 | 0 | if (hapd->conf->vendor_elements) { |
1008 | 0 | os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), |
1009 | 0 | wpabuf_len(hapd->conf->vendor_elements)); |
1010 | 0 | pos += wpabuf_len(hapd->conf->vendor_elements); |
1011 | 0 | } |
1012 | |
|
1013 | | #ifdef CONFIG_TESTING_OPTIONS |
1014 | | if (hapd->conf->presp_elements) { |
1015 | | os_memcpy(pos, wpabuf_head(hapd->conf->presp_elements), |
1016 | | wpabuf_len(hapd->conf->presp_elements)); |
1017 | | pos += wpabuf_len(hapd->conf->presp_elements); |
1018 | | } |
1019 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1020 | |
|
1021 | 0 | return pos; |
1022 | 0 | } |
1023 | | |
1024 | | |
1025 | | static void hostapd_gen_probe_resp(struct hostapd_data *hapd, |
1026 | | struct probe_resp_params *params) |
1027 | 0 | { |
1028 | 0 | struct hostapd_data *hapd_probed = hapd; |
1029 | 0 | u8 *pos; |
1030 | 0 | size_t buflen; |
1031 | |
|
1032 | 0 | hapd = hostapd_mbssid_get_tx_bss(hapd); |
1033 | |
|
1034 | 0 | #define MAX_PROBERESP_LEN 768 |
1035 | 0 | buflen = MAX_PROBERESP_LEN; |
1036 | 0 | buflen += hostapd_probe_resp_elems_len(hapd_probed, params); |
1037 | 0 | params->resp = os_zalloc(buflen); |
1038 | 0 | if (!params->resp) { |
1039 | 0 | params->resp_len = 0; |
1040 | 0 | return; |
1041 | 0 | } |
1042 | | |
1043 | 0 | params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
1044 | 0 | WLAN_FC_STYPE_PROBE_RESP); |
1045 | | /* Unicast the response to all requests on bands other than 6 GHz. For |
1046 | | * the 6 GHz, unicast is used only if the actual SSID is not included in |
1047 | | * the Beacon frames. Otherwise, broadcast response is used per IEEE |
1048 | | * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for |
1049 | | * the Probe Response frame template for the unsolicited (i.e., not as |
1050 | | * a response to a specific request) case. */ |
1051 | 0 | if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) || |
1052 | 0 | hapd->conf->ignore_broadcast_ssid)) |
1053 | 0 | os_memcpy(params->resp->da, params->req->sa, ETH_ALEN); |
1054 | 0 | else |
1055 | 0 | os_memset(params->resp->da, 0xff, ETH_ALEN); |
1056 | 0 | os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN); |
1057 | |
|
1058 | 0 | os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN); |
1059 | 0 | params->resp->u.probe_resp.beacon_int = |
1060 | 0 | host_to_le16(hapd->iconf->beacon_int); |
1061 | | |
1062 | | /* hardware or low-level driver will setup seq_ctrl and timestamp */ |
1063 | 0 | params->resp->u.probe_resp.capab_info = |
1064 | 0 | host_to_le16(hostapd_own_capab_info(hapd)); |
1065 | |
|
1066 | 0 | pos = hostapd_probe_resp_fill_elems(hapd_probed, params, |
1067 | 0 | params->resp->u.probe_resp.variable, |
1068 | 0 | buflen); |
1069 | |
|
1070 | 0 | params->resp_len = pos - (u8 *) params->resp; |
1071 | 0 | } |
1072 | | |
1073 | | |
1074 | | #ifdef CONFIG_IEEE80211BE |
1075 | | static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, |
1076 | | struct probe_resp_params *params, |
1077 | | const struct ieee80211_mgmt *mgmt, |
1078 | | int mld_id, u16 links) |
1079 | | { |
1080 | | struct hostapd_data *link; |
1081 | | |
1082 | | params->mld_ap = NULL; |
1083 | | params->mld_info = os_zalloc(sizeof(*params->mld_info)); |
1084 | | if (!params->mld_info) |
1085 | | return; |
1086 | | |
1087 | | wpa_printf(MSG_DEBUG, |
1088 | | "MLD: Got ML probe request with AP MLD ID %d for links %04x", |
1089 | | mld_id, links); |
1090 | | |
1091 | | for_each_mld_link(link, hapd) { |
1092 | | struct mld_link_info *link_info; |
1093 | | u8 mld_link_id = link->mld_link_id; |
1094 | | |
1095 | | /* |
1096 | | * Set mld_ap iff the ML probe request explicitly |
1097 | | * requested a specific MLD ID. In that case, the targeted |
1098 | | * AP may have been a nontransmitted BSSID on the same |
1099 | | * interface. |
1100 | | */ |
1101 | | if (mld_id != -1 && link->iface == hapd->iface) |
1102 | | params->mld_ap = link; |
1103 | | |
1104 | | /* Never duplicate main Probe Response frame body */ |
1105 | | if (link == hapd) |
1106 | | continue; |
1107 | | |
1108 | | /* Only include requested links */ |
1109 | | if (!(BIT(mld_link_id) & links)) |
1110 | | continue; |
1111 | | |
1112 | | link_info = ¶ms->mld_info->links[mld_link_id]; |
1113 | | os_memcpy(link_info, &hapd->partner_links[mld_link_id], |
1114 | | sizeof(hapd->partner_links[mld_link_id])); |
1115 | | |
1116 | | wpa_printf(MSG_DEBUG, |
1117 | | "MLD: ML probe response includes link STA info for %d: %u bytes", |
1118 | | mld_link_id, link_info->resp_sta_profile_len); |
1119 | | } |
1120 | | |
1121 | | if (mld_id != -1 && !params->mld_ap) { |
1122 | | wpa_printf(MSG_DEBUG, |
1123 | | "MLD: No nontransmitted BSSID for MLD ID %d", |
1124 | | mld_id); |
1125 | | goto fail; |
1126 | | } |
1127 | | |
1128 | | return; |
1129 | | |
1130 | | fail: |
1131 | | hostapd_free_probe_resp_params(params); |
1132 | | params->mld_ap = NULL; |
1133 | | params->mld_info = NULL; |
1134 | | } |
1135 | | #endif /* CONFIG_IEEE80211BE */ |
1136 | | |
1137 | | |
1138 | | enum ssid_match_result { |
1139 | | NO_SSID_MATCH, |
1140 | | EXACT_SSID_MATCH, |
1141 | | WILDCARD_SSID_MATCH, |
1142 | | CO_LOCATED_SSID_MATCH, |
1143 | | }; |
1144 | | |
1145 | | static enum ssid_match_result ssid_match(struct hostapd_data *hapd, |
1146 | | const u8 *ssid, size_t ssid_len, |
1147 | | const u8 *ssid_list, |
1148 | | size_t ssid_list_len, |
1149 | | const u8 *short_ssid_list, |
1150 | | size_t short_ssid_list_len) |
1151 | 0 | { |
1152 | 0 | const u8 *pos, *end; |
1153 | 0 | struct hostapd_iface *iface = hapd->iface; |
1154 | 0 | int wildcard = 0; |
1155 | 0 | size_t i, j; |
1156 | |
|
1157 | 0 | if (ssid_len == 0) |
1158 | 0 | wildcard = 1; |
1159 | 0 | if (ssid_len == hapd->conf->ssid.ssid_len && |
1160 | 0 | os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) |
1161 | 0 | return EXACT_SSID_MATCH; |
1162 | | |
1163 | 0 | if (ssid_list) { |
1164 | 0 | pos = ssid_list; |
1165 | 0 | end = ssid_list + ssid_list_len; |
1166 | 0 | while (end - pos >= 2) { |
1167 | 0 | if (2 + pos[1] > end - pos) |
1168 | 0 | break; |
1169 | 0 | if (pos[1] == 0) |
1170 | 0 | wildcard = 1; |
1171 | 0 | if (pos[1] == hapd->conf->ssid.ssid_len && |
1172 | 0 | os_memcmp(pos + 2, hapd->conf->ssid.ssid, |
1173 | 0 | pos[1]) == 0) |
1174 | 0 | return EXACT_SSID_MATCH; |
1175 | 0 | pos += 2 + pos[1]; |
1176 | 0 | } |
1177 | 0 | } |
1178 | | |
1179 | 0 | if (short_ssid_list) { |
1180 | 0 | pos = short_ssid_list; |
1181 | 0 | end = short_ssid_list + short_ssid_list_len; |
1182 | 0 | while (end - pos >= 4) { |
1183 | 0 | if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos)) |
1184 | 0 | return EXACT_SSID_MATCH; |
1185 | 0 | pos += 4; |
1186 | 0 | } |
1187 | 0 | } |
1188 | | |
1189 | 0 | if (wildcard) |
1190 | 0 | return WILDCARD_SSID_MATCH; |
1191 | | |
1192 | 0 | if (!iface->interfaces || iface->interfaces->count <= 1 || |
1193 | 0 | is_6ghz_op_class(hapd->iconf->op_class)) |
1194 | 0 | return NO_SSID_MATCH; |
1195 | | |
1196 | 0 | for (i = 0; i < iface->interfaces->count; i++) { |
1197 | 0 | struct hostapd_iface *colocated; |
1198 | |
|
1199 | 0 | colocated = iface->interfaces->iface[i]; |
1200 | |
|
1201 | 0 | if (colocated == iface || |
1202 | 0 | !is_6ghz_op_class(colocated->conf->op_class)) |
1203 | 0 | continue; |
1204 | | |
1205 | 0 | for (j = 0; j < colocated->num_bss; j++) { |
1206 | 0 | struct hostapd_bss_config *conf; |
1207 | |
|
1208 | 0 | conf = colocated->bss[j]->conf; |
1209 | 0 | if (ssid_len == conf->ssid.ssid_len && |
1210 | 0 | os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0) |
1211 | 0 | return CO_LOCATED_SSID_MATCH; |
1212 | 0 | } |
1213 | 0 | } |
1214 | | |
1215 | 0 | return NO_SSID_MATCH; |
1216 | 0 | } |
1217 | | |
1218 | | |
1219 | | void sta_track_expire(struct hostapd_iface *iface, int force) |
1220 | 0 | { |
1221 | 0 | struct os_reltime now; |
1222 | 0 | struct hostapd_sta_info *info; |
1223 | |
|
1224 | 0 | if (!iface->num_sta_seen) |
1225 | 0 | return; |
1226 | | |
1227 | 0 | os_get_reltime(&now); |
1228 | 0 | while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, |
1229 | 0 | list))) { |
1230 | 0 | if (!force && |
1231 | 0 | !os_reltime_expired(&now, &info->last_seen, |
1232 | 0 | iface->conf->track_sta_max_age)) |
1233 | 0 | break; |
1234 | 0 | force = 0; |
1235 | |
|
1236 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " |
1237 | 0 | MACSTR, iface->bss[0]->conf->iface, |
1238 | 0 | MAC2STR(info->addr)); |
1239 | 0 | dl_list_del(&info->list); |
1240 | 0 | iface->num_sta_seen--; |
1241 | 0 | sta_track_del(info); |
1242 | 0 | } |
1243 | 0 | } |
1244 | | |
1245 | | |
1246 | | static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, |
1247 | | const u8 *addr) |
1248 | 0 | { |
1249 | 0 | struct hostapd_sta_info *info; |
1250 | |
|
1251 | 0 | dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) |
1252 | 0 | if (ether_addr_equal(addr, info->addr)) |
1253 | 0 | return info; |
1254 | | |
1255 | 0 | return NULL; |
1256 | 0 | } |
1257 | | |
1258 | | |
1259 | | void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) |
1260 | 0 | { |
1261 | 0 | struct hostapd_sta_info *info; |
1262 | |
|
1263 | 0 | info = sta_track_get(iface, addr); |
1264 | 0 | if (info) { |
1265 | | /* Move the most recent entry to the end of the list */ |
1266 | 0 | dl_list_del(&info->list); |
1267 | 0 | dl_list_add_tail(&iface->sta_seen, &info->list); |
1268 | 0 | os_get_reltime(&info->last_seen); |
1269 | 0 | info->ssi_signal = ssi_signal; |
1270 | 0 | return; |
1271 | 0 | } |
1272 | | |
1273 | | /* Add a new entry */ |
1274 | 0 | info = os_zalloc(sizeof(*info)); |
1275 | 0 | if (info == NULL) |
1276 | 0 | return; |
1277 | 0 | os_memcpy(info->addr, addr, ETH_ALEN); |
1278 | 0 | os_get_reltime(&info->last_seen); |
1279 | 0 | info->ssi_signal = ssi_signal; |
1280 | |
|
1281 | 0 | if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { |
1282 | | /* Expire oldest entry to make room for a new one */ |
1283 | 0 | sta_track_expire(iface, 1); |
1284 | 0 | } |
1285 | |
|
1286 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " |
1287 | 0 | MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); |
1288 | 0 | dl_list_add_tail(&iface->sta_seen, &info->list); |
1289 | 0 | iface->num_sta_seen++; |
1290 | 0 | } |
1291 | | |
1292 | | |
1293 | | struct hostapd_data * |
1294 | | sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, |
1295 | | const char *ifname) |
1296 | 0 | { |
1297 | 0 | struct hapd_interfaces *interfaces = iface->interfaces; |
1298 | 0 | size_t i, j; |
1299 | |
|
1300 | 0 | for (i = 0; i < interfaces->count; i++) { |
1301 | 0 | struct hostapd_data *hapd = NULL; |
1302 | |
|
1303 | 0 | iface = interfaces->iface[i]; |
1304 | 0 | for (j = 0; j < iface->num_bss; j++) { |
1305 | 0 | hapd = iface->bss[j]; |
1306 | 0 | if (os_strcmp(ifname, hapd->conf->iface) == 0) |
1307 | 0 | break; |
1308 | 0 | hapd = NULL; |
1309 | 0 | } |
1310 | |
|
1311 | 0 | if (hapd && sta_track_get(iface, addr)) |
1312 | 0 | return hapd; |
1313 | 0 | } |
1314 | | |
1315 | 0 | return NULL; |
1316 | 0 | } |
1317 | | |
1318 | | |
1319 | | #ifdef CONFIG_TAXONOMY |
1320 | | void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, |
1321 | | struct wpabuf **probe_ie_taxonomy) |
1322 | | { |
1323 | | struct hostapd_sta_info *info; |
1324 | | |
1325 | | info = sta_track_get(iface, addr); |
1326 | | if (!info) |
1327 | | return; |
1328 | | |
1329 | | wpabuf_free(*probe_ie_taxonomy); |
1330 | | *probe_ie_taxonomy = info->probe_ie_taxonomy; |
1331 | | info->probe_ie_taxonomy = NULL; |
1332 | | } |
1333 | | #endif /* CONFIG_TAXONOMY */ |
1334 | | |
1335 | | |
1336 | | #ifdef CONFIG_IEEE80211BE |
1337 | | static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len, |
1338 | | int *mld_id, u16 *links) |
1339 | | { |
1340 | | u16 ml_control; |
1341 | | const struct element *sub; |
1342 | | const u8 *pos; |
1343 | | size_t len; |
1344 | | |
1345 | | *mld_id = -1; |
1346 | | *links = 0xffff; |
1347 | | |
1348 | | if (ml_len < sizeof(struct ieee80211_eht_ml)) |
1349 | | return false; |
1350 | | |
1351 | | ml_control = le_to_host16(ml->ml_control); |
1352 | | if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) != |
1353 | | MULTI_LINK_CONTROL_TYPE_PROBE_REQ) { |
1354 | | wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req"); |
1355 | | return false; |
1356 | | } |
1357 | | |
1358 | | if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) { |
1359 | | wpa_printf(MSG_DEBUG, "MLD: ML probe req too short"); |
1360 | | return false; |
1361 | | } |
1362 | | |
1363 | | pos = ml->variable; |
1364 | | len = pos[0]; |
1365 | | if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) { |
1366 | | wpa_printf(MSG_DEBUG, |
1367 | | "MLD: ML probe request with invalid length"); |
1368 | | return false; |
1369 | | } |
1370 | | |
1371 | | if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) { |
1372 | | if (len < 2) { |
1373 | | wpa_printf(MSG_DEBUG, |
1374 | | "MLD: ML probe req too short for MLD ID"); |
1375 | | return false; |
1376 | | } |
1377 | | |
1378 | | *mld_id = pos[1]; |
1379 | | } |
1380 | | pos += len; |
1381 | | |
1382 | | /* Parse subelements (if there are any) */ |
1383 | | len = ml_len - len - sizeof(struct ieee80211_eht_ml); |
1384 | | for_each_element_id(sub, 0, pos, len) { |
1385 | | const struct ieee80211_eht_per_sta_profile *sta; |
1386 | | u16 sta_control; |
1387 | | |
1388 | | if (*links == 0xffff) |
1389 | | *links = 0; |
1390 | | |
1391 | | if (sub->datalen < |
1392 | | sizeof(struct ieee80211_eht_per_sta_profile)) { |
1393 | | wpa_printf(MSG_DEBUG, |
1394 | | "MLD: ML probe req %d too short for sta profile", |
1395 | | sub->datalen); |
1396 | | return false; |
1397 | | } |
1398 | | |
1399 | | sta = (struct ieee80211_eht_per_sta_profile *) sub->data; |
1400 | | |
1401 | | /* |
1402 | | * Extract the link ID, do not return whether a complete or |
1403 | | * partial profile was requested. |
1404 | | */ |
1405 | | sta_control = le_to_host16(sta->sta_control); |
1406 | | *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK); |
1407 | | } |
1408 | | |
1409 | | if (!for_each_element_completed(sub, pos, len)) { |
1410 | | wpa_printf(MSG_DEBUG, |
1411 | | "MLD: ML probe req sub-elements parsing error"); |
1412 | | return false; |
1413 | | } |
1414 | | |
1415 | | return true; |
1416 | | } |
1417 | | #endif /* CONFIG_IEEE80211BE */ |
1418 | | |
1419 | | |
1420 | | void handle_probe_req(struct hostapd_data *hapd, |
1421 | | const struct ieee80211_mgmt *mgmt, size_t len, |
1422 | | int ssi_signal) |
1423 | 0 | { |
1424 | 0 | struct ieee802_11_elems elems; |
1425 | 0 | const u8 *ie; |
1426 | 0 | size_t ie_len; |
1427 | 0 | size_t i; |
1428 | 0 | int noack; |
1429 | 0 | enum ssid_match_result res; |
1430 | 0 | int ret; |
1431 | 0 | u16 csa_offs[2]; |
1432 | 0 | size_t csa_offs_len; |
1433 | 0 | struct radius_sta rad_info; |
1434 | 0 | struct probe_resp_params params; |
1435 | 0 | char *hex = NULL; |
1436 | | #ifdef CONFIG_IEEE80211BE |
1437 | | int mld_id; |
1438 | | u16 links; |
1439 | | #endif /* CONFIG_IEEE80211BE */ |
1440 | |
|
1441 | 0 | if (hapd->iconf->rssi_ignore_probe_request && ssi_signal && |
1442 | 0 | ssi_signal < hapd->iconf->rssi_ignore_probe_request) |
1443 | 0 | return; |
1444 | | |
1445 | 0 | if (len < IEEE80211_HDRLEN) |
1446 | 0 | return; |
1447 | 0 | ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; |
1448 | 0 | if (hapd->iconf->track_sta_max_num) |
1449 | 0 | sta_track_add(hapd->iface, mgmt->sa, ssi_signal); |
1450 | 0 | ie_len = len - IEEE80211_HDRLEN; |
1451 | |
|
1452 | 0 | ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, |
1453 | 0 | &rad_info, 1); |
1454 | 0 | if (ret == HOSTAPD_ACL_REJECT) { |
1455 | 0 | wpa_msg(hapd->msg_ctx, MSG_DEBUG, |
1456 | 0 | "Ignore Probe Request frame from " MACSTR |
1457 | 0 | " due to ACL reject ", MAC2STR(mgmt->sa)); |
1458 | 0 | return; |
1459 | 0 | } |
1460 | | |
1461 | 0 | for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) |
1462 | 0 | if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, |
1463 | 0 | mgmt->sa, mgmt->da, mgmt->bssid, |
1464 | 0 | ie, ie_len, ssi_signal) > 0) |
1465 | 0 | return; |
1466 | | |
1467 | 0 | if (!hapd->conf->send_probe_response) |
1468 | 0 | return; |
1469 | | |
1470 | 0 | if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { |
1471 | 0 | wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, |
1472 | 0 | MAC2STR(mgmt->sa)); |
1473 | 0 | return; |
1474 | 0 | } |
1475 | | |
1476 | 0 | if ((!elems.ssid || !elems.supp_rates)) { |
1477 | 0 | wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " |
1478 | 0 | "without SSID or supported rates element", |
1479 | 0 | MAC2STR(mgmt->sa)); |
1480 | 0 | return; |
1481 | 0 | } |
1482 | | |
1483 | | /* |
1484 | | * No need to reply if the Probe Request frame was sent on an adjacent |
1485 | | * channel. IEEE Std 802.11-2012 describes this as a requirement for an |
1486 | | * AP with dot11RadioMeasurementActivated set to true, but strictly |
1487 | | * speaking does not allow such ignoring of Probe Request frames if |
1488 | | * dot11RadioMeasurementActivated is false. Anyway, this can help reduce |
1489 | | * number of unnecessary Probe Response frames for cases where the STA |
1490 | | * is less likely to see them (Probe Request frame sent on a |
1491 | | * neighboring, but partially overlapping, channel). |
1492 | | */ |
1493 | 0 | if (elems.ds_params && |
1494 | 0 | hapd->iface->current_mode && |
1495 | 0 | (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || |
1496 | 0 | hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && |
1497 | 0 | hapd->iconf->channel != elems.ds_params[0]) { |
1498 | 0 | wpa_printf(MSG_DEBUG, |
1499 | 0 | "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u", |
1500 | 0 | hapd->iconf->channel, elems.ds_params[0]); |
1501 | 0 | return; |
1502 | 0 | } |
1503 | | |
1504 | | #ifdef CONFIG_P2P |
1505 | | if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { |
1506 | | struct wpabuf *wps; |
1507 | | wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); |
1508 | | if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { |
1509 | | wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " |
1510 | | "due to mismatch with Requested Device " |
1511 | | "Type"); |
1512 | | wpabuf_free(wps); |
1513 | | return; |
1514 | | } |
1515 | | wpabuf_free(wps); |
1516 | | } |
1517 | | |
1518 | | if (hapd->p2p && hapd->p2p_group && elems.p2p) { |
1519 | | struct wpabuf *p2p; |
1520 | | p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); |
1521 | | if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { |
1522 | | wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " |
1523 | | "due to mismatch with Device ID"); |
1524 | | wpabuf_free(p2p); |
1525 | | return; |
1526 | | } |
1527 | | wpabuf_free(p2p); |
1528 | | } |
1529 | | #endif /* CONFIG_P2P */ |
1530 | | |
1531 | 0 | if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && |
1532 | 0 | elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) { |
1533 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " |
1534 | 0 | "broadcast SSID ignored", MAC2STR(mgmt->sa)); |
1535 | 0 | return; |
1536 | 0 | } |
1537 | | |
1538 | | #ifdef CONFIG_P2P |
1539 | | if ((hapd->conf->p2p & P2P_GROUP_OWNER) && |
1540 | | elems.ssid_len == P2P_WILDCARD_SSID_LEN && |
1541 | | os_memcmp(elems.ssid, P2P_WILDCARD_SSID, |
1542 | | P2P_WILDCARD_SSID_LEN) == 0) { |
1543 | | /* Process P2P Wildcard SSID like Wildcard SSID */ |
1544 | | elems.ssid_len = 0; |
1545 | | } |
1546 | | #endif /* CONFIG_P2P */ |
1547 | | |
1548 | | #ifdef CONFIG_TAXONOMY |
1549 | | { |
1550 | | struct sta_info *sta; |
1551 | | struct hostapd_sta_info *info; |
1552 | | |
1553 | | if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { |
1554 | | taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); |
1555 | | } else if ((info = sta_track_get(hapd->iface, |
1556 | | mgmt->sa)) != NULL) { |
1557 | | taxonomy_hostapd_sta_info_probe_req(hapd, info, |
1558 | | ie, ie_len); |
1559 | | } |
1560 | | } |
1561 | | #endif /* CONFIG_TAXONOMY */ |
1562 | | |
1563 | 0 | res = ssid_match(hapd, elems.ssid, elems.ssid_len, |
1564 | 0 | elems.ssid_list, elems.ssid_list_len, |
1565 | 0 | elems.short_ssid_list, elems.short_ssid_list_len); |
1566 | 0 | if (res == NO_SSID_MATCH) { |
1567 | 0 | if (!(mgmt->da[0] & 0x01)) { |
1568 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1569 | 0 | " for foreign SSID '%s' (DA " MACSTR ")%s", |
1570 | 0 | MAC2STR(mgmt->sa), |
1571 | 0 | wpa_ssid_txt(elems.ssid, elems.ssid_len), |
1572 | 0 | MAC2STR(mgmt->da), |
1573 | 0 | elems.ssid_list ? " (SSID list)" : ""); |
1574 | 0 | } |
1575 | 0 | return; |
1576 | 0 | } |
1577 | | |
1578 | 0 | if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) { |
1579 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " |
1580 | 0 | "broadcast SSID ignored", MAC2STR(mgmt->sa)); |
1581 | 0 | return; |
1582 | 0 | } |
1583 | | |
1584 | 0 | #ifdef CONFIG_INTERWORKING |
1585 | 0 | if (hapd->conf->interworking && |
1586 | 0 | elems.interworking && elems.interworking_len >= 1) { |
1587 | 0 | u8 ant = elems.interworking[0] & 0x0f; |
1588 | 0 | if (ant != INTERWORKING_ANT_WILDCARD && |
1589 | 0 | ant != hapd->conf->access_network_type) { |
1590 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1591 | 0 | " for mismatching ANT %u ignored", |
1592 | 0 | MAC2STR(mgmt->sa), ant); |
1593 | 0 | return; |
1594 | 0 | } |
1595 | 0 | } |
1596 | | |
1597 | 0 | if (hapd->conf->interworking && elems.interworking && |
1598 | 0 | (elems.interworking_len == 7 || elems.interworking_len == 9)) { |
1599 | 0 | const u8 *hessid; |
1600 | 0 | if (elems.interworking_len == 7) |
1601 | 0 | hessid = elems.interworking + 1; |
1602 | 0 | else |
1603 | 0 | hessid = elems.interworking + 1 + 2; |
1604 | 0 | if (!is_broadcast_ether_addr(hessid) && |
1605 | 0 | !ether_addr_equal(hessid, hapd->conf->hessid)) { |
1606 | 0 | wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR |
1607 | 0 | " for mismatching HESSID " MACSTR |
1608 | 0 | " ignored", |
1609 | 0 | MAC2STR(mgmt->sa), MAC2STR(hessid)); |
1610 | 0 | return; |
1611 | 0 | } |
1612 | 0 | } |
1613 | 0 | #endif /* CONFIG_INTERWORKING */ |
1614 | | |
1615 | | #ifdef CONFIG_P2P |
1616 | | if ((hapd->conf->p2p & P2P_GROUP_OWNER) && |
1617 | | supp_rates_11b_only(&elems)) { |
1618 | | /* Indicates support for 11b rates only */ |
1619 | | wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " |
1620 | | MACSTR " with only 802.11b rates", |
1621 | | MAC2STR(mgmt->sa)); |
1622 | | return; |
1623 | | } |
1624 | | #endif /* CONFIG_P2P */ |
1625 | | |
1626 | | /* TODO: verify that supp_rates contains at least one matching rate |
1627 | | * with AP configuration */ |
1628 | | |
1629 | 0 | if (hapd->conf->no_probe_resp_if_seen_on && |
1630 | 0 | is_multicast_ether_addr(mgmt->da) && |
1631 | 0 | is_multicast_ether_addr(mgmt->bssid) && |
1632 | 0 | sta_track_seen_on(hapd->iface, mgmt->sa, |
1633 | 0 | hapd->conf->no_probe_resp_if_seen_on)) { |
1634 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR |
1635 | 0 | " since STA has been seen on %s", |
1636 | 0 | hapd->conf->iface, MAC2STR(mgmt->sa), |
1637 | 0 | hapd->conf->no_probe_resp_if_seen_on); |
1638 | 0 | return; |
1639 | 0 | } |
1640 | | |
1641 | 0 | if (hapd->conf->no_probe_resp_if_max_sta && |
1642 | 0 | is_multicast_ether_addr(mgmt->da) && |
1643 | 0 | is_multicast_ether_addr(mgmt->bssid) && |
1644 | 0 | hapd->num_sta >= hapd->conf->max_num_sta && |
1645 | 0 | !ap_get_sta(hapd, mgmt->sa)) { |
1646 | 0 | wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR |
1647 | 0 | " since no room for additional STA", |
1648 | 0 | hapd->conf->iface, MAC2STR(mgmt->sa)); |
1649 | 0 | return; |
1650 | 0 | } |
1651 | | |
1652 | | #ifdef CONFIG_TESTING_OPTIONS |
1653 | | if (hapd->iconf->ignore_probe_probability > 0.0 && |
1654 | | drand48() < hapd->iconf->ignore_probe_probability) { |
1655 | | wpa_printf(MSG_INFO, |
1656 | | "TESTING: ignoring probe request from " MACSTR, |
1657 | | MAC2STR(mgmt->sa)); |
1658 | | return; |
1659 | | } |
1660 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1661 | | |
1662 | | /* Do not send Probe Response frame from a non-transmitting multiple |
1663 | | * BSSID profile unless the Probe Request frame is directed at that |
1664 | | * particular BSS. */ |
1665 | 0 | if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH) |
1666 | 0 | return; |
1667 | | |
1668 | 0 | if (hapd->conf->notify_mgmt_frames) { |
1669 | 0 | size_t hex_len; |
1670 | |
|
1671 | 0 | hex_len = len * 2 + 1; |
1672 | 0 | hex = os_malloc(hex_len); |
1673 | 0 | if (hex) |
1674 | 0 | wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len); |
1675 | 0 | } |
1676 | |
|
1677 | 0 | wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR |
1678 | 0 | " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal, |
1679 | 0 | hex ? " buf=" : "", hex ? hex : ""); |
1680 | |
|
1681 | 0 | os_free(hex); |
1682 | |
|
1683 | 0 | os_memset(¶ms, 0, sizeof(params)); |
1684 | |
|
1685 | | #ifdef CONFIG_IEEE80211BE |
1686 | | if (hapd->conf->mld_ap && elems.probe_req_mle && |
1687 | | parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle, |
1688 | | elems.probe_req_mle_len, &mld_id, &links)) { |
1689 | | hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt, |
1690 | | mld_id, links); |
1691 | | } |
1692 | | #endif /* CONFIG_IEEE80211BE */ |
1693 | |
|
1694 | 0 | params.req = mgmt; |
1695 | 0 | params.is_p2p = !!elems.p2p; |
1696 | 0 | params.known_bss = elems.mbssid_known_bss; |
1697 | 0 | params.known_bss_len = elems.mbssid_known_bss_len; |
1698 | |
|
1699 | 0 | hostapd_gen_probe_resp(hapd, ¶ms); |
1700 | |
|
1701 | 0 | hostapd_free_probe_resp_params(¶ms); |
1702 | |
|
1703 | 0 | if (!params.resp) |
1704 | 0 | return; |
1705 | | |
1706 | | /* |
1707 | | * If this is a broadcast probe request, apply no ack policy to avoid |
1708 | | * excessive retries. |
1709 | | */ |
1710 | 0 | noack = !!(res == WILDCARD_SSID_MATCH && |
1711 | 0 | is_broadcast_ether_addr(mgmt->da)); |
1712 | |
|
1713 | 0 | csa_offs_len = 0; |
1714 | 0 | if (hapd->csa_in_progress) { |
1715 | 0 | if (params.csa_pos) |
1716 | 0 | csa_offs[csa_offs_len++] = |
1717 | 0 | params.csa_pos - (u8 *) params.resp; |
1718 | |
|
1719 | 0 | if (params.ecsa_pos) |
1720 | 0 | csa_offs[csa_offs_len++] = |
1721 | 0 | params.ecsa_pos - (u8 *) params.resp; |
1722 | 0 | } |
1723 | |
|
1724 | 0 | ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), |
1725 | 0 | params.resp, params.resp_len, noack, |
1726 | 0 | csa_offs_len ? csa_offs : NULL, |
1727 | 0 | csa_offs_len, 0); |
1728 | |
|
1729 | 0 | if (ret < 0) |
1730 | 0 | wpa_printf(MSG_INFO, "handle_probe_req: send failed"); |
1731 | |
|
1732 | 0 | os_free(params.resp); |
1733 | |
|
1734 | 0 | wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " |
1735 | 0 | "SSID", MAC2STR(mgmt->sa), |
1736 | 0 | elems.ssid_len == 0 ? "broadcast" : "our"); |
1737 | 0 | } |
1738 | | |
1739 | | |
1740 | | static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, |
1741 | | size_t *resp_len) |
1742 | 0 | { |
1743 | 0 | struct probe_resp_params params; |
1744 | | |
1745 | | /* check probe response offloading caps and print warnings */ |
1746 | 0 | if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) |
1747 | 0 | return NULL; |
1748 | | |
1749 | 0 | #ifdef CONFIG_WPS |
1750 | 0 | if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && |
1751 | 0 | (!(hapd->iface->probe_resp_offloads & |
1752 | 0 | (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | |
1753 | 0 | WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) |
1754 | 0 | wpa_printf(MSG_WARNING, "Device is trying to offload WPS " |
1755 | 0 | "Probe Response while not supporting this"); |
1756 | 0 | #endif /* CONFIG_WPS */ |
1757 | |
|
1758 | | #ifdef CONFIG_P2P |
1759 | | if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && |
1760 | | !(hapd->iface->probe_resp_offloads & |
1761 | | WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) |
1762 | | wpa_printf(MSG_WARNING, "Device is trying to offload P2P " |
1763 | | "Probe Response while not supporting this"); |
1764 | | #endif /* CONFIG_P2P */ |
1765 | |
|
1766 | 0 | if (hapd->conf->interworking && |
1767 | 0 | !(hapd->iface->probe_resp_offloads & |
1768 | 0 | WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) |
1769 | 0 | wpa_printf(MSG_WARNING, "Device is trying to offload " |
1770 | 0 | "Interworking Probe Response while not supporting " |
1771 | 0 | "this"); |
1772 | | |
1773 | | /* Generate a Probe Response template for the non-P2P case */ |
1774 | 0 | os_memset(¶ms, 0, sizeof(params)); |
1775 | 0 | params.req = NULL; |
1776 | 0 | params.is_p2p = false; |
1777 | 0 | params.known_bss = NULL; |
1778 | 0 | params.known_bss_len = 0; |
1779 | 0 | params.mld_ap = NULL; |
1780 | 0 | params.mld_info = NULL; |
1781 | |
|
1782 | 0 | hostapd_gen_probe_resp(hapd, ¶ms); |
1783 | 0 | *resp_len = params.resp_len; |
1784 | 0 | if (!params.resp) |
1785 | 0 | return NULL; |
1786 | | |
1787 | | /* TODO: Avoid passing these through struct hostapd_data */ |
1788 | 0 | if (params.csa_pos) |
1789 | 0 | hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp; |
1790 | 0 | if (params.ecsa_pos) |
1791 | 0 | hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos - |
1792 | 0 | (u8 *) params.resp; |
1793 | | #ifdef CONFIG_IEEE80211AX |
1794 | | if (params.cca_pos) |
1795 | | hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp; |
1796 | | #endif /* CONFIG_IEEE80211AX */ |
1797 | |
|
1798 | 0 | return (u8 *) params.resp; |
1799 | 0 | } |
1800 | | |
1801 | | #endif /* NEED_AP_MLME */ |
1802 | | |
1803 | | |
1804 | | #ifdef CONFIG_IEEE80211AX |
1805 | | /* Unsolicited broadcast Probe Response transmission, 6 GHz only */ |
1806 | | u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd, |
1807 | | struct unsol_bcast_probe_resp *ubpr) |
1808 | | { |
1809 | | struct probe_resp_params probe_params; |
1810 | | |
1811 | | if (!is_6ghz_op_class(hapd->iconf->op_class)) |
1812 | | return NULL; |
1813 | | |
1814 | | ubpr->unsol_bcast_probe_resp_interval = |
1815 | | hapd->conf->unsol_bcast_probe_resp_interval; |
1816 | | |
1817 | | os_memset(&probe_params, 0, sizeof(probe_params)); |
1818 | | probe_params.req = NULL; |
1819 | | probe_params.is_p2p = false; |
1820 | | probe_params.known_bss = NULL; |
1821 | | probe_params.known_bss_len = 0; |
1822 | | probe_params.mld_ap = NULL; |
1823 | | probe_params.mld_info = NULL; |
1824 | | |
1825 | | hostapd_gen_probe_resp(hapd, &probe_params); |
1826 | | ubpr->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len; |
1827 | | return (u8 *) probe_params.resp; |
1828 | | } |
1829 | | #endif /* CONFIG_IEEE80211AX */ |
1830 | | |
1831 | | |
1832 | | void sta_track_del(struct hostapd_sta_info *info) |
1833 | 0 | { |
1834 | | #ifdef CONFIG_TAXONOMY |
1835 | | wpabuf_free(info->probe_ie_taxonomy); |
1836 | | info->probe_ie_taxonomy = NULL; |
1837 | | #endif /* CONFIG_TAXONOMY */ |
1838 | 0 | os_free(info); |
1839 | 0 | } |
1840 | | |
1841 | | |
1842 | | #ifdef CONFIG_FILS |
1843 | | |
1844 | | static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd) |
1845 | | { |
1846 | | #ifdef CONFIG_IEEE80211BE |
1847 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) |
1848 | | return FD_CAP_PHY_INDEX_EHT; |
1849 | | #endif /* CONFIG_IEEE80211BE */ |
1850 | | |
1851 | | #ifdef CONFIG_IEEE80211AX |
1852 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) |
1853 | | return FD_CAP_PHY_INDEX_HE; |
1854 | | #endif /* CONFIG_IEEE80211AX */ |
1855 | | |
1856 | | #ifdef CONFIG_IEEE80211AC |
1857 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) |
1858 | | return FD_CAP_PHY_INDEX_VHT; |
1859 | | #endif /* CONFIG_IEEE80211AC */ |
1860 | | |
1861 | | if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n) |
1862 | | return FD_CAP_PHY_INDEX_HT; |
1863 | | |
1864 | | return 0; |
1865 | | } |
1866 | | |
1867 | | |
1868 | | static u16 hostapd_gen_fils_discovery_nss(struct hostapd_hw_modes *mode, |
1869 | | u16 phy_index, u8 he_mcs_nss_size) |
1870 | | { |
1871 | | u16 nss = 0; |
1872 | | |
1873 | | if (!mode) |
1874 | | return 0; |
1875 | | |
1876 | | if (phy_index == FD_CAP_PHY_INDEX_HE) { |
1877 | | const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs; |
1878 | | int i; |
1879 | | u16 mcs[6]; |
1880 | | |
1881 | | os_memset(mcs, 0xff, 6 * sizeof(u16)); |
1882 | | |
1883 | | if (he_mcs_nss_size == 4) { |
1884 | | mcs[0] = WPA_GET_LE16(&he_mcs[0]); |
1885 | | mcs[1] = WPA_GET_LE16(&he_mcs[2]); |
1886 | | } |
1887 | | |
1888 | | if (he_mcs_nss_size == 8) { |
1889 | | mcs[2] = WPA_GET_LE16(&he_mcs[4]); |
1890 | | mcs[3] = WPA_GET_LE16(&he_mcs[6]); |
1891 | | } |
1892 | | |
1893 | | if (he_mcs_nss_size == 12) { |
1894 | | mcs[4] = WPA_GET_LE16(&he_mcs[8]); |
1895 | | mcs[5] = WPA_GET_LE16(&he_mcs[10]); |
1896 | | } |
1897 | | |
1898 | | for (i = 0; i < HE_NSS_MAX_STREAMS; i++) { |
1899 | | u16 nss_mask = 0x3 << (i * 2); |
1900 | | |
1901 | | /* |
1902 | | * If Tx and/or Rx indicate support for a given NSS, |
1903 | | * count it towards the maximum NSS. |
1904 | | */ |
1905 | | if (he_mcs_nss_size == 4 && |
1906 | | (((mcs[0] & nss_mask) != nss_mask) || |
1907 | | ((mcs[1] & nss_mask) != nss_mask))) { |
1908 | | nss++; |
1909 | | continue; |
1910 | | } |
1911 | | |
1912 | | if (he_mcs_nss_size == 8 && |
1913 | | (((mcs[2] & nss_mask) != nss_mask) || |
1914 | | ((mcs[3] & nss_mask) != nss_mask))) { |
1915 | | nss++; |
1916 | | continue; |
1917 | | } |
1918 | | |
1919 | | if (he_mcs_nss_size == 12 && |
1920 | | (((mcs[4] & nss_mask) != nss_mask) || |
1921 | | ((mcs[5] & nss_mask) != nss_mask))) { |
1922 | | nss++; |
1923 | | continue; |
1924 | | } |
1925 | | } |
1926 | | } else if (phy_index == FD_CAP_PHY_INDEX_EHT) { |
1927 | | u8 rx_nss, tx_nss, max_nss = 0, i; |
1928 | | u8 *mcs = mode->eht_capab[IEEE80211_MODE_AP].mcs; |
1929 | | |
1930 | | /* |
1931 | | * The Supported EHT-MCS And NSS Set field for the AP contains |
1932 | | * one to three EHT-MCS Map fields based on the supported |
1933 | | * bandwidth. Check the first byte (max NSS for Rx/Tx that |
1934 | | * supports EHT-MCS 0-9) for each bandwidth (<= 80, |
1935 | | * 160, 320) to find the maximum NSS. This assumes that |
1936 | | * the lowest MCS rates support the largest number of spatial |
1937 | | * streams. If values are different between Tx, Rx or the |
1938 | | * bandwidths, choose the highest value. |
1939 | | */ |
1940 | | for (i = 0; i < 3; i++) { |
1941 | | rx_nss = mcs[3 * i] & 0x0F; |
1942 | | if (rx_nss > max_nss) |
1943 | | max_nss = rx_nss; |
1944 | | |
1945 | | tx_nss = (mcs[3 * i] & 0xF0) >> 4; |
1946 | | if (tx_nss > max_nss) |
1947 | | max_nss = tx_nss; |
1948 | | } |
1949 | | |
1950 | | nss = max_nss; |
1951 | | } |
1952 | | |
1953 | | if (nss > 4) |
1954 | | return FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT; |
1955 | | if (nss) |
1956 | | return (nss - 1) << FD_CAP_NSS_SHIFT; |
1957 | | |
1958 | | return 0; |
1959 | | } |
1960 | | |
1961 | | |
1962 | | static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd) |
1963 | | { |
1964 | | u16 cap_info, phy_index; |
1965 | | u8 chwidth = FD_CAP_BSS_CHWIDTH_20, he_mcs_nss_size = 4; |
1966 | | struct hostapd_hw_modes *mode = hapd->iface->current_mode; |
1967 | | |
1968 | | cap_info = FD_CAP_ESS; |
1969 | | if (hapd->conf->wpa) |
1970 | | cap_info |= FD_CAP_PRIVACY; |
1971 | | |
1972 | | if (is_6ghz_op_class(hapd->iconf->op_class)) { |
1973 | | switch (hapd->iconf->op_class) { |
1974 | | case 137: |
1975 | | chwidth = FD_CAP_BSS_CHWIDTH_320; |
1976 | | break; |
1977 | | case 135: |
1978 | | he_mcs_nss_size += 4; |
1979 | | /* fallthrough */ |
1980 | | case 134: |
1981 | | he_mcs_nss_size += 4; |
1982 | | chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; |
1983 | | break; |
1984 | | case 133: |
1985 | | chwidth = FD_CAP_BSS_CHWIDTH_80; |
1986 | | break; |
1987 | | case 132: |
1988 | | chwidth = FD_CAP_BSS_CHWIDTH_40; |
1989 | | break; |
1990 | | } |
1991 | | } else { |
1992 | | switch (hostapd_get_oper_chwidth(hapd->iconf)) { |
1993 | | case CONF_OPER_CHWIDTH_80P80MHZ: |
1994 | | he_mcs_nss_size += 4; |
1995 | | /* fallthrough */ |
1996 | | case CONF_OPER_CHWIDTH_160MHZ: |
1997 | | he_mcs_nss_size += 4; |
1998 | | chwidth = FD_CAP_BSS_CHWIDTH_160_80_80; |
1999 | | break; |
2000 | | case CONF_OPER_CHWIDTH_80MHZ: |
2001 | | chwidth = FD_CAP_BSS_CHWIDTH_80; |
2002 | | break; |
2003 | | case CONF_OPER_CHWIDTH_USE_HT: |
2004 | | if (hapd->iconf->secondary_channel) |
2005 | | chwidth = FD_CAP_BSS_CHWIDTH_40; |
2006 | | else |
2007 | | chwidth = FD_CAP_BSS_CHWIDTH_20; |
2008 | | break; |
2009 | | default: |
2010 | | break; |
2011 | | } |
2012 | | } |
2013 | | |
2014 | | phy_index = hostapd_gen_fils_discovery_phy_index(hapd); |
2015 | | cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT; |
2016 | | cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT; |
2017 | | cap_info |= hostapd_gen_fils_discovery_nss(mode, phy_index, |
2018 | | he_mcs_nss_size); |
2019 | | return cap_info; |
2020 | | } |
2021 | | |
2022 | | |
2023 | | static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len) |
2024 | | { |
2025 | | struct ieee80211_mgmt *head; |
2026 | | const u8 *mobility_domain; |
2027 | | u8 *pos, *length_pos, buf[200]; |
2028 | | u16 ctl = 0; |
2029 | | u8 fd_rsn_info[5]; |
2030 | | size_t total_len, buf_len; |
2031 | | |
2032 | | total_len = 24 + 2 + 12; |
2033 | | |
2034 | | /* FILS Discovery Frame Control */ |
2035 | | ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) | |
2036 | | FD_FRAME_CTL_SHORT_SSID_PRESENT | |
2037 | | FD_FRAME_CTL_LENGTH_PRESENT | |
2038 | | FD_FRAME_CTL_CAP_PRESENT; |
2039 | | total_len += 4 + 1 + 2; |
2040 | | |
2041 | | /* Fill primary channel information for 6 GHz channels with over 20 MHz |
2042 | | * bandwidth, if the primary channel is not a PSC */ |
2043 | | if (is_6ghz_op_class(hapd->iconf->op_class) && |
2044 | | !is_6ghz_psc_frequency(ieee80211_chan_to_freq( |
2045 | | NULL, hapd->iconf->op_class, |
2046 | | hapd->iconf->channel)) && |
2047 | | op_class_to_bandwidth(hapd->iconf->op_class) > 20) { |
2048 | | ctl |= FD_FRAME_CTL_PRI_CHAN_PRESENT; |
2049 | | total_len += 2; |
2050 | | } |
2051 | | |
2052 | | /* Check for optional subfields and calculate length */ |
2053 | | if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) { |
2054 | | ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT; |
2055 | | total_len += sizeof(fd_rsn_info); |
2056 | | } |
2057 | | |
2058 | | mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN); |
2059 | | if (mobility_domain) { |
2060 | | ctl |= FD_FRAME_CTL_MD_PRESENT; |
2061 | | total_len += 3; |
2062 | | } |
2063 | | |
2064 | | total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true); |
2065 | | |
2066 | | pos = hostapd_eid_fils_indic(hapd, buf, 0); |
2067 | | buf_len = pos - buf; |
2068 | | total_len += buf_len; |
2069 | | |
2070 | | /* he_elem_len() may return too large a value for FD frame, but that is |
2071 | | * fine here since this is used as the maximum length of the buffer. */ |
2072 | | total_len += he_elem_len(hapd); |
2073 | | |
2074 | | head = os_zalloc(total_len); |
2075 | | if (!head) |
2076 | | return NULL; |
2077 | | |
2078 | | head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
2079 | | WLAN_FC_STYPE_ACTION); |
2080 | | os_memset(head->da, 0xff, ETH_ALEN); |
2081 | | os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); |
2082 | | os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); |
2083 | | |
2084 | | head->u.action.category = WLAN_ACTION_PUBLIC; |
2085 | | head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY; |
2086 | | |
2087 | | pos = &head->u.action.u.public_action.variable[0]; |
2088 | | |
2089 | | /* FILS Discovery Information field */ |
2090 | | |
2091 | | /* FILS Discovery Frame Control */ |
2092 | | WPA_PUT_LE16(pos, ctl); |
2093 | | pos += 2; |
2094 | | |
2095 | | /* Hardware or low-level driver will fill in the Timestamp value */ |
2096 | | pos += 8; |
2097 | | |
2098 | | /* Beacon Interval */ |
2099 | | WPA_PUT_LE16(pos, hapd->iconf->beacon_int); |
2100 | | pos += 2; |
2101 | | |
2102 | | /* Short SSID */ |
2103 | | WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid); |
2104 | | pos += sizeof(hapd->conf->ssid.short_ssid); |
2105 | | |
2106 | | /* Store position of FILS discovery information element Length field */ |
2107 | | length_pos = pos++; |
2108 | | |
2109 | | /* FD Capability */ |
2110 | | WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd)); |
2111 | | pos += 2; |
2112 | | |
2113 | | /* Operating Class and Primary Channel - if a 6 GHz chan is non PSC */ |
2114 | | if (ctl & FD_FRAME_CTL_PRI_CHAN_PRESENT) { |
2115 | | *pos++ = hapd->iconf->op_class; |
2116 | | *pos++ = hapd->iconf->channel; |
2117 | | } |
2118 | | |
2119 | | /* AP Configuration Sequence Number - not present */ |
2120 | | |
2121 | | /* Access Network Options - not present */ |
2122 | | |
2123 | | /* FD RSN Information */ |
2124 | | if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) { |
2125 | | os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info)); |
2126 | | pos += sizeof(fd_rsn_info); |
2127 | | } |
2128 | | |
2129 | | /* Channel Center Frequency Segment 1 - not present */ |
2130 | | |
2131 | | /* Mobility Domain */ |
2132 | | if (ctl & FD_FRAME_CTL_MD_PRESENT) { |
2133 | | os_memcpy(pos, &mobility_domain[2], 3); |
2134 | | pos += 3; |
2135 | | } |
2136 | | |
2137 | | /* Fill in the Length field value */ |
2138 | | *length_pos = pos - (length_pos + 1); |
2139 | | |
2140 | | pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true); |
2141 | | |
2142 | | /* FILS Indication element */ |
2143 | | if (buf_len) { |
2144 | | os_memcpy(pos, buf, buf_len); |
2145 | | pos += buf_len; |
2146 | | } |
2147 | | |
2148 | | if (is_6ghz_op_class(hapd->iconf->op_class)) |
2149 | | pos = hostapd_eid_txpower_envelope(hapd, pos); |
2150 | | |
2151 | | *len = pos - (u8 *) head; |
2152 | | wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template", |
2153 | | head, pos - (u8 *) head); |
2154 | | return (u8 *) head; |
2155 | | } |
2156 | | |
2157 | | |
2158 | | /* Configure FILS Discovery frame transmission parameters */ |
2159 | | static u8 * hostapd_fils_discovery(struct hostapd_data *hapd, |
2160 | | struct wpa_driver_ap_params *params) |
2161 | | { |
2162 | | params->fd_max_int = hapd->conf->fils_discovery_max_int; |
2163 | | if (is_6ghz_op_class(hapd->iconf->op_class) && |
2164 | | params->fd_max_int > FD_MAX_INTERVAL_6GHZ) |
2165 | | params->fd_max_int = FD_MAX_INTERVAL_6GHZ; |
2166 | | |
2167 | | params->fd_min_int = hapd->conf->fils_discovery_min_int; |
2168 | | if (params->fd_min_int > params->fd_max_int) |
2169 | | params->fd_min_int = params->fd_max_int; |
2170 | | |
2171 | | if (params->fd_max_int) |
2172 | | return hostapd_gen_fils_discovery(hapd, |
2173 | | ¶ms->fd_frame_tmpl_len); |
2174 | | |
2175 | | return NULL; |
2176 | | } |
2177 | | |
2178 | | #endif /* CONFIG_FILS */ |
2179 | | |
2180 | | |
2181 | | int ieee802_11_build_ap_params(struct hostapd_data *hapd, |
2182 | | struct wpa_driver_ap_params *params) |
2183 | 0 | { |
2184 | 0 | struct ieee80211_mgmt *head = NULL; |
2185 | 0 | u8 *tail = NULL; |
2186 | 0 | size_t head_len = 0, tail_len = 0; |
2187 | 0 | u8 *resp = NULL; |
2188 | 0 | size_t resp_len = 0; |
2189 | 0 | #ifdef NEED_AP_MLME |
2190 | 0 | u16 capab_info; |
2191 | 0 | u8 *pos, *tailpos, *tailend, *csa_pos; |
2192 | 0 | bool complete = false; |
2193 | 0 | #endif /* NEED_AP_MLME */ |
2194 | |
|
2195 | 0 | os_memset(params, 0, sizeof(*params)); |
2196 | |
|
2197 | 0 | #ifdef NEED_AP_MLME |
2198 | 0 | #define BEACON_HEAD_BUF_SIZE 256 |
2199 | 0 | #define BEACON_TAIL_BUF_SIZE 1500 |
2200 | 0 | head = os_zalloc(BEACON_HEAD_BUF_SIZE); |
2201 | 0 | tail_len = BEACON_TAIL_BUF_SIZE; |
2202 | 0 | #ifdef CONFIG_WPS |
2203 | 0 | if (hapd->conf->wps_state && hapd->wps_beacon_ie) |
2204 | 0 | tail_len += wpabuf_len(hapd->wps_beacon_ie); |
2205 | 0 | #endif /* CONFIG_WPS */ |
2206 | | #ifdef CONFIG_P2P |
2207 | | if (hapd->p2p_beacon_ie) |
2208 | | tail_len += wpabuf_len(hapd->p2p_beacon_ie); |
2209 | | #endif /* CONFIG_P2P */ |
2210 | | #ifdef CONFIG_FST |
2211 | | if (hapd->iface->fst_ies) |
2212 | | tail_len += wpabuf_len(hapd->iface->fst_ies); |
2213 | | #endif /* CONFIG_FST */ |
2214 | 0 | if (hapd->conf->vendor_elements) |
2215 | 0 | tail_len += wpabuf_len(hapd->conf->vendor_elements); |
2216 | |
|
2217 | | #ifdef CONFIG_IEEE80211AC |
2218 | | if (hapd->conf->vendor_vht) { |
2219 | | tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + |
2220 | | 2 + sizeof(struct ieee80211_vht_operation); |
2221 | | } |
2222 | | #endif /* CONFIG_IEEE80211AC */ |
2223 | |
|
2224 | 0 | tail_len += he_elem_len(hapd); |
2225 | |
|
2226 | | #ifdef CONFIG_IEEE80211BE |
2227 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
2228 | | tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); |
2229 | | tail_len += 3 + sizeof(struct ieee80211_eht_operation); |
2230 | | if (hapd->iconf->punct_bitmap) |
2231 | | tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; |
2232 | | |
2233 | | /* |
2234 | | * TODO: Multi-Link element has variable length and can be |
2235 | | * long based on the common info and number of per |
2236 | | * station profiles. For now use 256. |
2237 | | */ |
2238 | | if (hapd->conf->mld_ap) { |
2239 | | tail_len += 256; |
2240 | | |
2241 | | /* for Max Channel Switch Time element during channel |
2242 | | * switch */ |
2243 | | tail_len += 6; |
2244 | | } |
2245 | | } |
2246 | | #endif /* CONFIG_IEEE80211BE */ |
2247 | |
|
2248 | 0 | if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && |
2249 | 0 | hapd == hostapd_mbssid_get_tx_bss(hapd)) |
2250 | 0 | tail_len += 5; /* Multiple BSSID Configuration element */ |
2251 | 0 | tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true); |
2252 | 0 | tail_len += hostapd_mbo_ie_len(hapd); |
2253 | 0 | tail_len += hostapd_eid_owe_trans_len(hapd); |
2254 | 0 | tail_len += hostapd_eid_dpp_cc_len(hapd); |
2255 | 0 | tail_len += hostapd_get_rsne_override_len(hapd); |
2256 | 0 | tail_len += hostapd_get_rsne_override_2_len(hapd); |
2257 | 0 | tail_len += hostapd_get_rsnxe_override_len(hapd); |
2258 | |
|
2259 | 0 | tailpos = tail = os_malloc(tail_len); |
2260 | 0 | if (head == NULL || tail == NULL) { |
2261 | 0 | wpa_printf(MSG_ERROR, "Failed to set beacon data"); |
2262 | 0 | os_free(head); |
2263 | 0 | os_free(tail); |
2264 | 0 | return -1; |
2265 | 0 | } |
2266 | 0 | tailend = tail + tail_len; |
2267 | |
|
2268 | 0 | head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, |
2269 | 0 | WLAN_FC_STYPE_BEACON); |
2270 | 0 | head->duration = host_to_le16(0); |
2271 | 0 | os_memset(head->da, 0xff, ETH_ALEN); |
2272 | |
|
2273 | 0 | os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); |
2274 | 0 | os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); |
2275 | 0 | head->u.beacon.beacon_int = |
2276 | 0 | host_to_le16(hapd->iconf->beacon_int); |
2277 | | |
2278 | | /* hardware or low-level driver will setup seq_ctrl and timestamp */ |
2279 | 0 | capab_info = hostapd_own_capab_info(hapd); |
2280 | 0 | head->u.beacon.capab_info = host_to_le16(capab_info); |
2281 | 0 | pos = &head->u.beacon.variable[0]; |
2282 | | |
2283 | | /* SSID */ |
2284 | 0 | *pos++ = WLAN_EID_SSID; |
2285 | 0 | if (hapd->conf->ignore_broadcast_ssid == 2) { |
2286 | | /* clear the data, but keep the correct length of the SSID */ |
2287 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
2288 | 0 | os_memset(pos, 0, hapd->conf->ssid.ssid_len); |
2289 | 0 | pos += hapd->conf->ssid.ssid_len; |
2290 | 0 | } else if (hapd->conf->ignore_broadcast_ssid) { |
2291 | 0 | *pos++ = 0; /* empty SSID */ |
2292 | 0 | } else { |
2293 | 0 | *pos++ = hapd->conf->ssid.ssid_len; |
2294 | 0 | os_memcpy(pos, hapd->conf->ssid.ssid, |
2295 | 0 | hapd->conf->ssid.ssid_len); |
2296 | 0 | pos += hapd->conf->ssid.ssid_len; |
2297 | 0 | } |
2298 | | |
2299 | | /* Supported rates */ |
2300 | 0 | pos = hostapd_eid_supp_rates(hapd, pos); |
2301 | | |
2302 | | /* DS Params */ |
2303 | 0 | pos = hostapd_eid_ds_params(hapd, pos); |
2304 | |
|
2305 | 0 | head_len = pos - (u8 *) head; |
2306 | |
|
2307 | 0 | tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos); |
2308 | | |
2309 | | /* Power Constraint element */ |
2310 | 0 | tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); |
2311 | | |
2312 | | /* CSA IE */ |
2313 | 0 | csa_pos = hostapd_eid_csa(hapd, tailpos); |
2314 | 0 | if (csa_pos != tailpos) |
2315 | 0 | hapd->cs_c_off_beacon = csa_pos - tail - 1; |
2316 | 0 | tailpos = csa_pos; |
2317 | | |
2318 | | /* ERP Information element */ |
2319 | 0 | tailpos = hostapd_eid_erp_info(hapd, tailpos); |
2320 | | |
2321 | | /* Extended supported rates */ |
2322 | 0 | tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); |
2323 | |
|
2324 | 0 | tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos); |
2325 | 0 | tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos); |
2326 | 0 | tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, |
2327 | 0 | tailend - tailpos); |
2328 | 0 | tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos); |
2329 | | |
2330 | | /* eCSA IE */ |
2331 | 0 | csa_pos = hostapd_eid_ecsa(hapd, tailpos); |
2332 | 0 | if (csa_pos != tailpos) |
2333 | 0 | hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; |
2334 | 0 | tailpos = csa_pos; |
2335 | |
|
2336 | 0 | tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); |
2337 | 0 | tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); |
2338 | 0 | tailpos = hostapd_eid_ht_operation(hapd, tailpos); |
2339 | |
|
2340 | 0 | if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) { |
2341 | 0 | if (ieee802_11_build_ap_params_mbssid(hapd, params)) { |
2342 | 0 | os_free(head); |
2343 | 0 | os_free(tail); |
2344 | 0 | wpa_printf(MSG_ERROR, |
2345 | 0 | "MBSSID: Failed to set beacon data"); |
2346 | 0 | return -1; |
2347 | 0 | } |
2348 | 0 | complete = hapd->iconf->mbssid == MBSSID_ENABLED || |
2349 | 0 | (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && |
2350 | 0 | params->mbssid.mbssid_elem_count == 1); |
2351 | 0 | } |
2352 | | |
2353 | 0 | tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete); |
2354 | | |
2355 | | /* |
2356 | | * TODO: Time Advertisement element should only be included in some |
2357 | | * DTIM Beacon frames. |
2358 | | */ |
2359 | 0 | tailpos = hostapd_eid_time_adv(hapd, tailpos); |
2360 | |
|
2361 | 0 | tailpos = hostapd_eid_interworking(hapd, tailpos); |
2362 | 0 | tailpos = hostapd_eid_adv_proto(hapd, tailpos); |
2363 | 0 | tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); |
2364 | |
|
2365 | | #ifdef CONFIG_FST |
2366 | | if (hapd->iface->fst_ies) { |
2367 | | os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), |
2368 | | wpabuf_len(hapd->iface->fst_ies)); |
2369 | | tailpos += wpabuf_len(hapd->iface->fst_ies); |
2370 | | } |
2371 | | #endif /* CONFIG_FST */ |
2372 | |
|
2373 | | #ifdef CONFIG_IEEE80211AC |
2374 | | if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && |
2375 | | !is_6ghz_op_class(hapd->iconf->op_class)) { |
2376 | | tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); |
2377 | | tailpos = hostapd_eid_vht_operation(hapd, tailpos); |
2378 | | tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); |
2379 | | } |
2380 | | #endif /* CONFIG_IEEE80211AC */ |
2381 | |
|
2382 | | #ifdef CONFIG_IEEE80211AX |
2383 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && |
2384 | | is_6ghz_op_class(hapd->iconf->op_class)) |
2385 | | tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); |
2386 | | #endif /* CONFIG_IEEE80211AX */ |
2387 | |
|
2388 | 0 | tailpos = hostapd_eid_chsw_wrapper(hapd, tailpos); |
2389 | |
|
2390 | 0 | tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true); |
2391 | 0 | tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); |
2392 | | |
2393 | | /* Max Channel Switch Time element */ |
2394 | 0 | tailpos = hostapd_eid_max_cs_time(hapd, tailpos); |
2395 | |
|
2396 | 0 | tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos); |
2397 | 0 | tailpos = hostapd_eid_mbssid_config(hapd, tailpos, |
2398 | 0 | params->mbssid.mbssid_elem_count); |
2399 | |
|
2400 | | #ifdef CONFIG_IEEE80211AX |
2401 | | if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { |
2402 | | u8 *cca_pos; |
2403 | | |
2404 | | tailpos = hostapd_eid_he_capab(hapd, tailpos, |
2405 | | IEEE80211_MODE_AP); |
2406 | | tailpos = hostapd_eid_he_operation(hapd, tailpos); |
2407 | | |
2408 | | /* BSS Color Change Announcement element */ |
2409 | | cca_pos = hostapd_eid_cca(hapd, tailpos); |
2410 | | if (cca_pos != tailpos) |
2411 | | hapd->cca_c_off_beacon = cca_pos - tail - 2; |
2412 | | tailpos = cca_pos; |
2413 | | |
2414 | | tailpos = hostapd_eid_spatial_reuse(hapd, tailpos); |
2415 | | tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); |
2416 | | tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos); |
2417 | | } |
2418 | | #endif /* CONFIG_IEEE80211AX */ |
2419 | |
|
2420 | | #ifdef CONFIG_IEEE80211BE |
2421 | | if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { |
2422 | | if (hapd->conf->mld_ap) |
2423 | | tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL, |
2424 | | tailpos, false); |
2425 | | tailpos = hostapd_eid_eht_capab(hapd, tailpos, |
2426 | | IEEE80211_MODE_AP); |
2427 | | tailpos = hostapd_eid_eht_operation(hapd, tailpos); |
2428 | | } |
2429 | | #endif /* CONFIG_IEEE80211BE */ |
2430 | |
|
2431 | | #ifdef CONFIG_IEEE80211AC |
2432 | | if (hapd->conf->vendor_vht) |
2433 | | tailpos = hostapd_eid_vendor_vht(hapd, tailpos); |
2434 | | #endif /* CONFIG_IEEE80211AC */ |
2435 | | |
2436 | | /* WPA */ |
2437 | 0 | tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos); |
2438 | | |
2439 | | /* Wi-Fi Alliance WMM */ |
2440 | 0 | tailpos = hostapd_eid_wmm(hapd, tailpos); |
2441 | |
|
2442 | 0 | #ifdef CONFIG_WPS |
2443 | 0 | if (hapd->conf->wps_state && hapd->wps_beacon_ie) { |
2444 | 0 | os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie), |
2445 | 0 | wpabuf_len(hapd->wps_beacon_ie)); |
2446 | 0 | tailpos += wpabuf_len(hapd->wps_beacon_ie); |
2447 | 0 | } |
2448 | 0 | #endif /* CONFIG_WPS */ |
2449 | |
|
2450 | | #ifdef CONFIG_P2P |
2451 | | if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { |
2452 | | os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), |
2453 | | wpabuf_len(hapd->p2p_beacon_ie)); |
2454 | | tailpos += wpabuf_len(hapd->p2p_beacon_ie); |
2455 | | } |
2456 | | #endif /* CONFIG_P2P */ |
2457 | | #ifdef CONFIG_P2P_MANAGER |
2458 | | if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == |
2459 | | P2P_MANAGE) |
2460 | | tailpos = hostapd_eid_p2p_manage(hapd, tailpos); |
2461 | | #endif /* CONFIG_P2P_MANAGER */ |
2462 | |
|
2463 | 0 | #ifdef CONFIG_HS20 |
2464 | 0 | tailpos = hostapd_eid_hs20_indication(hapd, tailpos); |
2465 | 0 | #endif /* CONFIG_HS20 */ |
2466 | |
|
2467 | 0 | tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); |
2468 | 0 | tailpos = hostapd_eid_owe_trans(hapd, tailpos, |
2469 | 0 | tail + tail_len - tailpos); |
2470 | 0 | tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos); |
2471 | |
|
2472 | 0 | tailpos = hostapd_get_rsne_override(hapd, tailpos, |
2473 | 0 | tail + tail_len - tailpos); |
2474 | 0 | tailpos = hostapd_get_rsne_override_2(hapd, tailpos, |
2475 | 0 | tail + tail_len - tailpos); |
2476 | 0 | tailpos = hostapd_get_rsnxe_override(hapd, tailpos, |
2477 | 0 | tail + tail_len - tailpos); |
2478 | |
|
2479 | 0 | if (hapd->conf->vendor_elements) { |
2480 | 0 | os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), |
2481 | 0 | wpabuf_len(hapd->conf->vendor_elements)); |
2482 | 0 | tailpos += wpabuf_len(hapd->conf->vendor_elements); |
2483 | 0 | } |
2484 | |
|
2485 | 0 | tail_len = tailpos > tail ? tailpos - tail : 0; |
2486 | |
|
2487 | 0 | resp = hostapd_probe_resp_offloads(hapd, &resp_len); |
2488 | 0 | #endif /* NEED_AP_MLME */ |
2489 | | |
2490 | | /* If key management offload is enabled, configure PSK to the driver. */ |
2491 | 0 | if (wpa_key_mgmt_wpa_psk_no_sae(hapd->conf->wpa_key_mgmt) && |
2492 | 0 | (hapd->iface->drv_flags2 & |
2493 | 0 | WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) { |
2494 | 0 | if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) { |
2495 | 0 | os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk, |
2496 | 0 | PMK_LEN); |
2497 | 0 | params->psk_len = PMK_LEN; |
2498 | 0 | } else if (hapd->conf->ssid.wpa_passphrase && |
2499 | 0 | pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase, |
2500 | 0 | hapd->conf->ssid.ssid, |
2501 | 0 | hapd->conf->ssid.ssid_len, 4096, |
2502 | 0 | params->psk, PMK_LEN) == 0) { |
2503 | 0 | params->psk_len = PMK_LEN; |
2504 | 0 | } |
2505 | 0 | } |
2506 | |
|
2507 | | #ifdef CONFIG_SAE |
2508 | | /* If SAE offload is enabled, provide password to lower layer for |
2509 | | * SAE authentication and PMK generation. |
2510 | | */ |
2511 | | if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt | |
2512 | | hapd->conf->rsn_override_key_mgmt | |
2513 | | hapd->conf->rsn_override_key_mgmt_2) && |
2514 | | (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) { |
2515 | | if (hostapd_sae_pk_in_use(hapd->conf)) { |
2516 | | wpa_printf(MSG_ERROR, |
2517 | | "SAE PK not supported with SAE offload"); |
2518 | | return -1; |
2519 | | } |
2520 | | |
2521 | | if (hostapd_sae_pw_id_in_use(hapd->conf)) { |
2522 | | wpa_printf(MSG_ERROR, |
2523 | | "SAE Password Identifiers not supported with SAE offload"); |
2524 | | return -1; |
2525 | | } |
2526 | | |
2527 | | params->sae_password = sae_get_password(hapd, NULL, NULL, NULL, |
2528 | | NULL, NULL); |
2529 | | if (!params->sae_password) { |
2530 | | wpa_printf(MSG_ERROR, "SAE password not configured for offload"); |
2531 | | return -1; |
2532 | | } |
2533 | | } |
2534 | | #endif /* CONFIG_SAE */ |
2535 | |
|
2536 | 0 | params->head = (u8 *) head; |
2537 | 0 | params->head_len = head_len; |
2538 | 0 | params->tail = tail; |
2539 | 0 | params->tail_len = tail_len; |
2540 | 0 | params->proberesp = resp; |
2541 | 0 | params->proberesp_len = resp_len; |
2542 | 0 | params->dtim_period = hapd->conf->dtim_period; |
2543 | 0 | params->beacon_int = hapd->iconf->beacon_int; |
2544 | 0 | params->basic_rates = hapd->iface->basic_rates; |
2545 | 0 | params->beacon_rate = hapd->iconf->beacon_rate; |
2546 | 0 | params->rate_type = hapd->iconf->rate_type; |
2547 | 0 | params->ssid = hapd->conf->ssid.ssid; |
2548 | 0 | params->ssid_len = hapd->conf->ssid.ssid_len; |
2549 | 0 | if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == |
2550 | 0 | (WPA_PROTO_WPA | WPA_PROTO_RSN)) |
2551 | 0 | params->pairwise_ciphers = hapd->conf->wpa_pairwise | |
2552 | 0 | hapd->conf->rsn_pairwise; |
2553 | 0 | else if (hapd->conf->wpa & WPA_PROTO_RSN) |
2554 | 0 | params->pairwise_ciphers = hapd->conf->rsn_pairwise; |
2555 | 0 | else if (hapd->conf->wpa & WPA_PROTO_WPA) |
2556 | 0 | params->pairwise_ciphers = hapd->conf->wpa_pairwise; |
2557 | 0 | params->group_cipher = hapd->conf->wpa_group; |
2558 | 0 | params->key_mgmt_suites = hapd->conf->wpa_key_mgmt | |
2559 | 0 | hapd->conf->rsn_override_key_mgmt | |
2560 | 0 | hapd->conf->rsn_override_key_mgmt_2; |
2561 | 0 | params->auth_algs = hapd->conf->auth_algs; |
2562 | 0 | params->wpa_version = hapd->conf->wpa; |
2563 | 0 | params->privacy = hapd->conf->wpa; |
2564 | | #ifdef CONFIG_WEP |
2565 | | params->privacy |= hapd->conf->ssid.wep.keys_set || |
2566 | | (hapd->conf->ieee802_1x && |
2567 | | (hapd->conf->default_wep_key_len || |
2568 | | hapd->conf->individual_wep_key_len)); |
2569 | | #endif /* CONFIG_WEP */ |
2570 | 0 | switch (hapd->conf->ignore_broadcast_ssid) { |
2571 | 0 | case 0: |
2572 | 0 | params->hide_ssid = NO_SSID_HIDING; |
2573 | 0 | break; |
2574 | 0 | case 1: |
2575 | 0 | params->hide_ssid = HIDDEN_SSID_ZERO_LEN; |
2576 | 0 | break; |
2577 | 0 | case 2: |
2578 | 0 | params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; |
2579 | 0 | break; |
2580 | 0 | } |
2581 | 0 | params->isolate = hapd->conf->isolate; |
2582 | 0 | #ifdef NEED_AP_MLME |
2583 | 0 | params->cts_protect = !!(ieee802_11_erp_info(hapd) & |
2584 | 0 | ERP_INFO_USE_PROTECTION); |
2585 | 0 | params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && |
2586 | 0 | hapd->iconf->preamble == SHORT_PREAMBLE; |
2587 | 0 | if (hapd->iface->current_mode && |
2588 | 0 | hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) |
2589 | 0 | params->short_slot_time = |
2590 | 0 | hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; |
2591 | 0 | else |
2592 | 0 | params->short_slot_time = -1; |
2593 | 0 | if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) |
2594 | 0 | params->ht_opmode = -1; |
2595 | 0 | else |
2596 | 0 | params->ht_opmode = hapd->iface->ht_op_mode; |
2597 | 0 | #endif /* NEED_AP_MLME */ |
2598 | 0 | params->interworking = hapd->conf->interworking; |
2599 | 0 | if (hapd->conf->interworking && |
2600 | 0 | !is_zero_ether_addr(hapd->conf->hessid)) |
2601 | 0 | params->hessid = hapd->conf->hessid; |
2602 | 0 | params->access_network_type = hapd->conf->access_network_type; |
2603 | 0 | params->ap_max_inactivity = hapd->conf->ap_max_inactivity; |
2604 | | #ifdef CONFIG_P2P |
2605 | | params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow; |
2606 | | #endif /* CONFIG_P2P */ |
2607 | 0 | #ifdef CONFIG_HS20 |
2608 | 0 | params->disable_dgaf = hapd->conf->disable_dgaf; |
2609 | 0 | #endif /* CONFIG_HS20 */ |
2610 | 0 | params->multicast_to_unicast = hapd->conf->multicast_to_unicast; |
2611 | 0 | params->pbss = hapd->conf->pbss; |
2612 | |
|
2613 | 0 | if (hapd->conf->ftm_responder) { |
2614 | 0 | if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { |
2615 | 0 | params->ftm_responder = 1; |
2616 | 0 | params->lci = hapd->iface->conf->lci; |
2617 | 0 | params->civic = hapd->iface->conf->civic; |
2618 | 0 | } else { |
2619 | 0 | wpa_printf(MSG_WARNING, |
2620 | 0 | "Not configuring FTM responder as the driver doesn't advertise support for it"); |
2621 | 0 | } |
2622 | 0 | } |
2623 | |
|
2624 | | #ifdef CONFIG_IEEE80211BE |
2625 | | if (hapd->conf->mld_ap && hapd->iconf->ieee80211be && |
2626 | | !hapd->conf->disable_11be) { |
2627 | | params->mld_ap = true; |
2628 | | params->mld_link_id = hapd->mld_link_id; |
2629 | | } |
2630 | | #endif /* CONFIG_IEEE80211BE */ |
2631 | |
|
2632 | 0 | return 0; |
2633 | 0 | } |
2634 | | |
2635 | | |
2636 | | void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) |
2637 | 0 | { |
2638 | 0 | os_free(params->tail); |
2639 | 0 | params->tail = NULL; |
2640 | 0 | os_free(params->head); |
2641 | 0 | params->head = NULL; |
2642 | 0 | os_free(params->proberesp); |
2643 | 0 | params->proberesp = NULL; |
2644 | 0 | os_free(params->mbssid.mbssid_elem); |
2645 | 0 | params->mbssid.mbssid_elem = NULL; |
2646 | 0 | os_free(params->mbssid.mbssid_elem_offset); |
2647 | 0 | params->mbssid.mbssid_elem_offset = NULL; |
2648 | 0 | os_free(params->mbssid.rnr_elem); |
2649 | 0 | params->mbssid.rnr_elem = NULL; |
2650 | 0 | os_free(params->mbssid.rnr_elem_offset); |
2651 | 0 | params->mbssid.rnr_elem_offset = NULL; |
2652 | | #ifdef CONFIG_FILS |
2653 | | os_free(params->fd_frame_tmpl); |
2654 | | params->fd_frame_tmpl = NULL; |
2655 | | #endif /* CONFIG_FILS */ |
2656 | | #ifdef CONFIG_IEEE80211AX |
2657 | | os_free(params->ubpr.unsol_bcast_probe_resp_tmpl); |
2658 | | params->ubpr.unsol_bcast_probe_resp_tmpl = NULL; |
2659 | | #endif /* CONFIG_IEEE80211AX */ |
2660 | 0 | os_free(params->allowed_freqs); |
2661 | 0 | params->allowed_freqs = NULL; |
2662 | 0 | } |
2663 | | |
2664 | | |
2665 | | static int __ieee802_11_set_beacon(struct hostapd_data *hapd) |
2666 | 0 | { |
2667 | 0 | struct wpa_driver_ap_params params; |
2668 | 0 | struct hostapd_freq_params freq; |
2669 | 0 | struct hostapd_iface *iface = hapd->iface; |
2670 | 0 | struct hostapd_config *iconf = iface->conf; |
2671 | 0 | struct hostapd_hw_modes *cmode = iface->current_mode; |
2672 | 0 | struct wpabuf *beacon, *proberesp, *assocresp; |
2673 | 0 | bool twt_he_responder = false; |
2674 | 0 | int res, ret = -1, i; |
2675 | 0 | struct hostapd_hw_modes *mode; |
2676 | |
|
2677 | 0 | if (!hapd->drv_priv) { |
2678 | 0 | wpa_printf(MSG_ERROR, "Interface is disabled"); |
2679 | 0 | return -1; |
2680 | 0 | } |
2681 | | |
2682 | 0 | if (hapd->csa_in_progress) { |
2683 | 0 | wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); |
2684 | 0 | return -1; |
2685 | 0 | } |
2686 | | |
2687 | | #ifdef CONFIG_IEEE80211AX |
2688 | | if (hapd->cca_in_progress) { |
2689 | | wpa_printf(MSG_ERROR, |
2690 | | "Cannot set beacons during CCA period"); |
2691 | | return -1; |
2692 | | } |
2693 | | #endif /* CONFIG_IEEE80211AX */ |
2694 | | |
2695 | 0 | hapd->beacon_set_done = 1; |
2696 | |
|
2697 | 0 | if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) |
2698 | 0 | return -1; |
2699 | | |
2700 | 0 | if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < |
2701 | 0 | 0) |
2702 | 0 | goto fail; |
2703 | | |
2704 | 0 | params.beacon_ies = beacon; |
2705 | 0 | params.proberesp_ies = proberesp; |
2706 | 0 | params.assocresp_ies = assocresp; |
2707 | 0 | params.reenable = hapd->reenable_beacon; |
2708 | | #ifdef CONFIG_IEEE80211AX |
2709 | | params.he_spr_ctrl = hapd->iface->conf->spr.sr_control; |
2710 | | params.he_spr_non_srg_obss_pd_max_offset = |
2711 | | hapd->iface->conf->spr.non_srg_obss_pd_max_offset; |
2712 | | params.he_spr_srg_obss_pd_min_offset = |
2713 | | hapd->iface->conf->spr.srg_obss_pd_min_offset; |
2714 | | params.he_spr_srg_obss_pd_max_offset = |
2715 | | hapd->iface->conf->spr.srg_obss_pd_max_offset; |
2716 | | os_memcpy(params.he_spr_bss_color_bitmap, |
2717 | | hapd->iface->conf->spr.srg_bss_color_bitmap, 8); |
2718 | | os_memcpy(params.he_spr_partial_bssid_bitmap, |
2719 | | hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8); |
2720 | | params.he_bss_color_disabled = |
2721 | | hapd->iface->conf->he_op.he_bss_color_disabled; |
2722 | | params.he_bss_color_partial = |
2723 | | hapd->iface->conf->he_op.he_bss_color_partial; |
2724 | | params.he_bss_color = hapd->iface->conf->he_op.he_bss_color; |
2725 | | twt_he_responder = hostapd_get_he_twt_responder(hapd, |
2726 | | IEEE80211_MODE_AP); |
2727 | | params.ubpr.unsol_bcast_probe_resp_tmpl = |
2728 | | hostapd_unsol_bcast_probe_resp(hapd, ¶ms.ubpr); |
2729 | | #endif /* CONFIG_IEEE80211AX */ |
2730 | 0 | params.twt_responder = |
2731 | 0 | twt_he_responder || hostapd_get_ht_vht_twt_responder(hapd); |
2732 | 0 | hapd->reenable_beacon = 0; |
2733 | | #ifdef CONFIG_SAE |
2734 | | params.sae_pwe = hapd->conf->sae_pwe; |
2735 | | #endif /* CONFIG_SAE */ |
2736 | |
|
2737 | | #ifdef CONFIG_FILS |
2738 | | params.fd_frame_tmpl = hostapd_fils_discovery(hapd, ¶ms); |
2739 | | #endif /* CONFIG_FILS */ |
2740 | |
|
2741 | | #ifdef CONFIG_IEEE80211BE |
2742 | | params.punct_bitmap = iconf->punct_bitmap; |
2743 | | #endif /* CONFIG_IEEE80211BE */ |
2744 | |
|
2745 | 0 | if (cmode && |
2746 | 0 | hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, |
2747 | 0 | iconf->channel, iconf->enable_edmg, |
2748 | 0 | iconf->edmg_channel, iconf->ieee80211n, |
2749 | 0 | iconf->ieee80211ac, iconf->ieee80211ax, |
2750 | 0 | iconf->ieee80211be, |
2751 | 0 | iconf->secondary_channel, |
2752 | 0 | hostapd_get_oper_chwidth(iconf), |
2753 | 0 | hostapd_get_oper_centr_freq_seg0_idx(iconf), |
2754 | 0 | hostapd_get_oper_centr_freq_seg1_idx(iconf), |
2755 | 0 | cmode->vht_capab, |
2756 | 0 | &cmode->he_capab[IEEE80211_MODE_AP], |
2757 | 0 | &cmode->eht_capab[IEEE80211_MODE_AP], |
2758 | 0 | hostapd_get_punct_bitmap(hapd)) == 0) { |
2759 | 0 | freq.link_id = -1; |
2760 | | #ifdef CONFIG_IEEE80211BE |
2761 | | if (hapd->conf->mld_ap) |
2762 | | freq.link_id = hapd->mld_link_id; |
2763 | | #endif /* CONFIG_IEEE80211BE */ |
2764 | 0 | params.freq = &freq; |
2765 | 0 | } |
2766 | |
|
2767 | 0 | for (i = 0; i < hapd->iface->num_hw_features; i++) { |
2768 | 0 | mode = &hapd->iface->hw_features[i]; |
2769 | |
|
2770 | 0 | if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY && |
2771 | 0 | iconf->hw_mode != mode->mode) |
2772 | 0 | continue; |
2773 | | |
2774 | 0 | hostapd_get_hw_mode_any_channels(hapd, mode, |
2775 | 0 | !(iconf->acs_freq_list.num || |
2776 | 0 | iconf->acs_ch_list.num), |
2777 | 0 | true, ¶ms.allowed_freqs); |
2778 | 0 | } |
2779 | |
|
2780 | 0 | res = hostapd_drv_set_ap(hapd, ¶ms); |
2781 | 0 | hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); |
2782 | 0 | if (res) |
2783 | 0 | wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); |
2784 | 0 | else |
2785 | 0 | ret = 0; |
2786 | 0 | fail: |
2787 | 0 | ieee802_11_free_ap_params(¶ms); |
2788 | 0 | return ret; |
2789 | 0 | } |
2790 | | |
2791 | | |
2792 | | void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd) |
2793 | 0 | { |
2794 | 0 | __ieee802_11_set_beacon(hapd); |
2795 | 0 | } |
2796 | | |
2797 | | |
2798 | | #ifdef CONFIG_IEEE80211BE |
2799 | | |
2800 | | static int hostapd_get_probe_resp_tmpl(struct hostapd_data *hapd, |
2801 | | struct probe_resp_params *params, |
2802 | | bool is_ml_sta_info) |
2803 | | { |
2804 | | os_memset(params, 0, sizeof(*params)); |
2805 | | hostapd_gen_probe_resp(hapd, params); |
2806 | | if (!params->resp) |
2807 | | return -1; |
2808 | | |
2809 | | /* The caller takes care of freeing params->resp. */ |
2810 | | return 0; |
2811 | | } |
2812 | | |
2813 | | |
2814 | | static bool is_restricted_eid_in_sta_profile(u8 eid, bool tx_vap) |
2815 | | { |
2816 | | switch (eid) { |
2817 | | case WLAN_EID_TIM: |
2818 | | case WLAN_EID_BSS_MAX_IDLE_PERIOD: |
2819 | | case WLAN_EID_MULTIPLE_BSSID: |
2820 | | case WLAN_EID_REDUCED_NEIGHBOR_REPORT: |
2821 | | case WLAN_EID_NEIGHBOR_REPORT: |
2822 | | return true; |
2823 | | case WLAN_EID_SSID: |
2824 | | /* SSID is not restricted for non-transmitted BSSID */ |
2825 | | return tx_vap; |
2826 | | default: |
2827 | | return false; |
2828 | | } |
2829 | | } |
2830 | | |
2831 | | |
2832 | | static bool is_restricted_ext_eid_in_sta_profile(u8 ext_id) |
2833 | | { |
2834 | | switch (ext_id) { |
2835 | | case WLAN_EID_EXT_MULTI_LINK: |
2836 | | return true; |
2837 | | default: |
2838 | | return false; |
2839 | | } |
2840 | | } |
2841 | | |
2842 | | |
2843 | | /* Create the link STA profiles based on inheritance from the reporting |
2844 | | * profile. |
2845 | | * |
2846 | | * NOTE: The same function is used for length calculation as well as filling |
2847 | | * data in the given buffer. This avoids risk of not updating the length |
2848 | | * function but filling function or vice versa. |
2849 | | */ |
2850 | | static size_t hostapd_add_sta_profile(struct ieee80211_mgmt *link_fdata, |
2851 | | size_t link_data_len, |
2852 | | struct ieee80211_mgmt *own_fdata, |
2853 | | size_t own_data_len, |
2854 | | u8 *sta_profile, bool tx_vap) |
2855 | | { |
2856 | | const struct element *link_elem; |
2857 | | size_t sta_profile_len = 0; |
2858 | | const u8 *link_elem_data; |
2859 | | u8 link_ele_len; |
2860 | | u8 *link_data; |
2861 | | const struct element *own_elem; |
2862 | | u8 link_eid, own_eid, own_ele_len; |
2863 | | const u8 *own_elem_data; |
2864 | | u8 *own_data; |
2865 | | bool is_ext; |
2866 | | bool ie_found; |
2867 | | u8 non_inherit_ele_ext_list[256] = { 0 }; |
2868 | | u8 non_inherit_ele_ext_list_len = 0; |
2869 | | u8 non_inherit_ele_list[256] = { 0 }; |
2870 | | u8 non_inherit_ele_list_len = 0; |
2871 | | u8 num_link_elem_vendor_ies = 0, num_own_elem_vendor_ies = 0; |
2872 | | bool add_vendor_ies = false, is_identical_vendor_ies = true; |
2873 | | /* The bitmap of parsed EIDs. There are 256 EIDs and ext EIDs, so 32 |
2874 | | * bytes to store the bitmaps. */ |
2875 | | u8 parsed_eid_bmap[32] = { 0 }, parsed_ext_eid_bmap[32] = { 0 }; |
2876 | | /* extra len used in the logic includes the element id and len */ |
2877 | | u8 extra_len = 2; |
2878 | | |
2879 | | /* Include len for capab info */ |
2880 | | sta_profile_len += sizeof(le16); |
2881 | | if (sta_profile) { |
2882 | | os_memcpy(sta_profile, &link_fdata->u.probe_resp.capab_info, |
2883 | | sizeof(le16)); |
2884 | | sta_profile += sizeof(le16); |
2885 | | } |
2886 | | |
2887 | | own_data = own_fdata->u.probe_resp.variable; |
2888 | | link_data = link_fdata->u.probe_resp.variable; |
2889 | | |
2890 | | /* The below logic takes the reporting BSS data and reported BSS data |
2891 | | * and performs intersection to build the STA profile of the reported |
2892 | | * BSS. Certain elements are not added to the STA profile as |
2893 | | * recommended in standard. Matching element information in the |
2894 | | * reporting BSS profile are ignored in the STA profile. Remaining |
2895 | | * elements pertaining to the STA profile are appended at the end. */ |
2896 | | for_each_element(own_elem, own_data, own_data_len) { |
2897 | | is_ext = false; |
2898 | | ie_found = false; |
2899 | | |
2900 | | /* Pick one of own elements and get its EID and length */ |
2901 | | own_elem_data = own_elem->data; |
2902 | | own_ele_len = own_elem->datalen; |
2903 | | |
2904 | | if (own_elem->id == WLAN_EID_EXTENSION) { |
2905 | | is_ext = true; |
2906 | | own_eid = *(own_elem_data); |
2907 | | if (is_restricted_ext_eid_in_sta_profile(own_eid)) |
2908 | | continue; |
2909 | | } else { |
2910 | | own_eid = own_elem->id; |
2911 | | if (is_restricted_eid_in_sta_profile(own_eid, tx_vap)) |
2912 | | continue; |
2913 | | } |
2914 | | |
2915 | | for_each_element(link_elem, link_data, link_data_len) { |
2916 | | /* If the element type mismatches, do not consider |
2917 | | * this link element for comparison. */ |
2918 | | if ((link_elem->id == WLAN_EID_EXTENSION && |
2919 | | !is_ext) || |
2920 | | (is_ext && link_elem->id != WLAN_EID_EXTENSION)) |
2921 | | continue; |
2922 | | |
2923 | | /* Comparison can be done so get the link element and |
2924 | | * its EID and length. */ |
2925 | | link_elem_data = link_elem->data; |
2926 | | link_ele_len = link_elem->datalen; |
2927 | | |
2928 | | if (link_elem->id == WLAN_EID_EXTENSION) |
2929 | | link_eid = *(link_elem_data); |
2930 | | else |
2931 | | link_eid = link_elem->id; |
2932 | | |
2933 | | /* Ignore if EID does not match */ |
2934 | | if (own_eid != link_eid) |
2935 | | continue; |
2936 | | |
2937 | | ie_found = true; |
2938 | | |
2939 | | /* Ignore if the contents is identical. */ |
2940 | | if (own_ele_len == link_ele_len && |
2941 | | os_memcmp(own_elem->data, link_elem->data, |
2942 | | own_ele_len) == 0) { |
2943 | | if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { |
2944 | | is_identical_vendor_ies = true; |
2945 | | num_own_elem_vendor_ies++; |
2946 | | } |
2947 | | |
2948 | | /* Update the parsed EIDs bitmap */ |
2949 | | if (is_ext) |
2950 | | parsed_ext_eid_bmap[own_eid / 8] |= |
2951 | | BIT(own_eid % 8); |
2952 | | else |
2953 | | parsed_eid_bmap[own_eid / 8] |= |
2954 | | BIT(own_eid % 8); |
2955 | | break; |
2956 | | } |
2957 | | |
2958 | | /* No need to include this non-matching Vendor Specific |
2959 | | * element explicitly at this point. */ |
2960 | | if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { |
2961 | | is_identical_vendor_ies = false; |
2962 | | continue; |
2963 | | } |
2964 | | |
2965 | | /* This element is present in the reported profile |
2966 | | * as well as present in the reporting profile. |
2967 | | * However, there is a mismatch in the contents and |
2968 | | * hence, include this in the per STA profile. */ |
2969 | | sta_profile_len += link_ele_len + extra_len; |
2970 | | if (sta_profile) { |
2971 | | os_memcpy(sta_profile, |
2972 | | link_elem->data - extra_len, |
2973 | | link_ele_len + extra_len); |
2974 | | sta_profile += link_ele_len + extra_len; |
2975 | | } |
2976 | | |
2977 | | /* Update the parsed EIDs bitmap */ |
2978 | | if (is_ext) |
2979 | | parsed_ext_eid_bmap[own_eid / 8] |= |
2980 | | BIT(own_eid % 8); |
2981 | | else |
2982 | | parsed_eid_bmap[own_eid / 8] |= |
2983 | | BIT(own_eid % 8); |
2984 | | break; |
2985 | | } |
2986 | | |
2987 | | /* We found at least one Vendor Specific element in reporting |
2988 | | * link which is not same (or present) in the reported link. We |
2989 | | * need to include all Vendor Specific elements from the |
2990 | | * reported link. */ |
2991 | | if (!is_identical_vendor_ies) |
2992 | | add_vendor_ies = true; |
2993 | | |
2994 | | /* This is a unique element in the reporting profile which is |
2995 | | * not present in the reported profile. Update the |
2996 | | * non-inheritance list. */ |
2997 | | if (!ie_found) { |
2998 | | u8 idx; |
2999 | | |
3000 | | if (is_ext) { |
3001 | | idx = non_inherit_ele_ext_list_len++; |
3002 | | non_inherit_ele_ext_list[idx] = own_eid; |
3003 | | } else { |
3004 | | idx = non_inherit_ele_list_len++; |
3005 | | non_inherit_ele_list[idx] = own_eid; |
3006 | | } |
3007 | | } |
3008 | | } |
3009 | | |
3010 | | /* Parse the remaining elements in the reported profile */ |
3011 | | for_each_element(link_elem, link_data, link_data_len) { |
3012 | | link_elem_data = link_elem->data; |
3013 | | link_ele_len = link_elem->datalen; |
3014 | | |
3015 | | /* No need to check this Vendor Specific element at this point. |
3016 | | * Just take the count and continue. */ |
3017 | | if (link_elem->id == WLAN_EID_VENDOR_SPECIFIC) { |
3018 | | num_link_elem_vendor_ies++; |
3019 | | continue; |
3020 | | } |
3021 | | |
3022 | | if (link_elem->id == WLAN_EID_EXTENSION) { |
3023 | | link_eid = *(link_elem_data); |
3024 | | |
3025 | | if ((parsed_ext_eid_bmap[link_eid / 8] & |
3026 | | BIT(link_eid % 8)) || |
3027 | | is_restricted_ext_eid_in_sta_profile(link_eid)) |
3028 | | continue; |
3029 | | } else { |
3030 | | link_eid = link_elem->id; |
3031 | | |
3032 | | if ((parsed_eid_bmap[link_eid / 8] & |
3033 | | BIT(link_eid % 8)) || |
3034 | | is_restricted_eid_in_sta_profile(link_eid, tx_vap)) |
3035 | | continue; |
3036 | | } |
3037 | | |
3038 | | sta_profile_len += link_ele_len + extra_len; |
3039 | | if (sta_profile) { |
3040 | | os_memcpy(sta_profile, link_elem_data - extra_len, |
3041 | | link_ele_len + extra_len); |
3042 | | sta_profile += link_ele_len + extra_len; |
3043 | | } |
3044 | | } |
3045 | | |
3046 | | /* Handle Vendor Specific elements |
3047 | | * Add all the Vendor Specific elements of the reported link if |
3048 | | * a. There is at least one non-matching Vendor Specific element, or |
3049 | | * b. The number of Vendor Specific elements in reporting and reported |
3050 | | * link is not same. */ |
3051 | | if (add_vendor_ies || |
3052 | | num_own_elem_vendor_ies != num_link_elem_vendor_ies) { |
3053 | | for_each_element(link_elem, link_data, link_data_len) { |
3054 | | link_elem_data = link_elem->data; |
3055 | | link_ele_len = link_elem->datalen; |
3056 | | |
3057 | | if (link_elem->id != WLAN_EID_VENDOR_SPECIFIC) |
3058 | | continue; |
3059 | | |
3060 | | sta_profile_len += link_ele_len + extra_len; |
3061 | | if (sta_profile) { |
3062 | | os_memcpy(sta_profile, |
3063 | | link_elem_data - extra_len, |
3064 | | link_ele_len + extra_len); |
3065 | | sta_profile += link_ele_len + extra_len; |
3066 | | } |
3067 | | } |
3068 | | } |
3069 | | |
3070 | | /* Handle non-inheritance |
3071 | | * Non-Inheritance element: |
3072 | | * Element ID Ext: 1 octet |
3073 | | * Length: 1 octet |
3074 | | * Ext tag number: 1 octet |
3075 | | * Length of Elements ID list: 1 octet |
3076 | | * Elements ID list: variable |
3077 | | * Length of Elements ID Extension list: 1 octet |
3078 | | * Elements ID extensions list: variable |
3079 | | */ |
3080 | | if (non_inherit_ele_list_len || non_inherit_ele_ext_list_len) |
3081 | | sta_profile_len += 3 + 2 + non_inherit_ele_list_len + |
3082 | | non_inherit_ele_ext_list_len; |
3083 | | |
3084 | | if (sta_profile && |
3085 | | (non_inherit_ele_list_len || non_inherit_ele_ext_list_len)) { |
3086 | | *sta_profile++ = WLAN_EID_EXTENSION; |
3087 | | *sta_profile++ = non_inherit_ele_list_len + |
3088 | | non_inherit_ele_ext_list_len + 3; |
3089 | | *sta_profile++ = WLAN_EID_EXT_NON_INHERITANCE; |
3090 | | *sta_profile++ = non_inherit_ele_list_len; |
3091 | | os_memcpy(sta_profile, non_inherit_ele_list, |
3092 | | non_inherit_ele_list_len); |
3093 | | sta_profile += non_inherit_ele_list_len; |
3094 | | *sta_profile++ = non_inherit_ele_ext_list_len; |
3095 | | os_memcpy(sta_profile, non_inherit_ele_ext_list, |
3096 | | non_inherit_ele_ext_list_len); |
3097 | | sta_profile += non_inherit_ele_ext_list_len; |
3098 | | } |
3099 | | |
3100 | | return sta_profile_len; |
3101 | | } |
3102 | | |
3103 | | |
3104 | | static u8 * hostapd_gen_sta_profile(struct ieee80211_mgmt *link_data, |
3105 | | size_t link_data_len, |
3106 | | struct ieee80211_mgmt *own_data, |
3107 | | size_t own_data_len, |
3108 | | size_t *sta_profile_len, bool tx_vap) |
3109 | | { |
3110 | | u8 *sta_profile; |
3111 | | |
3112 | | /* Get the length first */ |
3113 | | *sta_profile_len = hostapd_add_sta_profile(link_data, link_data_len, |
3114 | | own_data, own_data_len, |
3115 | | NULL, tx_vap); |
3116 | | if (!(*sta_profile_len) || *sta_profile_len > EHT_ML_MAX_STA_PROF_LEN) |
3117 | | return NULL; |
3118 | | |
3119 | | sta_profile = os_zalloc(*sta_profile_len); |
3120 | | if (!sta_profile) |
3121 | | return NULL; |
3122 | | |
3123 | | /* Now fill in the data */ |
3124 | | hostapd_add_sta_profile(link_data, link_data_len, own_data, |
3125 | | own_data_len, sta_profile, tx_vap); |
3126 | | |
3127 | | /* The caller takes care of freeing the returned sta_profile */ |
3128 | | return sta_profile; |
3129 | | } |
3130 | | |
3131 | | |
3132 | | static void hostapd_gen_per_sta_profiles(struct hostapd_data *hapd) |
3133 | | { |
3134 | | bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd); |
3135 | | size_t link_data_len, sta_profile_len; |
3136 | | size_t own_data_len, fixed; |
3137 | | struct probe_resp_params link_params; |
3138 | | struct probe_resp_params own_params; |
3139 | | struct ieee80211_mgmt *link_data; |
3140 | | struct ieee80211_mgmt *own_data; |
3141 | | struct mld_link_info *link_info; |
3142 | | struct hostapd_data *link_bss; |
3143 | | u8 link_id, *sta_profile; |
3144 | | |
3145 | | if (!hapd->conf->mld_ap || !hapd->started) |
3146 | | return; |
3147 | | |
3148 | | wpa_printf(MSG_DEBUG, "MLD: Generating per STA profiles for MLD %s", |
3149 | | hapd->conf->iface); |
3150 | | |
3151 | | wpa_printf(MSG_DEBUG, "MLD: Reporting link %d", hapd->mld_link_id); |
3152 | | |
3153 | | /* Generate a Probe Response template for self */ |
3154 | | if (hostapd_get_probe_resp_tmpl(hapd, &own_params, false)) { |
3155 | | wpa_printf(MSG_ERROR, |
3156 | | "MLD: Error in building per STA profiles"); |
3157 | | return; |
3158 | | } |
3159 | | |
3160 | | own_data = own_params.resp; |
3161 | | own_data_len = own_params.resp_len; |
3162 | | |
3163 | | /* Consider the length of the variable fields */ |
3164 | | fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
3165 | | if (own_data_len < fixed) |
3166 | | goto fail; |
3167 | | own_data_len -= fixed; |
3168 | | |
3169 | | for_each_mld_link(link_bss, hapd) { |
3170 | | if (link_bss == hapd || !link_bss->started) |
3171 | | continue; |
3172 | | |
3173 | | link_id = link_bss->mld_link_id; |
3174 | | if (link_id >= MAX_NUM_MLD_LINKS) |
3175 | | continue; |
3176 | | |
3177 | | sta_profile = NULL; |
3178 | | sta_profile_len = 0; |
3179 | | |
3180 | | /* Generate a Probe Response frame template for partner link */ |
3181 | | if (hostapd_get_probe_resp_tmpl(link_bss, &link_params, true)) { |
3182 | | wpa_printf(MSG_ERROR, |
3183 | | "MLD: Could not get link STA probe response template for link %d", |
3184 | | link_id); |
3185 | | continue; |
3186 | | } |
3187 | | |
3188 | | link_data = link_params.resp; |
3189 | | link_data_len = link_params.resp_len; |
3190 | | |
3191 | | /* Consider length of the variable fields */ |
3192 | | fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
3193 | | if (link_data_len < fixed) |
3194 | | continue; |
3195 | | link_data_len -= fixed; |
3196 | | |
3197 | | sta_profile = hostapd_gen_sta_profile(link_data, link_data_len, |
3198 | | own_data, own_data_len, |
3199 | | &sta_profile_len, tx_vap); |
3200 | | if (!sta_profile) { |
3201 | | wpa_printf(MSG_ERROR, |
3202 | | "MLD: Could not generate link STA profile for link %d", |
3203 | | link_id); |
3204 | | continue; |
3205 | | } |
3206 | | |
3207 | | link_info = &hapd->partner_links[link_id]; |
3208 | | link_info->valid = true; |
3209 | | |
3210 | | os_free(link_info->resp_sta_profile); |
3211 | | link_info->resp_sta_profile_len = sta_profile_len; |
3212 | | |
3213 | | link_info->resp_sta_profile = os_memdup(sta_profile, |
3214 | | sta_profile_len); |
3215 | | if (!link_info->resp_sta_profile) |
3216 | | link_info->resp_sta_profile_len = 0; |
3217 | | |
3218 | | os_memcpy(link_info->local_addr, link_bss->own_addr, ETH_ALEN); |
3219 | | |
3220 | | wpa_printf(MSG_DEBUG, |
3221 | | "MLD: Reported link STA info for %d: %u bytes", |
3222 | | link_id, link_info->resp_sta_profile_len); |
3223 | | |
3224 | | os_free(sta_profile); |
3225 | | os_free(link_params.resp); |
3226 | | } |
3227 | | |
3228 | | fail: |
3229 | | os_free(own_params.resp); |
3230 | | } |
3231 | | |
3232 | | #endif /* CONFIG_IEEE80211BE */ |
3233 | | |
3234 | | |
3235 | | int ieee802_11_set_beacon(struct hostapd_data *hapd) |
3236 | 0 | { |
3237 | 0 | struct hostapd_iface *iface = hapd->iface; |
3238 | 0 | int ret; |
3239 | 0 | size_t i, j; |
3240 | 0 | bool is_6g, hapd_mld = false; |
3241 | | #ifdef CONFIG_IEEE80211BE |
3242 | | struct hostapd_data *link_bss; |
3243 | | #endif /* CONFIG_IEEE80211BE */ |
3244 | |
|
3245 | 0 | ret = __ieee802_11_set_beacon(hapd); |
3246 | 0 | if (ret != 0) |
3247 | 0 | return ret; |
3248 | | |
3249 | 0 | if (!iface->interfaces || iface->interfaces->count <= 1) |
3250 | 0 | return 0; |
3251 | | |
3252 | | #ifdef CONFIG_IEEE80211BE |
3253 | | hapd_mld = hapd->conf->mld_ap; |
3254 | | #endif /* CONFIG_IEEE80211BE */ |
3255 | | |
3256 | | /* Update Beacon frames in case of 6 GHz colocation or AP MLD */ |
3257 | 0 | is_6g = is_6ghz_op_class(iface->conf->op_class); |
3258 | 0 | for (j = 0; j < iface->interfaces->count; j++) { |
3259 | 0 | struct hostapd_iface *other; |
3260 | 0 | bool other_iface_6g; |
3261 | |
|
3262 | 0 | other = iface->interfaces->iface[j]; |
3263 | 0 | if (other == iface || !other || !other->conf) |
3264 | 0 | continue; |
3265 | | |
3266 | 0 | other_iface_6g = is_6ghz_op_class(other->conf->op_class); |
3267 | |
|
3268 | 0 | if (is_6g == other_iface_6g && !hapd_mld) |
3269 | 0 | continue; |
3270 | | |
3271 | 0 | for (i = 0; i < other->num_bss; i++) { |
3272 | | #ifdef CONFIG_IEEE80211BE |
3273 | | if (is_6g == other_iface_6g && |
3274 | | !(hapd_mld && other->bss[i]->conf->mld_ap && |
3275 | | hostapd_is_ml_partner(hapd, other->bss[i]))) |
3276 | | continue; |
3277 | | #endif /* CONFIG_IEEE80211BE */ |
3278 | |
|
3279 | 0 | if (other->bss[i] && other->bss[i]->started && |
3280 | 0 | other->bss[i]->beacon_set_done) |
3281 | 0 | __ieee802_11_set_beacon(other->bss[i]); |
3282 | 0 | } |
3283 | 0 | } |
3284 | |
|
3285 | | #ifdef CONFIG_IEEE80211BE |
3286 | | if (!hapd_mld) |
3287 | | return 0; |
3288 | | |
3289 | | /* Generate per STA profiles for each affiliated APs */ |
3290 | | for_each_mld_link(link_bss, hapd) |
3291 | | hostapd_gen_per_sta_profiles(link_bss); |
3292 | | #endif /* CONFIG_IEEE80211BE */ |
3293 | |
|
3294 | 0 | return 0; |
3295 | 0 | } |
3296 | | |
3297 | | |
3298 | | int ieee802_11_set_beacons(struct hostapd_iface *iface) |
3299 | 0 | { |
3300 | 0 | size_t i; |
3301 | 0 | int ret = 0; |
3302 | |
|
3303 | 0 | for (i = 0; i < iface->num_bss; i++) { |
3304 | 0 | if (iface->bss[i]->started && |
3305 | 0 | ieee802_11_set_beacon(iface->bss[i]) < 0) |
3306 | 0 | ret = -1; |
3307 | 0 | } |
3308 | |
|
3309 | 0 | return ret; |
3310 | 0 | } |
3311 | | |
3312 | | |
3313 | | /* only update beacons if started */ |
3314 | | int ieee802_11_update_beacons(struct hostapd_iface *iface) |
3315 | 1.92k | { |
3316 | 1.92k | size_t i; |
3317 | 1.92k | int ret = 0; |
3318 | | |
3319 | 1.92k | for (i = 0; i < iface->num_bss; i++) { |
3320 | 0 | if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && |
3321 | 0 | ieee802_11_set_beacon(iface->bss[i]) < 0) |
3322 | 0 | ret = -1; |
3323 | 0 | } |
3324 | | |
3325 | 1.92k | return ret; |
3326 | 1.92k | } |
3327 | | |
3328 | | #endif /* CONFIG_NATIVE_WINDOWS */ |