]>
Commit | Line | Data |
---|---|---|
d4370eac MP |
1 | /* |
2 | * hostapd / IEEE 802.11 Management | |
b6668734 | 3 | * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> |
d4370eac | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
d4370eac MP |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "common/ieee802_11_defs.h" | |
13 | #include "hostapd.h" | |
14 | #include "sta_info.h" | |
15 | #include "ap_config.h" | |
16 | #include "ap_drv_ops.h" | |
39b97072 | 17 | #include "ieee802_11.h" |
d4370eac MP |
18 | |
19 | ||
20 | #ifdef CONFIG_IEEE80211W | |
21 | ||
22 | u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, | |
23 | struct sta_info *sta, u8 *eid) | |
24 | { | |
25 | u8 *pos = eid; | |
26 | u32 timeout, tu; | |
10e694a6 | 27 | struct os_reltime now, passed; |
d4370eac MP |
28 | |
29 | *pos++ = WLAN_EID_TIMEOUT_INTERVAL; | |
30 | *pos++ = 5; | |
31 | *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; | |
10e694a6 JB |
32 | os_get_reltime(&now); |
33 | os_reltime_sub(&now, &sta->sa_query_start, &passed); | |
d4370eac MP |
34 | tu = (passed.sec * 1000000 + passed.usec) / 1024; |
35 | if (hapd->conf->assoc_sa_query_max_timeout > tu) | |
36 | timeout = hapd->conf->assoc_sa_query_max_timeout - tu; | |
37 | else | |
38 | timeout = 0; | |
39 | if (timeout < hapd->conf->assoc_sa_query_max_timeout) | |
40 | timeout++; /* add some extra time for local timers */ | |
41 | WPA_PUT_LE32(pos, timeout); | |
42 | pos += 4; | |
43 | ||
44 | return pos; | |
45 | } | |
46 | ||
47 | ||
48 | /* MLME-SAQuery.request */ | |
49 | void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, | |
50 | const u8 *addr, const u8 *trans_id) | |
51 | { | |
52 | struct ieee80211_mgmt mgmt; | |
53 | u8 *end; | |
54 | ||
55 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " | |
56 | MACSTR, MAC2STR(addr)); | |
57 | wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", | |
58 | trans_id, WLAN_SA_QUERY_TR_ID_LEN); | |
59 | ||
60 | os_memset(&mgmt, 0, sizeof(mgmt)); | |
61 | mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
62 | WLAN_FC_STYPE_ACTION); | |
63 | os_memcpy(mgmt.da, addr, ETH_ALEN); | |
64 | os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); | |
65 | os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); | |
66 | mgmt.u.action.category = WLAN_ACTION_SA_QUERY; | |
67 | mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; | |
68 | os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, | |
69 | WLAN_SA_QUERY_TR_ID_LEN); | |
70 | end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; | |
8cfa3527 | 71 | if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) |
61323e70 | 72 | wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); |
d4370eac MP |
73 | } |
74 | ||
75 | ||
19df9b07 JM |
76 | static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, |
77 | const u8 *sa, const u8 *trans_id) | |
d4370eac MP |
78 | { |
79 | struct sta_info *sta; | |
80 | struct ieee80211_mgmt resp; | |
81 | u8 *end; | |
82 | ||
83 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " | |
84 | MACSTR, MAC2STR(sa)); | |
85 | wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", | |
86 | trans_id, WLAN_SA_QUERY_TR_ID_LEN); | |
87 | ||
88 | sta = ap_get_sta(hapd, sa); | |
89 | if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { | |
90 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " | |
91 | "from unassociated STA " MACSTR, MAC2STR(sa)); | |
92 | return; | |
93 | } | |
94 | ||
95 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " | |
96 | MACSTR, MAC2STR(sa)); | |
97 | ||
98 | os_memset(&resp, 0, sizeof(resp)); | |
99 | resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
100 | WLAN_FC_STYPE_ACTION); | |
101 | os_memcpy(resp.da, sa, ETH_ALEN); | |
102 | os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); | |
103 | os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); | |
104 | resp.u.action.category = WLAN_ACTION_SA_QUERY; | |
105 | resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; | |
106 | os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, | |
107 | WLAN_SA_QUERY_TR_ID_LEN); | |
108 | end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; | |
8cfa3527 | 109 | if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) |
61323e70 | 110 | wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); |
d4370eac MP |
111 | } |
112 | ||
113 | ||
114 | void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, | |
115 | const u8 action_type, const u8 *trans_id) | |
116 | { | |
117 | struct sta_info *sta; | |
118 | int i; | |
119 | ||
120 | if (action_type == WLAN_SA_QUERY_REQUEST) { | |
121 | ieee802_11_send_sa_query_resp(hapd, sa, trans_id); | |
122 | return; | |
123 | } | |
124 | ||
125 | if (action_type != WLAN_SA_QUERY_RESPONSE) { | |
126 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " | |
127 | "Action %d", action_type); | |
128 | return; | |
129 | } | |
130 | ||
131 | wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " | |
132 | MACSTR, MAC2STR(sa)); | |
133 | wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", | |
134 | trans_id, WLAN_SA_QUERY_TR_ID_LEN); | |
135 | ||
136 | /* MLME-SAQuery.confirm */ | |
137 | ||
138 | sta = ap_get_sta(hapd, sa); | |
139 | if (sta == NULL || sta->sa_query_trans_id == NULL) { | |
140 | wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " | |
141 | "pending SA Query request found"); | |
142 | return; | |
143 | } | |
144 | ||
145 | for (i = 0; i < sta->sa_query_count; i++) { | |
146 | if (os_memcmp(sta->sa_query_trans_id + | |
147 | i * WLAN_SA_QUERY_TR_ID_LEN, | |
148 | trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) | |
149 | break; | |
150 | } | |
151 | ||
152 | if (i >= sta->sa_query_count) { | |
153 | wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " | |
154 | "transaction identifier found"); | |
155 | return; | |
156 | } | |
157 | ||
158 | hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, | |
159 | HOSTAPD_LEVEL_DEBUG, | |
160 | "Reply to pending SA Query received"); | |
161 | ap_sta_stop_sa_query(hapd, sta); | |
162 | } | |
163 | ||
164 | #endif /* CONFIG_IEEE80211W */ | |
06c4d247 JM |
165 | |
166 | ||
8cd6b7bc JB |
167 | static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) |
168 | { | |
169 | *pos = 0x00; | |
170 | ||
171 | switch (idx) { | |
172 | case 0: /* Bits 0-7 */ | |
db63757d PS |
173 | if (hapd->iconf->obss_interval) |
174 | *pos |= 0x01; /* Bit 0 - Coexistence management */ | |
6315bfdb AO |
175 | if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) |
176 | *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ | |
8cd6b7bc JB |
177 | break; |
178 | case 1: /* Bits 8-15 */ | |
7d597d46 KP |
179 | if (hapd->conf->proxy_arp) |
180 | *pos |= 0x10; /* Bit 12 - Proxy ARP */ | |
8cd6b7bc JB |
181 | break; |
182 | case 2: /* Bits 16-23 */ | |
183 | if (hapd->conf->wnm_sleep_mode) | |
184 | *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ | |
185 | if (hapd->conf->bss_transition) | |
186 | *pos |= 0x08; /* Bit 19 - BSS Transition */ | |
187 | break; | |
188 | case 3: /* Bits 24-31 */ | |
189 | #ifdef CONFIG_WNM | |
190 | *pos |= 0x02; /* Bit 25 - SSID List */ | |
191 | #endif /* CONFIG_WNM */ | |
192 | if (hapd->conf->time_advertisement == 2) | |
193 | *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ | |
194 | if (hapd->conf->interworking) | |
195 | *pos |= 0x80; /* Bit 31 - Interworking */ | |
196 | break; | |
197 | case 4: /* Bits 32-39 */ | |
c551700f KP |
198 | if (hapd->conf->qos_map_set_len) |
199 | *pos |= 0x01; /* Bit 32 - QoS Map */ | |
8cd6b7bc JB |
200 | if (hapd->conf->tdls & TDLS_PROHIBIT) |
201 | *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ | |
202 | if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { | |
203 | /* Bit 39 - TDLS Channel Switching Prohibited */ | |
204 | *pos |= 0x80; | |
205 | } | |
206 | break; | |
207 | case 5: /* Bits 40-47 */ | |
3fb17a95 JM |
208 | #ifdef CONFIG_HS20 |
209 | if (hapd->conf->hs20) | |
210 | *pos |= 0x40; /* Bit 46 - WNM-Notification */ | |
211 | #endif /* CONFIG_HS20 */ | |
e5783434 JM |
212 | #ifdef CONFIG_MBO |
213 | if (hapd->conf->mbo_enabled) | |
214 | *pos |= 0x40; /* Bit 46 - WNM-Notification */ | |
215 | #endif /* CONFIG_MBO */ | |
8cd6b7bc JB |
216 | break; |
217 | case 6: /* Bits 48-55 */ | |
218 | if (hapd->conf->ssid.utf8_ssid) | |
219 | *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ | |
220 | break; | |
f55acd90 JM |
221 | case 7: /* Bits 56-63 */ |
222 | break; | |
faecb392 LD |
223 | case 8: /* Bits 64-71 */ |
224 | if (hapd->conf->ftm_responder) | |
225 | *pos |= 0x40; /* Bit 70 - FTM responder */ | |
226 | if (hapd->conf->ftm_initiator) | |
227 | *pos |= 0x80; /* Bit 71 - FTM initiator */ | |
f55acd90 JM |
228 | case 9: /* Bits 72-79 */ |
229 | #ifdef CONFIG_FILS | |
230 | if ((hapd->conf->wpa & WPA_PROTO_RSN) && | |
231 | wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) | |
232 | *pos |= 0x01; | |
233 | #endif /* CONFIG_FILS */ | |
faecb392 | 234 | break; |
8cd6b7bc JB |
235 | } |
236 | } | |
237 | ||
238 | ||
06c4d247 JM |
239 | u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) |
240 | { | |
241 | u8 *pos = eid; | |
8cd6b7bc | 242 | u8 len = 0, i; |
06c4d247 JM |
243 | |
244 | if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) | |
245 | len = 5; | |
246 | if (len < 4 && hapd->conf->interworking) | |
247 | len = 4; | |
c79938a5 JM |
248 | if (len < 3 && hapd->conf->wnm_sleep_mode) |
249 | len = 3; | |
db63757d PS |
250 | if (len < 1 && hapd->iconf->obss_interval) |
251 | len = 1; | |
b93c8509 JM |
252 | if (len < 7 && hapd->conf->ssid.utf8_ssid) |
253 | len = 7; | |
faecb392 LD |
254 | if (len < 9 && |
255 | (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) | |
256 | len = 9; | |
0a66ce3c JM |
257 | #ifdef CONFIG_WNM |
258 | if (len < 4) | |
259 | len = 4; | |
260 | #endif /* CONFIG_WNM */ | |
3fb17a95 JM |
261 | #ifdef CONFIG_HS20 |
262 | if (hapd->conf->hs20 && len < 6) | |
263 | len = 6; | |
264 | #endif /* CONFIG_HS20 */ | |
e5783434 JM |
265 | #ifdef CONFIG_MBO |
266 | if (hapd->conf->mbo_enabled && len < 6) | |
267 | len = 6; | |
268 | #endif /* CONFIG_MBO */ | |
f55acd90 JM |
269 | #ifdef CONFIG_FILS |
270 | if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || | |
271 | !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) | |
272 | len = 10; | |
273 | #endif /* CONFIG_FILS */ | |
8cd6b7bc JB |
274 | if (len < hapd->iface->extended_capa_len) |
275 | len = hapd->iface->extended_capa_len; | |
06c4d247 JM |
276 | if (len == 0) |
277 | return eid; | |
278 | ||
279 | *pos++ = WLAN_EID_EXT_CAPAB; | |
280 | *pos++ = len; | |
8cd6b7bc JB |
281 | for (i = 0; i < len; i++, pos++) { |
282 | hostapd_ext_capab_byte(hapd, pos, i); | |
06c4d247 | 283 | |
8cd6b7bc JB |
284 | if (i < hapd->iface->extended_capa_len) { |
285 | *pos &= ~hapd->iface->extended_capa_mask[i]; | |
286 | *pos |= hapd->iface->extended_capa[i]; | |
287 | } | |
288 | } | |
b93c8509 | 289 | |
3db5439a JM |
290 | while (len > 0 && eid[1 + len] == 0) { |
291 | len--; | |
292 | eid[1] = len; | |
293 | } | |
294 | if (len == 0) | |
295 | return eid; | |
296 | ||
297 | return eid + 2 + len; | |
06c4d247 JM |
298 | } |
299 | ||
300 | ||
c551700f KP |
301 | u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) |
302 | { | |
303 | u8 *pos = eid; | |
304 | u8 len = hapd->conf->qos_map_set_len; | |
305 | ||
306 | if (!len) | |
307 | return eid; | |
308 | ||
309 | *pos++ = WLAN_EID_QOS_MAP_SET; | |
310 | *pos++ = len; | |
311 | os_memcpy(pos, hapd->conf->qos_map_set, len); | |
312 | pos += len; | |
313 | ||
314 | return pos; | |
315 | } | |
316 | ||
317 | ||
06c4d247 JM |
318 | u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) |
319 | { | |
320 | u8 *pos = eid; | |
321 | #ifdef CONFIG_INTERWORKING | |
322 | u8 *len; | |
323 | ||
324 | if (!hapd->conf->interworking) | |
325 | return eid; | |
326 | ||
327 | *pos++ = WLAN_EID_INTERWORKING; | |
328 | len = pos++; | |
329 | ||
330 | *pos = hapd->conf->access_network_type; | |
331 | if (hapd->conf->internet) | |
332 | *pos |= INTERWORKING_ANO_INTERNET; | |
333 | if (hapd->conf->asra) | |
334 | *pos |= INTERWORKING_ANO_ASRA; | |
335 | if (hapd->conf->esr) | |
336 | *pos |= INTERWORKING_ANO_ESR; | |
337 | if (hapd->conf->uesa) | |
338 | *pos |= INTERWORKING_ANO_UESA; | |
339 | pos++; | |
340 | ||
341 | if (hapd->conf->venue_info_set) { | |
342 | *pos++ = hapd->conf->venue_group; | |
343 | *pos++ = hapd->conf->venue_type; | |
344 | } | |
345 | ||
346 | if (!is_zero_ether_addr(hapd->conf->hessid)) { | |
347 | os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); | |
348 | pos += ETH_ALEN; | |
349 | } | |
350 | ||
351 | *len = pos - len - 1; | |
352 | #endif /* CONFIG_INTERWORKING */ | |
353 | ||
354 | return pos; | |
355 | } | |
c7c178e1 JM |
356 | |
357 | ||
358 | u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) | |
359 | { | |
360 | u8 *pos = eid; | |
361 | #ifdef CONFIG_INTERWORKING | |
362 | ||
363 | /* TODO: Separate configuration for ANQP? */ | |
364 | if (!hapd->conf->interworking) | |
365 | return eid; | |
366 | ||
367 | *pos++ = WLAN_EID_ADV_PROTO; | |
368 | *pos++ = 2; | |
1d21e9dd | 369 | *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ |
c7c178e1 JM |
370 | *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; |
371 | #endif /* CONFIG_INTERWORKING */ | |
372 | ||
373 | return pos; | |
374 | } | |
4b2a77ab JM |
375 | |
376 | ||
377 | u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) | |
378 | { | |
379 | u8 *pos = eid; | |
380 | #ifdef CONFIG_INTERWORKING | |
381 | u8 *len; | |
382 | unsigned int i, count; | |
383 | ||
384 | if (!hapd->conf->interworking || | |
385 | hapd->conf->roaming_consortium == NULL || | |
386 | hapd->conf->roaming_consortium_count == 0) | |
387 | return eid; | |
388 | ||
389 | *pos++ = WLAN_EID_ROAMING_CONSORTIUM; | |
390 | len = pos++; | |
391 | ||
392 | /* Number of ANQP OIs (in addition to the max 3 listed here) */ | |
393 | if (hapd->conf->roaming_consortium_count > 3 + 255) | |
394 | *pos++ = 255; | |
395 | else if (hapd->conf->roaming_consortium_count > 3) | |
396 | *pos++ = hapd->conf->roaming_consortium_count - 3; | |
397 | else | |
398 | *pos++ = 0; | |
399 | ||
400 | /* OU #1 and #2 Lengths */ | |
401 | *pos = hapd->conf->roaming_consortium[0].len; | |
402 | if (hapd->conf->roaming_consortium_count > 1) | |
403 | *pos |= hapd->conf->roaming_consortium[1].len << 4; | |
404 | pos++; | |
405 | ||
406 | if (hapd->conf->roaming_consortium_count > 3) | |
407 | count = 3; | |
408 | else | |
409 | count = hapd->conf->roaming_consortium_count; | |
410 | ||
411 | for (i = 0; i < count; i++) { | |
412 | os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, | |
413 | hapd->conf->roaming_consortium[i].len); | |
414 | pos += hapd->conf->roaming_consortium[i].len; | |
415 | } | |
416 | ||
417 | *len = pos - len - 1; | |
418 | #endif /* CONFIG_INTERWORKING */ | |
419 | ||
420 | return pos; | |
421 | } | |
39b97072 JM |
422 | |
423 | ||
424 | u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) | |
425 | { | |
426 | if (hapd->conf->time_advertisement != 2) | |
427 | return eid; | |
428 | ||
429 | if (hapd->time_adv == NULL && | |
430 | hostapd_update_time_adv(hapd) < 0) | |
431 | return eid; | |
432 | ||
4c8a333b JM |
433 | if (hapd->time_adv == NULL) |
434 | return eid; | |
435 | ||
39b97072 JM |
436 | os_memcpy(eid, wpabuf_head(hapd->time_adv), |
437 | wpabuf_len(hapd->time_adv)); | |
438 | eid += wpabuf_len(hapd->time_adv); | |
439 | ||
440 | return eid; | |
441 | } | |
442 | ||
443 | ||
444 | u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) | |
445 | { | |
446 | size_t len; | |
447 | ||
448 | if (hapd->conf->time_advertisement != 2) | |
449 | return eid; | |
450 | ||
451 | len = os_strlen(hapd->conf->time_zone); | |
452 | ||
453 | *eid++ = WLAN_EID_TIME_ZONE; | |
454 | *eid++ = len; | |
455 | os_memcpy(eid, hapd->conf->time_zone, len); | |
456 | eid += len; | |
457 | ||
458 | return eid; | |
459 | } | |
460 | ||
461 | ||
462 | int hostapd_update_time_adv(struct hostapd_data *hapd) | |
463 | { | |
464 | const int elen = 2 + 1 + 10 + 5 + 1; | |
465 | struct os_time t; | |
466 | struct os_tm tm; | |
467 | u8 *pos; | |
468 | ||
469 | if (hapd->conf->time_advertisement != 2) | |
470 | return 0; | |
471 | ||
472 | if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) | |
473 | return -1; | |
474 | ||
475 | if (!hapd->time_adv) { | |
476 | hapd->time_adv = wpabuf_alloc(elen); | |
477 | if (hapd->time_adv == NULL) | |
478 | return -1; | |
479 | pos = wpabuf_put(hapd->time_adv, elen); | |
480 | } else | |
481 | pos = wpabuf_mhead_u8(hapd->time_adv); | |
482 | ||
483 | *pos++ = WLAN_EID_TIME_ADVERTISEMENT; | |
484 | *pos++ = 1 + 10 + 5 + 1; | |
485 | ||
486 | *pos++ = 2; /* UTC time at which the TSF timer is 0 */ | |
487 | ||
488 | /* Time Value at TSF 0 */ | |
489 | /* FIX: need to calculate this based on the current TSF value */ | |
490 | WPA_PUT_LE16(pos, tm.year); /* Year */ | |
491 | pos += 2; | |
492 | *pos++ = tm.month; /* Month */ | |
493 | *pos++ = tm.day; /* Day of month */ | |
494 | *pos++ = tm.hour; /* Hours */ | |
495 | *pos++ = tm.min; /* Minutes */ | |
496 | *pos++ = tm.sec; /* Seconds */ | |
497 | WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ | |
498 | pos += 2; | |
499 | *pos++ = 0; /* Reserved */ | |
500 | ||
501 | /* Time Error */ | |
502 | /* TODO: fill in an estimate on the error */ | |
503 | *pos++ = 0; | |
504 | *pos++ = 0; | |
505 | *pos++ = 0; | |
506 | *pos++ = 0; | |
507 | *pos++ = 0; | |
508 | ||
509 | *pos++ = hapd->time_update_counter++; | |
510 | ||
511 | return 0; | |
512 | } | |
b6668734 JM |
513 | |
514 | ||
515 | u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) | |
516 | { | |
517 | u8 *pos = eid; | |
518 | ||
519 | #ifdef CONFIG_WNM | |
520 | if (hapd->conf->ap_max_inactivity > 0) { | |
521 | unsigned int val; | |
522 | *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; | |
523 | *pos++ = 3; | |
524 | val = hapd->conf->ap_max_inactivity; | |
525 | if (val > 68000) | |
526 | val = 68000; | |
527 | val *= 1000; | |
528 | val /= 1024; | |
529 | if (val == 0) | |
530 | val = 1; | |
531 | if (val > 65535) | |
532 | val = 65535; | |
533 | WPA_PUT_LE16(pos, val); | |
534 | pos += 2; | |
535 | *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ | |
536 | } | |
537 | #endif /* CONFIG_WNM */ | |
538 | ||
539 | return pos; | |
540 | } | |
fb9a1c3e AS |
541 | |
542 | ||
543 | #ifdef CONFIG_MBO | |
544 | ||
545 | u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) | |
546 | { | |
65833d71 | 547 | u8 mbo[9], *mbo_pos = mbo; |
fb9a1c3e AS |
548 | u8 *pos = eid; |
549 | ||
65833d71 | 550 | if (!hapd->conf->mbo_enabled && !hapd->enable_oce) |
fb9a1c3e AS |
551 | return eid; |
552 | ||
65833d71 AP |
553 | if (hapd->conf->mbo_enabled) { |
554 | *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; | |
555 | *mbo_pos++ = 1; | |
556 | /* Not Cellular aware */ | |
557 | *mbo_pos++ = 0; | |
558 | } | |
fb9a1c3e | 559 | |
65833d71 | 560 | if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { |
fb9a1c3e AS |
561 | *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; |
562 | *mbo_pos++ = 1; | |
563 | *mbo_pos++ = hapd->mbo_assoc_disallow; | |
564 | } | |
565 | ||
65833d71 AP |
566 | if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) { |
567 | u8 ctrl; | |
568 | ||
569 | ctrl = OCE_RELEASE; | |
570 | if ((hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) == | |
571 | OCE_STA_CFON) | |
572 | ctrl |= OCE_IS_STA_CFON; | |
573 | ||
574 | *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; | |
575 | *mbo_pos++ = 1; | |
576 | *mbo_pos++ = ctrl; | |
577 | } | |
578 | ||
fb9a1c3e AS |
579 | pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); |
580 | ||
581 | return pos; | |
582 | } | |
583 | ||
584 | ||
585 | u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) | |
586 | { | |
65833d71 AP |
587 | u8 len; |
588 | ||
589 | if (!hapd->conf->mbo_enabled && !hapd->enable_oce) | |
fb9a1c3e AS |
590 | return 0; |
591 | ||
592 | /* | |
593 | * MBO IE header (6) + Capability Indication attribute (3) + | |
594 | * Association Disallowed attribute (3) = 12 | |
595 | */ | |
65833d71 AP |
596 | len = 6; |
597 | if (hapd->conf->mbo_enabled) | |
598 | len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); | |
599 | ||
600 | /* OCE capability indication attribute (3) */ | |
601 | if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) | |
602 | len += 3; | |
603 | ||
604 | return len; | |
fb9a1c3e AS |
605 | } |
606 | ||
607 | #endif /* CONFIG_MBO */ | |
adf0478e JM |
608 | |
609 | ||
610 | void ap_copy_sta_supp_op_classes(struct sta_info *sta, | |
611 | const u8 *supp_op_classes, | |
612 | size_t supp_op_classes_len) | |
613 | { | |
614 | if (!supp_op_classes) | |
615 | return; | |
616 | os_free(sta->supp_op_classes); | |
617 | sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); | |
618 | if (!sta->supp_op_classes) | |
619 | return; | |
620 | ||
621 | sta->supp_op_classes[0] = supp_op_classes_len; | |
622 | os_memcpy(sta->supp_op_classes + 1, supp_op_classes, | |
623 | supp_op_classes_len); | |
624 | } | |
198a942c JM |
625 | |
626 | ||
627 | u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) | |
628 | { | |
629 | u8 *pos = eid; | |
630 | #ifdef CONFIG_FILS | |
631 | u8 *len; | |
632 | u16 fils_info = 0; | |
26bf70e3 JM |
633 | size_t realms; |
634 | struct fils_realm *realm; | |
198a942c JM |
635 | |
636 | if (!(hapd->conf->wpa & WPA_PROTO_RSN) || | |
637 | !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) | |
638 | return pos; | |
639 | ||
26bf70e3 JM |
640 | realms = dl_list_len(&hapd->conf->fils_realms); |
641 | if (realms > 7) | |
642 | realms = 7; /* 3 bit count field limits this to max 7 */ | |
643 | ||
198a942c JM |
644 | *pos++ = WLAN_EID_FILS_INDICATION; |
645 | len = pos++; | |
646 | /* TODO: B0..B2: Number of Public Key Identifiers */ | |
94f66e8a | 647 | if (hapd->conf->erp_domain) { |
94f66e8a | 648 | /* B3..B5: Number of Realm Identifiers */ |
26bf70e3 | 649 | fils_info |= realms << 3; |
94f66e8a | 650 | } |
198a942c JM |
651 | /* TODO: B6: FILS IP Address Configuration */ |
652 | if (hapd->conf->fils_cache_id_set) | |
653 | fils_info |= BIT(7); | |
654 | if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) | |
655 | fils_info |= BIT(8); /* HESSID Included */ | |
656 | /* FILS Shared Key Authentication without PFS Supported */ | |
657 | fils_info |= BIT(9); | |
1764559e JM |
658 | if (hapd->conf->fils_dh_group) { |
659 | /* FILS Shared Key Authentication with PFS Supported */ | |
660 | fils_info |= BIT(10); | |
661 | } | |
198a942c JM |
662 | /* TODO: B11: FILS Public Key Authentication Supported */ |
663 | /* B12..B15: Reserved */ | |
664 | WPA_PUT_LE16(pos, fils_info); | |
665 | pos += 2; | |
666 | if (hapd->conf->fils_cache_id_set) { | |
667 | os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); | |
668 | pos += FILS_CACHE_ID_LEN; | |
669 | } | |
670 | if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { | |
671 | os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); | |
672 | pos += ETH_ALEN; | |
673 | } | |
26bf70e3 JM |
674 | |
675 | dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, | |
676 | list) { | |
677 | if (realms == 0) | |
678 | break; | |
679 | realms--; | |
680 | os_memcpy(pos, realm->hash, 2); | |
94f66e8a JM |
681 | pos += 2; |
682 | } | |
198a942c JM |
683 | *len = pos - len - 1; |
684 | #endif /* CONFIG_FILS */ | |
685 | ||
686 | return pos; | |
687 | } |