]>
Commit | Line | Data |
---|---|---|
75cad1a0 XC |
1 | /* |
2 | * wpa_supplicant - WNM | |
ae8535b6 | 3 | * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. |
75cad1a0 XC |
4 | * |
5 | * This software may be distributed under the terms of the BSD license. | |
6 | * See README for more details. | |
7 | */ | |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "common/ieee802_11_defs.h" | |
ae8535b6 | 13 | #include "common/wpa_ctrl.h" |
75cad1a0 | 14 | #include "rsn_supp/wpa.h" |
61c54976 JM |
15 | #include "wpa_supplicant_i.h" |
16 | #include "driver_i.h" | |
2049a875 | 17 | #include "scan.h" |
e27d20bb VK |
18 | #include "ctrl_iface.h" |
19 | #include "bss.h" | |
20 | #include "wnm_sta.h" | |
95a3ea94 | 21 | #include "hs20_supplicant.h" |
75cad1a0 XC |
22 | |
23 | #define MAX_TFS_IE_LEN 1024 | |
e27d20bb | 24 | #define WNM_MAX_NEIGHBOR_REPORT 10 |
75cad1a0 | 25 | |
75cad1a0 XC |
26 | |
27 | /* get the TFS IE from driver */ | |
28 | static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, | |
29 | u16 *buf_len, enum wnm_oper oper) | |
30 | { | |
31 | wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); | |
32 | ||
33 | return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); | |
34 | } | |
35 | ||
36 | ||
37 | /* set the TFS IE to driver */ | |
38 | static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, | |
39 | const u8 *addr, u8 *buf, u16 *buf_len, | |
40 | enum wnm_oper oper) | |
41 | { | |
42 | wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); | |
43 | ||
44 | return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); | |
45 | } | |
46 | ||
47 | ||
48 | /* MLME-SLEEPMODE.request */ | |
49 | int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, | |
cd0ef657 | 50 | u8 action, u16 intval, struct wpabuf *tfs_req) |
75cad1a0 XC |
51 | { |
52 | struct ieee80211_mgmt *mgmt; | |
53 | int res; | |
54 | size_t len; | |
55 | struct wnm_sleep_element *wnmsleep_ie; | |
56 | u8 *wnmtfs_ie; | |
57 | u8 wnmsleep_ie_len; | |
58 | u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ | |
59 | enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : | |
60 | WNM_SLEEP_TFS_REQ_IE_NONE; | |
61 | ||
cd0ef657 JM |
62 | wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " |
63 | "action=%s to " MACSTR, | |
64 | action == 0 ? "enter" : "exit", | |
65 | MAC2STR(wpa_s->bssid)); | |
66 | ||
75cad1a0 XC |
67 | /* WNM-Sleep Mode IE */ |
68 | wnmsleep_ie_len = sizeof(struct wnm_sleep_element); | |
69 | wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); | |
70 | if (wnmsleep_ie == NULL) | |
71 | return -1; | |
72 | wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; | |
73 | wnmsleep_ie->len = wnmsleep_ie_len - 2; | |
74 | wnmsleep_ie->action_type = action; | |
75 | wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; | |
e9199e31 | 76 | wnmsleep_ie->intval = host_to_le16(intval); |
cd0ef657 JM |
77 | wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", |
78 | (u8 *) wnmsleep_ie, wnmsleep_ie_len); | |
75cad1a0 XC |
79 | |
80 | /* TFS IE(s) */ | |
cd0ef657 JM |
81 | if (tfs_req) { |
82 | wnmtfs_ie_len = wpabuf_len(tfs_req); | |
83 | wnmtfs_ie = os_malloc(wnmtfs_ie_len); | |
84 | if (wnmtfs_ie == NULL) { | |
85 | os_free(wnmsleep_ie); | |
86 | return -1; | |
87 | } | |
88 | os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); | |
89 | } else { | |
90 | wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); | |
91 | if (wnmtfs_ie == NULL) { | |
92 | os_free(wnmsleep_ie); | |
93 | return -1; | |
94 | } | |
95 | if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, | |
96 | tfs_oper)) { | |
97 | wnmtfs_ie_len = 0; | |
98 | os_free(wnmtfs_ie); | |
99 | wnmtfs_ie = NULL; | |
100 | } | |
75cad1a0 | 101 | } |
cd0ef657 JM |
102 | wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", |
103 | (u8 *) wnmtfs_ie, wnmtfs_ie_len); | |
75cad1a0 XC |
104 | |
105 | mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); | |
106 | if (mgmt == NULL) { | |
107 | wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " | |
108 | "WNM-Sleep Request action frame"); | |
14df897c JM |
109 | os_free(wnmsleep_ie); |
110 | os_free(wnmtfs_ie); | |
75cad1a0 XC |
111 | return -1; |
112 | } | |
113 | ||
114 | os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); | |
115 | os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); | |
116 | os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); | |
117 | mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
118 | WLAN_FC_STYPE_ACTION); | |
119 | mgmt->u.action.category = WLAN_ACTION_WNM; | |
120 | mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; | |
e0c54a15 | 121 | mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; |
75cad1a0 XC |
122 | os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, |
123 | wnmsleep_ie_len); | |
124 | /* copy TFS IE here */ | |
125 | if (wnmtfs_ie_len > 0) { | |
126 | os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + | |
127 | wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); | |
128 | } | |
129 | ||
130 | len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + | |
131 | wnmtfs_ie_len; | |
132 | ||
133 | res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, | |
134 | wpa_s->own_addr, wpa_s->bssid, | |
135 | &mgmt->u.action.category, len, 0); | |
136 | if (res < 0) | |
137 | wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " | |
138 | "(action=%d, intval=%d)", action, intval); | |
139 | ||
140 | os_free(wnmsleep_ie); | |
141 | os_free(wnmtfs_ie); | |
142 | os_free(mgmt); | |
143 | ||
144 | return res; | |
145 | } | |
146 | ||
147 | ||
74b4a360 JM |
148 | static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, |
149 | u8 *tfsresp_ie_start, | |
150 | u8 *tfsresp_ie_end) | |
151 | { | |
152 | wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, | |
153 | wpa_s->bssid, NULL, NULL); | |
154 | /* remove GTK/IGTK ?? */ | |
155 | ||
156 | /* set the TFS Resp IE(s) */ | |
157 | if (tfsresp_ie_start && tfsresp_ie_end && | |
158 | tfsresp_ie_end - tfsresp_ie_start >= 0) { | |
159 | u16 tfsresp_ie_len; | |
160 | tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - | |
161 | tfsresp_ie_start; | |
162 | wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); | |
163 | /* pass the TFS Resp IE(s) to driver for processing */ | |
164 | if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, | |
165 | tfsresp_ie_start, | |
166 | &tfsresp_ie_len, | |
167 | WNM_SLEEP_TFS_RESP_IE_SET)) | |
168 | wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); | |
169 | } | |
170 | } | |
171 | ||
172 | ||
173 | static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, | |
174 | const u8 *frm, u16 key_len_total) | |
175 | { | |
176 | u8 *ptr, *end; | |
177 | u8 gtk_len; | |
178 | ||
179 | wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, | |
180 | NULL, NULL); | |
181 | ||
182 | /* Install GTK/IGTK */ | |
183 | ||
184 | /* point to key data field */ | |
dbfb8e82 | 185 | ptr = (u8 *) frm + 1 + 2; |
74b4a360 JM |
186 | end = ptr + key_len_total; |
187 | wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); | |
188 | ||
189 | while (ptr + 1 < end) { | |
190 | if (ptr + 2 + ptr[1] > end) { | |
191 | wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " | |
192 | "length"); | |
193 | if (end > ptr) { | |
194 | wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", | |
195 | ptr, end - ptr); | |
196 | } | |
197 | break; | |
198 | } | |
199 | if (*ptr == WNM_SLEEP_SUBELEM_GTK) { | |
200 | if (ptr[1] < 11 + 5) { | |
201 | wpa_printf(MSG_DEBUG, "WNM: Too short GTK " | |
202 | "subelem"); | |
203 | break; | |
204 | } | |
205 | gtk_len = *(ptr + 4); | |
206 | if (ptr[1] < 11 + gtk_len || | |
207 | gtk_len < 5 || gtk_len > 32) { | |
208 | wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " | |
209 | "subelem"); | |
210 | break; | |
211 | } | |
212 | wpa_wnmsleep_install_key( | |
213 | wpa_s->wpa, | |
214 | WNM_SLEEP_SUBELEM_GTK, | |
215 | ptr); | |
216 | ptr += 13 + gtk_len; | |
217 | #ifdef CONFIG_IEEE80211W | |
218 | } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { | |
219 | if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { | |
220 | wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " | |
221 | "subelem"); | |
222 | break; | |
223 | } | |
224 | wpa_wnmsleep_install_key(wpa_s->wpa, | |
225 | WNM_SLEEP_SUBELEM_IGTK, ptr); | |
226 | ptr += 10 + WPA_IGTK_LEN; | |
227 | #endif /* CONFIG_IEEE80211W */ | |
228 | } else | |
229 | break; /* skip the loop */ | |
230 | } | |
231 | } | |
232 | ||
233 | ||
75cad1a0 XC |
234 | static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, |
235 | const u8 *frm, int len) | |
236 | { | |
237 | /* | |
9a147ba1 | 238 | * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | |
75cad1a0 XC |
239 | * WNM-Sleep Mode IE | TFS Response IE |
240 | */ | |
dbfb8e82 | 241 | u8 *pos = (u8 *) frm; /* point to payload after the action field */ |
9a147ba1 | 242 | u16 key_len_total; |
75cad1a0 XC |
243 | struct wnm_sleep_element *wnmsleep_ie = NULL; |
244 | /* multiple TFS Resp IE (assuming consecutive) */ | |
245 | u8 *tfsresp_ie_start = NULL; | |
246 | u8 *tfsresp_ie_end = NULL; | |
75cad1a0 | 247 | |
9a147ba1 JM |
248 | if (len < 3) |
249 | return; | |
250 | key_len_total = WPA_GET_LE16(frm + 1); | |
251 | ||
dbfb8e82 JM |
252 | wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", |
253 | frm[0], key_len_total); | |
254 | pos += 3 + key_len_total; | |
68db9ab0 JM |
255 | if (pos > frm + len) { |
256 | wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); | |
257 | return; | |
258 | } | |
75cad1a0 XC |
259 | while (pos - frm < len) { |
260 | u8 ie_len = *(pos + 1); | |
68db9ab0 JM |
261 | if (pos + 2 + ie_len > frm + len) { |
262 | wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); | |
263 | break; | |
264 | } | |
265 | wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); | |
75cad1a0 XC |
266 | if (*pos == WLAN_EID_WNMSLEEP) |
267 | wnmsleep_ie = (struct wnm_sleep_element *) pos; | |
268 | else if (*pos == WLAN_EID_TFS_RESP) { | |
269 | if (!tfsresp_ie_start) | |
270 | tfsresp_ie_start = pos; | |
271 | tfsresp_ie_end = pos; | |
272 | } else | |
273 | wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); | |
274 | pos += ie_len + 2; | |
275 | } | |
276 | ||
277 | if (!wnmsleep_ie) { | |
278 | wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); | |
279 | return; | |
280 | } | |
281 | ||
62f6fbb4 JM |
282 | if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || |
283 | wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { | |
75cad1a0 XC |
284 | wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " |
285 | "frame (action=%d, intval=%d)", | |
286 | wnmsleep_ie->action_type, wnmsleep_ie->intval); | |
df80a0cc | 287 | if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { |
74b4a360 JM |
288 | wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, |
289 | tfsresp_ie_end); | |
df80a0cc | 290 | } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { |
74b4a360 | 291 | wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); |
75cad1a0 XC |
292 | } |
293 | } else { | |
294 | wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " | |
295 | "(action=%d, intval=%d)", | |
296 | wnmsleep_ie->action_type, wnmsleep_ie->intval); | |
df80a0cc | 297 | if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) |
75cad1a0 XC |
298 | wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, |
299 | wpa_s->bssid, NULL, NULL); | |
df80a0cc | 300 | else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) |
75cad1a0 XC |
301 | wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, |
302 | wpa_s->bssid, NULL, NULL); | |
303 | } | |
304 | } | |
305 | ||
306 | ||
e27d20bb VK |
307 | void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) |
308 | { | |
309 | int i; | |
310 | ||
311 | for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { | |
e27d20bb | 312 | os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); |
e27d20bb VK |
313 | os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); |
314 | } | |
315 | ||
ec331d09 | 316 | wpa_s->wnm_num_neighbor_report = 0; |
e27d20bb VK |
317 | os_free(wpa_s->wnm_neighbor_report_elements); |
318 | wpa_s->wnm_neighbor_report_elements = NULL; | |
319 | } | |
320 | ||
321 | ||
322 | static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, | |
323 | u8 id, u8 elen, const u8 *pos) | |
324 | { | |
325 | switch (id) { | |
326 | case WNM_NEIGHBOR_TSF: | |
327 | if (elen < 2 + 2) { | |
328 | wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); | |
329 | break; | |
330 | } | |
09322678 JM |
331 | rep->tsf_offset = WPA_GET_LE16(pos); |
332 | rep->beacon_int = WPA_GET_LE16(pos + 2); | |
333 | rep->tsf_present = 1; | |
e27d20bb VK |
334 | break; |
335 | case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: | |
336 | if (elen < 2) { | |
337 | wpa_printf(MSG_DEBUG, "WNM: Too short condensed " | |
338 | "country string"); | |
339 | break; | |
340 | } | |
09322678 JM |
341 | os_memcpy(rep->country, pos, 2); |
342 | rep->country_present = 1; | |
e27d20bb VK |
343 | break; |
344 | case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: | |
345 | if (elen < 1) { | |
346 | wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " | |
347 | "candidate"); | |
348 | break; | |
349 | } | |
09322678 JM |
350 | rep->preference = pos[0]; |
351 | rep->preference_present = 1; | |
e27d20bb VK |
352 | break; |
353 | case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: | |
09322678 JM |
354 | rep->bss_term_tsf = WPA_GET_LE64(pos); |
355 | rep->bss_term_dur = WPA_GET_LE16(pos + 8); | |
356 | rep->bss_term_present = 1; | |
e27d20bb VK |
357 | break; |
358 | case WNM_NEIGHBOR_BEARING: | |
359 | if (elen < 8) { | |
360 | wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " | |
361 | "bearing"); | |
362 | break; | |
363 | } | |
09322678 JM |
364 | rep->bearing = WPA_GET_LE16(pos); |
365 | rep->distance = WPA_GET_LE32(pos + 2); | |
366 | rep->rel_height = WPA_GET_LE16(pos + 2 + 4); | |
367 | rep->bearing_present = 1; | |
e27d20bb VK |
368 | break; |
369 | case WNM_NEIGHBOR_MEASUREMENT_PILOT: | |
f6ce70dc | 370 | if (elen < 1) { |
e27d20bb VK |
371 | wpa_printf(MSG_DEBUG, "WNM: Too short measurement " |
372 | "pilot"); | |
373 | break; | |
374 | } | |
e9cb7b92 | 375 | os_free(rep->meas_pilot); |
e27d20bb VK |
376 | rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); |
377 | if (rep->meas_pilot == NULL) | |
378 | break; | |
e27d20bb | 379 | rep->meas_pilot->measurement_pilot = pos[0]; |
f6ce70dc JM |
380 | rep->meas_pilot->subelem_len = elen - 1; |
381 | os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); | |
e27d20bb VK |
382 | break; |
383 | case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: | |
f6ce70dc | 384 | if (elen < 5) { |
e27d20bb VK |
385 | wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " |
386 | "capabilities"); | |
387 | break; | |
388 | } | |
09322678 JM |
389 | os_memcpy(rep->rm_capab, pos, 5); |
390 | rep->rm_capab_present = 1; | |
e27d20bb VK |
391 | break; |
392 | case WNM_NEIGHBOR_MULTIPLE_BSSID: | |
f6ce70dc | 393 | if (elen < 1) { |
e27d20bb VK |
394 | wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); |
395 | break; | |
396 | } | |
e9cb7b92 | 397 | os_free(rep->mul_bssid); |
e27d20bb VK |
398 | rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); |
399 | if (rep->mul_bssid == NULL) | |
400 | break; | |
e27d20bb | 401 | rep->mul_bssid->max_bssid_indicator = pos[0]; |
f6ce70dc JM |
402 | rep->mul_bssid->subelem_len = elen - 1; |
403 | os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); | |
e27d20bb VK |
404 | break; |
405 | } | |
406 | } | |
407 | ||
408 | ||
409 | static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, | |
410 | const u8 *pos, u8 len, | |
411 | struct neighbor_report *rep) | |
412 | { | |
413 | u8 left = len; | |
414 | ||
415 | if (left < 13) { | |
416 | wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); | |
417 | return; | |
418 | } | |
419 | ||
420 | os_memcpy(rep->bssid, pos, ETH_ALEN); | |
4c381f0d | 421 | rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); |
e27d20bb VK |
422 | rep->regulatory_class = *(pos + 10); |
423 | rep->channel_number = *(pos + 11); | |
424 | rep->phy_type = *(pos + 12); | |
425 | ||
426 | pos += 13; | |
427 | left -= 13; | |
428 | ||
429 | while (left >= 2) { | |
430 | u8 id, elen; | |
431 | ||
432 | id = *pos++; | |
433 | elen = *pos++; | |
1aa6f953 JM |
434 | wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); |
435 | left -= 2; | |
436 | if (elen > left) { | |
437 | wpa_printf(MSG_DEBUG, | |
438 | "WNM: Truncated neighbor report subelement"); | |
439 | break; | |
440 | } | |
e27d20bb | 441 | wnm_parse_neighbor_report_elem(rep, id, elen, pos); |
1aa6f953 | 442 | left -= elen; |
e27d20bb VK |
443 | pos += elen; |
444 | } | |
445 | } | |
446 | ||
447 | ||
448 | static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, | |
449 | struct wpa_scan_results *scan_res, | |
450 | struct neighbor_report *neigh_rep, | |
451 | u8 num_neigh_rep, u8 *bssid_to_connect) | |
452 | { | |
453 | ||
454 | u8 i, j; | |
455 | ||
67adcd26 | 456 | if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss) |
e27d20bb VK |
457 | return 0; |
458 | ||
3c1060ff | 459 | wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", |
67adcd26 | 460 | MAC2STR(wpa_s->bssid), wpa_s->current_bss->level); |
3c1060ff | 461 | |
e27d20bb VK |
462 | for (i = 0; i < num_neigh_rep; i++) { |
463 | for (j = 0; j < scan_res->num; j++) { | |
464 | /* Check for a better RSSI AP */ | |
465 | if (os_memcmp(scan_res->res[j]->bssid, | |
466 | neigh_rep[i].bssid, ETH_ALEN) == 0 && | |
467 | scan_res->res[j]->level > | |
468 | wpa_s->current_bss->level) { | |
469 | /* Got a BSSID with better RSSI value */ | |
470 | os_memcpy(bssid_to_connect, neigh_rep[i].bssid, | |
471 | ETH_ALEN); | |
3c1060ff SD |
472 | wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR |
473 | " with better scan RSSI %d", | |
474 | MAC2STR(scan_res->res[j]->bssid), | |
475 | scan_res->res[j]->level); | |
e27d20bb VK |
476 | return 1; |
477 | } | |
3c1060ff SD |
478 | wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR |
479 | " RSSI %d", j, | |
480 | MAC2STR(scan_res->res[j]->bssid), | |
481 | scan_res->res[j]->level); | |
e27d20bb VK |
482 | } |
483 | } | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | ||
7b53acd3 JM |
489 | static void wnm_send_bss_transition_mgmt_resp( |
490 | struct wpa_supplicant *wpa_s, u8 dialog_token, | |
491 | enum bss_trans_mgmt_status_code status, u8 delay, | |
492 | const u8 *target_bssid) | |
2049a875 JM |
493 | { |
494 | u8 buf[1000], *pos; | |
495 | struct ieee80211_mgmt *mgmt; | |
496 | size_t len; | |
497 | ||
498 | wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " | |
499 | "to " MACSTR " dialog_token=%u status=%u delay=%d", | |
500 | MAC2STR(wpa_s->bssid), dialog_token, status, delay); | |
501 | ||
502 | mgmt = (struct ieee80211_mgmt *) buf; | |
503 | os_memset(&buf, 0, sizeof(buf)); | |
504 | os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); | |
505 | os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); | |
506 | os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); | |
507 | mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
508 | WLAN_FC_STYPE_ACTION); | |
509 | mgmt->u.action.category = WLAN_ACTION_WNM; | |
510 | mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; | |
511 | mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; | |
512 | mgmt->u.action.u.bss_tm_resp.status_code = status; | |
513 | mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; | |
514 | pos = mgmt->u.action.u.bss_tm_resp.variable; | |
515 | if (target_bssid) { | |
516 | os_memcpy(pos, target_bssid, ETH_ALEN); | |
517 | pos += ETH_ALEN; | |
2cd0f6a4 JM |
518 | } else if (status == WNM_BSS_TM_ACCEPT) { |
519 | /* | |
520 | * P802.11-REVmc clarifies that the Target BSSID field is always | |
521 | * present when status code is zero, so use a fake value here if | |
522 | * no BSSID is yet known. | |
523 | */ | |
524 | os_memset(pos, 0, ETH_ALEN); | |
525 | pos += ETH_ALEN; | |
2049a875 JM |
526 | } |
527 | ||
528 | len = pos - (u8 *) &mgmt->u.action.category; | |
529 | ||
530 | wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, | |
531 | wpa_s->own_addr, wpa_s->bssid, | |
532 | &mgmt->u.action.category, len, 0); | |
533 | } | |
534 | ||
535 | ||
e27d20bb VK |
536 | void wnm_scan_response(struct wpa_supplicant *wpa_s, |
537 | struct wpa_scan_results *scan_res) | |
538 | { | |
539 | u8 bssid[ETH_ALEN]; | |
540 | ||
541 | if (scan_res == NULL) { | |
542 | wpa_printf(MSG_ERROR, "Scan result is NULL"); | |
543 | goto send_bss_resp_fail; | |
544 | } | |
545 | ||
546 | /* Compare the Neighbor Report and scan results */ | |
547 | if (compare_scan_neighbor_results(wpa_s, scan_res, | |
548 | wpa_s->wnm_neighbor_report_elements, | |
549 | wpa_s->wnm_num_neighbor_report, | |
550 | bssid) == 1) { | |
551 | /* Associate to the network */ | |
552 | struct wpa_bss *bss; | |
553 | struct wpa_ssid *ssid = wpa_s->current_ssid; | |
554 | ||
555 | bss = wpa_bss_get_bssid(wpa_s, bssid); | |
556 | if (!bss) { | |
557 | wpa_printf(MSG_DEBUG, "WNM: Target AP not found from " | |
558 | "BSS table"); | |
559 | goto send_bss_resp_fail; | |
560 | } | |
561 | ||
562 | /* Send the BSS Management Response - Accept */ | |
563 | if (wpa_s->wnm_reply) { | |
564 | wnm_send_bss_transition_mgmt_resp(wpa_s, | |
565 | wpa_s->wnm_dialog_token, | |
7b53acd3 | 566 | WNM_BSS_TM_ACCEPT, |
2cd0f6a4 | 567 | 0, bssid); |
e27d20bb VK |
568 | } |
569 | ||
570 | wpa_s->reassociate = 1; | |
571 | wpa_supplicant_connect(wpa_s, bss, ssid); | |
572 | wnm_deallocate_memory(wpa_s); | |
573 | return; | |
574 | } | |
575 | ||
576 | /* Send reject response for all the failures */ | |
577 | send_bss_resp_fail: | |
578 | wnm_deallocate_memory(wpa_s); | |
579 | if (wpa_s->wnm_reply) { | |
580 | wnm_send_bss_transition_mgmt_resp(wpa_s, | |
581 | wpa_s->wnm_dialog_token, | |
7b53acd3 | 582 | WNM_BSS_TM_REJECT_UNSPECIFIED, |
e27d20bb VK |
583 | 0, NULL); |
584 | } | |
585 | return; | |
586 | } | |
587 | ||
588 | ||
8040dc53 JM |
589 | static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) |
590 | { | |
591 | unsigned int i; | |
592 | ||
593 | wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); | |
594 | if (!wpa_s->wnm_neighbor_report_elements) | |
595 | return; | |
596 | for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { | |
597 | struct neighbor_report *nei; | |
598 | ||
599 | nei = &wpa_s->wnm_neighbor_report_elements[i]; | |
600 | wpa_printf(MSG_DEBUG, "%u: " MACSTR | |
4c381f0d JM |
601 | " info=0x%x op_class=%u chan=%u phy=%u pref=%d", |
602 | i, MAC2STR(nei->bssid), nei->bssid_info, | |
603 | nei->regulatory_class, | |
8040dc53 | 604 | nei->channel_number, nei->phy_type, |
09322678 | 605 | nei->preference_present ? nei->preference : -1); |
8040dc53 JM |
606 | } |
607 | } | |
608 | ||
609 | ||
2049a875 JM |
610 | static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, |
611 | const u8 *pos, const u8 *end, | |
612 | int reply) | |
613 | { | |
8c9af762 JM |
614 | unsigned int beacon_int; |
615 | u8 valid_int; | |
616 | ||
2049a875 JM |
617 | if (pos + 5 > end) |
618 | return; | |
619 | ||
8c9af762 JM |
620 | if (wpa_s->current_bss) |
621 | beacon_int = wpa_s->current_bss->beacon_int; | |
622 | else | |
623 | beacon_int = 100; /* best guess */ | |
624 | ||
e27d20bb VK |
625 | wpa_s->wnm_dialog_token = pos[0]; |
626 | wpa_s->wnm_mode = pos[1]; | |
627 | wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); | |
8c9af762 | 628 | valid_int = pos[4]; |
e27d20bb | 629 | wpa_s->wnm_reply = reply; |
2049a875 JM |
630 | |
631 | wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " | |
632 | "dialog_token=%u request_mode=0x%x " | |
633 | "disassoc_timer=%u validity_interval=%u", | |
e27d20bb | 634 | wpa_s->wnm_dialog_token, wpa_s->wnm_mode, |
8c9af762 | 635 | wpa_s->wnm_dissoc_timer, valid_int); |
e27d20bb | 636 | |
2049a875 | 637 | pos += 5; |
e27d20bb | 638 | |
7b53acd3 | 639 | if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { |
e27d20bb VK |
640 | if (pos + 12 > end) { |
641 | wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); | |
642 | return; | |
643 | } | |
644 | os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); | |
2049a875 | 645 | pos += 12; /* BSS Termination Duration */ |
e27d20bb VK |
646 | } |
647 | ||
7b53acd3 | 648 | if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { |
2049a875 | 649 | char url[256]; |
ae8535b6 | 650 | |
2049a875 JM |
651 | if (pos + 1 > end || pos + 1 + pos[0] > end) { |
652 | wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " | |
653 | "Management Request (URL)"); | |
654 | return; | |
655 | } | |
656 | os_memcpy(url, pos + 1, pos[0]); | |
657 | url[pos[0]] = '\0'; | |
e27d20bb | 658 | pos += 1 + pos[0]; |
ae8535b6 | 659 | |
ae8535b6 JM |
660 | wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", |
661 | wpa_sm_pmf_enabled(wpa_s->wpa), | |
662 | wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); | |
2049a875 JM |
663 | } |
664 | ||
7b53acd3 | 665 | if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { |
2049a875 | 666 | wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " |
e27d20bb VK |
667 | "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); |
668 | if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { | |
2049a875 JM |
669 | /* TODO: mark current BSS less preferred for |
670 | * selection */ | |
671 | wpa_printf(MSG_DEBUG, "Trying to find another BSS"); | |
672 | wpa_supplicant_req_scan(wpa_s, 0, 0); | |
673 | } | |
674 | } | |
675 | ||
7b53acd3 | 676 | if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { |
8c9af762 JM |
677 | unsigned int valid_ms; |
678 | ||
e27d20bb VK |
679 | wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); |
680 | wpa_s->wnm_num_neighbor_report = 0; | |
681 | os_free(wpa_s->wnm_neighbor_report_elements); | |
682 | wpa_s->wnm_neighbor_report_elements = os_zalloc( | |
683 | WNM_MAX_NEIGHBOR_REPORT * | |
684 | sizeof(struct neighbor_report)); | |
685 | if (wpa_s->wnm_neighbor_report_elements == NULL) | |
686 | return; | |
687 | ||
688 | while (pos + 2 <= end && | |
689 | wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) | |
690 | { | |
691 | u8 tag = *pos++; | |
692 | u8 len = *pos++; | |
693 | ||
694 | wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", | |
695 | tag); | |
696 | if (pos + len > end) { | |
697 | wpa_printf(MSG_DEBUG, "WNM: Truncated request"); | |
698 | return; | |
699 | } | |
1aa6f953 JM |
700 | if (tag == WLAN_EID_NEIGHBOR_REPORT) { |
701 | struct neighbor_report *rep; | |
702 | rep = &wpa_s->wnm_neighbor_report_elements[ | |
703 | wpa_s->wnm_num_neighbor_report]; | |
704 | wnm_parse_neighbor_report(wpa_s, pos, len, rep); | |
705 | } | |
e27d20bb VK |
706 | |
707 | pos += len; | |
708 | wpa_s->wnm_num_neighbor_report++; | |
709 | } | |
8040dc53 | 710 | wnm_dump_cand_list(wpa_s); |
8c9af762 JM |
711 | valid_ms = valid_int * beacon_int * 128 / 125; |
712 | wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", | |
713 | valid_ms); | |
714 | os_get_reltime(&wpa_s->wnm_cand_valid_until); | |
715 | wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; | |
716 | wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; | |
717 | wpa_s->wnm_cand_valid_until.sec += | |
718 | wpa_s->wnm_cand_valid_until.usec / 1000000; | |
719 | wpa_s->wnm_cand_valid_until.usec %= 1000000; | |
e27d20bb VK |
720 | |
721 | wpa_s->scan_res_handler = wnm_scan_response; | |
722 | wpa_supplicant_req_scan(wpa_s, 0, 0); | |
723 | } else if (reply) { | |
6df634fa JM |
724 | enum bss_trans_mgmt_status_code status; |
725 | if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) | |
726 | status = WNM_BSS_TM_ACCEPT; | |
727 | else { | |
728 | wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); | |
729 | status = WNM_BSS_TM_REJECT_UNSPECIFIED; | |
730 | } | |
e27d20bb VK |
731 | wnm_send_bss_transition_mgmt_resp(wpa_s, |
732 | wpa_s->wnm_dialog_token, | |
6df634fa | 733 | status, 0, NULL); |
2049a875 JM |
734 | } |
735 | } | |
736 | ||
737 | ||
65bcd0a9 VK |
738 | int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, |
739 | u8 query_reason) | |
740 | { | |
741 | u8 buf[1000], *pos; | |
742 | struct ieee80211_mgmt *mgmt; | |
743 | size_t len; | |
744 | int ret; | |
745 | ||
746 | wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " | |
747 | MACSTR " query_reason=%u", | |
748 | MAC2STR(wpa_s->bssid), query_reason); | |
749 | ||
750 | mgmt = (struct ieee80211_mgmt *) buf; | |
751 | os_memset(&buf, 0, sizeof(buf)); | |
752 | os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); | |
753 | os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); | |
754 | os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); | |
755 | mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
756 | WLAN_FC_STYPE_ACTION); | |
757 | mgmt->u.action.category = WLAN_ACTION_WNM; | |
758 | mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; | |
a8a6a35f | 759 | mgmt->u.action.u.bss_tm_query.dialog_token = 1; |
65bcd0a9 VK |
760 | mgmt->u.action.u.bss_tm_query.query_reason = query_reason; |
761 | pos = mgmt->u.action.u.bss_tm_query.variable; | |
762 | ||
763 | len = pos - (u8 *) &mgmt->u.action.category; | |
764 | ||
765 | ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, | |
766 | wpa_s->own_addr, wpa_s->bssid, | |
767 | &mgmt->u.action.category, len, 0); | |
768 | ||
769 | return ret; | |
770 | } | |
771 | ||
772 | ||
95a3ea94 JM |
773 | static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, |
774 | const u8 *sa, const u8 *data, | |
775 | int len) | |
776 | { | |
7ef69479 | 777 | const u8 *pos, *end, *next; |
95a3ea94 JM |
778 | u8 ie, ie_len; |
779 | ||
780 | pos = data; | |
781 | end = data + len; | |
782 | ||
783 | while (pos + 1 < end) { | |
784 | ie = *pos++; | |
785 | ie_len = *pos++; | |
786 | wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", | |
787 | ie, ie_len); | |
788 | if (ie_len > end - pos) { | |
789 | wpa_printf(MSG_DEBUG, "WNM: Not enough room for " | |
790 | "subelement"); | |
791 | break; | |
792 | } | |
7ef69479 JM |
793 | next = pos + ie_len; |
794 | if (ie_len < 4) { | |
795 | pos = next; | |
796 | continue; | |
797 | } | |
798 | wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", | |
799 | WPA_GET_BE24(pos), pos[3]); | |
95a3ea94 JM |
800 | |
801 | #ifdef CONFIG_HS20 | |
802 | if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && | |
803 | WPA_GET_BE24(pos) == OUI_WFA && | |
804 | pos[3] == HS20_WNM_SUB_REM_NEEDED) { | |
805 | /* Subscription Remediation subelement */ | |
806 | const u8 *ie_end; | |
807 | u8 url_len; | |
808 | char *url; | |
809 | u8 osu_method; | |
810 | ||
811 | wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " | |
812 | "subelement"); | |
813 | ie_end = pos + ie_len; | |
814 | pos += 4; | |
815 | url_len = *pos++; | |
816 | if (url_len == 0) { | |
817 | wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); | |
818 | url = NULL; | |
819 | osu_method = 1; | |
820 | } else { | |
821 | if (pos + url_len + 1 > ie_end) { | |
822 | wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", | |
823 | url_len, | |
824 | (int) (ie_end - pos)); | |
825 | break; | |
826 | } | |
827 | url = os_malloc(url_len + 1); | |
828 | if (url == NULL) | |
829 | break; | |
830 | os_memcpy(url, pos, url_len); | |
831 | url[url_len] = '\0'; | |
832 | osu_method = pos[url_len]; | |
833 | } | |
834 | hs20_rx_subscription_remediation(wpa_s, url, | |
835 | osu_method); | |
836 | os_free(url); | |
7ef69479 JM |
837 | pos = next; |
838 | continue; | |
839 | } | |
840 | ||
841 | if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && | |
842 | WPA_GET_BE24(pos) == OUI_WFA && | |
843 | pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { | |
844 | const u8 *ie_end; | |
845 | u8 url_len; | |
846 | char *url; | |
847 | u8 code; | |
848 | u16 reauth_delay; | |
849 | ||
850 | ie_end = pos + ie_len; | |
851 | pos += 4; | |
852 | code = *pos++; | |
853 | reauth_delay = WPA_GET_LE16(pos); | |
854 | pos += 2; | |
855 | url_len = *pos++; | |
856 | wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " | |
857 | "Imminent - Reason Code %u " | |
858 | "Re-Auth Delay %u URL Length %u", | |
859 | code, reauth_delay, url_len); | |
860 | if (pos + url_len > ie_end) | |
861 | break; | |
862 | url = os_malloc(url_len + 1); | |
863 | if (url == NULL) | |
864 | break; | |
865 | os_memcpy(url, pos, url_len); | |
866 | url[url_len] = '\0'; | |
867 | hs20_rx_deauth_imminent_notice(wpa_s, code, | |
868 | reauth_delay, url); | |
869 | os_free(url); | |
870 | pos = next; | |
871 | continue; | |
95a3ea94 JM |
872 | } |
873 | #endif /* CONFIG_HS20 */ | |
874 | ||
7ef69479 | 875 | pos = next; |
95a3ea94 JM |
876 | } |
877 | } | |
878 | ||
879 | ||
880 | static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, | |
881 | const u8 *sa, const u8 *frm, int len) | |
882 | { | |
883 | const u8 *pos, *end; | |
884 | u8 dialog_token, type; | |
885 | ||
886 | /* Dialog Token [1] | Type [1] | Subelements */ | |
887 | ||
888 | if (len < 2 || sa == NULL) | |
889 | return; | |
890 | end = frm + len; | |
891 | pos = frm; | |
892 | dialog_token = *pos++; | |
893 | type = *pos++; | |
894 | ||
895 | wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " | |
896 | "(dialog_token %u type %u sa " MACSTR ")", | |
897 | dialog_token, type, MAC2STR(sa)); | |
898 | wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", | |
899 | pos, end - pos); | |
900 | ||
901 | if (wpa_s->wpa_state != WPA_COMPLETED || | |
902 | os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { | |
903 | wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " | |
904 | "from our AP - ignore it"); | |
905 | return; | |
906 | } | |
907 | ||
908 | switch (type) { | |
909 | case 1: | |
910 | ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); | |
911 | break; | |
912 | default: | |
913 | wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " | |
914 | "WNM-Notification type %u", type); | |
915 | break; | |
916 | } | |
917 | } | |
918 | ||
919 | ||
75cad1a0 | 920 | void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, |
dbfb8e82 | 921 | const struct ieee80211_mgmt *mgmt, size_t len) |
75cad1a0 | 922 | { |
27c77751 | 923 | const u8 *pos, *end; |
2049a875 | 924 | u8 act; |
27c77751 | 925 | |
dbfb8e82 | 926 | if (len < IEEE80211_HDRLEN + 2) |
27c77751 JM |
927 | return; |
928 | ||
2703fb4a | 929 | pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; |
27c77751 | 930 | act = *pos++; |
dbfb8e82 | 931 | end = ((const u8 *) mgmt) + len; |
27c77751 JM |
932 | |
933 | wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, | |
dbfb8e82 | 934 | act, MAC2STR(mgmt->sa)); |
2049a875 | 935 | if (wpa_s->wpa_state < WPA_ASSOCIATED || |
dbfb8e82 | 936 | os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { |
2049a875 JM |
937 | wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " |
938 | "frame"); | |
939 | return; | |
940 | } | |
75cad1a0 XC |
941 | |
942 | switch (act) { | |
27c77751 | 943 | case WNM_BSS_TRANS_MGMT_REQ: |
2049a875 | 944 | ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, |
dbfb8e82 | 945 | !(mgmt->da[0] & 0x01)); |
27c77751 | 946 | break; |
75cad1a0 | 947 | case WNM_SLEEP_MODE_RESP: |
dbfb8e82 | 948 | ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); |
75cad1a0 | 949 | break; |
95a3ea94 JM |
950 | case WNM_NOTIFICATION_REQ: |
951 | ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); | |
952 | break; | |
75cad1a0 | 953 | default: |
e27d20bb | 954 | wpa_printf(MSG_ERROR, "WNM: Unknown request"); |
75cad1a0 XC |
955 | break; |
956 | } | |
957 | } |