blob: 3b45bf632fdbea8b29a941c53283733b52fb0742 [file] [log] [blame]
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001/*
2 * wpa_supplicant - WNM
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -07003 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07004 *
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"
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -080013#include "common/ieee802_11_common.h"
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -070014#include "common/wpa_ctrl.h"
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070015#include "rsn_supp/wpa.h"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080016#include "wpa_supplicant_i.h"
17#include "driver_i.h"
18#include "scan.h"
Dmitry Shmidt44c95782013-05-17 09:51:35 -070019#include "ctrl_iface.h"
20#include "bss.h"
21#include "wnm_sta.h"
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080022#include "hs20_supplicant.h"
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070023
24#define MAX_TFS_IE_LEN 1024
Dmitry Shmidt44c95782013-05-17 09:51:35 -070025#define WNM_MAX_NEIGHBOR_REPORT 10
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070026
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070027
28/* get the TFS IE from driver */
29static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
30 u16 *buf_len, enum wnm_oper oper)
31{
32 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
33
34 return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
35}
36
37
38/* set the TFS IE to driver */
39static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080040 const u8 *addr, const u8 *buf, u16 buf_len,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070041 enum wnm_oper oper)
42{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080043 u16 len = buf_len;
44
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070045 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
46
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080047 return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070048}
49
50
51/* MLME-SLEEPMODE.request */
52int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080053 u8 action, u16 intval, struct wpabuf *tfs_req)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070054{
55 struct ieee80211_mgmt *mgmt;
56 int res;
57 size_t len;
58 struct wnm_sleep_element *wnmsleep_ie;
59 u8 *wnmtfs_ie;
60 u8 wnmsleep_ie_len;
61 u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
62 enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
63 WNM_SLEEP_TFS_REQ_IE_NONE;
64
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080065 wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
66 "action=%s to " MACSTR,
67 action == 0 ? "enter" : "exit",
68 MAC2STR(wpa_s->bssid));
69
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070070 /* WNM-Sleep Mode IE */
71 wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
72 wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
73 if (wnmsleep_ie == NULL)
74 return -1;
75 wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
76 wnmsleep_ie->len = wnmsleep_ie_len - 2;
77 wnmsleep_ie->action_type = action;
78 wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080079 wnmsleep_ie->intval = host_to_le16(intval);
80 wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
81 (u8 *) wnmsleep_ie, wnmsleep_ie_len);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070082
83 /* TFS IE(s) */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080084 if (tfs_req) {
85 wnmtfs_ie_len = wpabuf_len(tfs_req);
86 wnmtfs_ie = os_malloc(wnmtfs_ie_len);
87 if (wnmtfs_ie == NULL) {
88 os_free(wnmsleep_ie);
89 return -1;
90 }
91 os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
92 } else {
93 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
94 if (wnmtfs_ie == NULL) {
95 os_free(wnmsleep_ie);
96 return -1;
97 }
98 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
99 tfs_oper)) {
100 wnmtfs_ie_len = 0;
101 os_free(wnmtfs_ie);
102 wnmtfs_ie = NULL;
103 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700104 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800105 wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
106 (u8 *) wnmtfs_ie, wnmtfs_ie_len);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700107
108 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
109 if (mgmt == NULL) {
110 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
111 "WNM-Sleep Request action frame");
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800112 os_free(wnmsleep_ie);
113 os_free(wnmtfs_ie);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700114 return -1;
115 }
116
117 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
118 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
119 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
120 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
121 WLAN_FC_STYPE_ACTION);
122 mgmt->u.action.category = WLAN_ACTION_WNM;
123 mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800124 mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700125 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
126 wnmsleep_ie_len);
127 /* copy TFS IE here */
128 if (wnmtfs_ie_len > 0) {
129 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
130 wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
131 }
132
133 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
134 wnmtfs_ie_len;
135
136 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
137 wpa_s->own_addr, wpa_s->bssid,
138 &mgmt->u.action.category, len, 0);
139 if (res < 0)
140 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
141 "(action=%d, intval=%d)", action, intval);
142
143 os_free(wnmsleep_ie);
144 os_free(wnmtfs_ie);
145 os_free(mgmt);
146
147 return res;
148}
149
150
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800151static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800152 const u8 *tfsresp_ie_start,
153 const u8 *tfsresp_ie_end)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800154{
155 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
156 wpa_s->bssid, NULL, NULL);
157 /* remove GTK/IGTK ?? */
158
159 /* set the TFS Resp IE(s) */
160 if (tfsresp_ie_start && tfsresp_ie_end &&
161 tfsresp_ie_end - tfsresp_ie_start >= 0) {
162 u16 tfsresp_ie_len;
163 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
164 tfsresp_ie_start;
165 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
166 /* pass the TFS Resp IE(s) to driver for processing */
167 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
168 tfsresp_ie_start,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800169 tfsresp_ie_len,
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800170 WNM_SLEEP_TFS_RESP_IE_SET))
171 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
172 }
173}
174
175
176static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
177 const u8 *frm, u16 key_len_total)
178{
179 u8 *ptr, *end;
180 u8 gtk_len;
181
182 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid,
183 NULL, NULL);
184
185 /* Install GTK/IGTK */
186
187 /* point to key data field */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800188 ptr = (u8 *) frm + 1 + 2;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800189 end = ptr + key_len_total;
190 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
191
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800192 while (end - ptr > 1) {
193 if (2 + ptr[1] > end - ptr) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800194 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
195 "length");
196 if (end > ptr) {
197 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
198 ptr, end - ptr);
199 }
200 break;
201 }
202 if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
203 if (ptr[1] < 11 + 5) {
204 wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
205 "subelem");
206 break;
207 }
208 gtk_len = *(ptr + 4);
209 if (ptr[1] < 11 + gtk_len ||
210 gtk_len < 5 || gtk_len > 32) {
211 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
212 "subelem");
213 break;
214 }
215 wpa_wnmsleep_install_key(
216 wpa_s->wpa,
217 WNM_SLEEP_SUBELEM_GTK,
218 ptr);
219 ptr += 13 + gtk_len;
220#ifdef CONFIG_IEEE80211W
221 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
222 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
223 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
224 "subelem");
225 break;
226 }
227 wpa_wnmsleep_install_key(wpa_s->wpa,
228 WNM_SLEEP_SUBELEM_IGTK, ptr);
229 ptr += 10 + WPA_IGTK_LEN;
230#endif /* CONFIG_IEEE80211W */
231 } else
232 break; /* skip the loop */
233 }
234}
235
236
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700237static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
238 const u8 *frm, int len)
239{
240 /*
Dmitry Shmidt21de2142014-04-08 10:50:52 -0700241 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700242 * WNM-Sleep Mode IE | TFS Response IE
243 */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800244 const u8 *pos = frm; /* point to payload after the action field */
Dmitry Shmidt21de2142014-04-08 10:50:52 -0700245 u16 key_len_total;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700246 struct wnm_sleep_element *wnmsleep_ie = NULL;
247 /* multiple TFS Resp IE (assuming consecutive) */
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800248 const u8 *tfsresp_ie_start = NULL;
249 const u8 *tfsresp_ie_end = NULL;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800250 size_t left;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700251
Dmitry Shmidt21de2142014-04-08 10:50:52 -0700252 if (len < 3)
253 return;
254 key_len_total = WPA_GET_LE16(frm + 1);
255
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800256 wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
257 frm[0], key_len_total);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800258 left = len - 3;
259 if (key_len_total > left) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800260 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
261 return;
262 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800263 pos += 3 + key_len_total;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800264 while (pos - frm + 1 < len) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700265 u8 ie_len = *(pos + 1);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800266 if (2 + ie_len > frm + len - pos) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800267 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
268 break;
269 }
270 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800271 if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700272 wnmsleep_ie = (struct wnm_sleep_element *) pos;
273 else if (*pos == WLAN_EID_TFS_RESP) {
274 if (!tfsresp_ie_start)
275 tfsresp_ie_start = pos;
276 tfsresp_ie_end = pos;
277 } else
278 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
279 pos += ie_len + 2;
280 }
281
282 if (!wnmsleep_ie) {
283 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
284 return;
285 }
286
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800287 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
288 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700289 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
290 "frame (action=%d, intval=%d)",
291 wnmsleep_ie->action_type, wnmsleep_ie->intval);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800292 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
293 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
294 tfsresp_ie_end);
295 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
296 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700297 }
298 } else {
299 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
300 "(action=%d, intval=%d)",
301 wnmsleep_ie->action_type, wnmsleep_ie->intval);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800302 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700303 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
304 wpa_s->bssid, NULL, NULL);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800305 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700306 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
307 wpa_s->bssid, NULL, NULL);
308 }
309}
310
311
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700312void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
313{
314 int i;
315
316 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700317 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700318 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
319 }
320
Dmitry Shmidt21de2142014-04-08 10:50:52 -0700321 wpa_s->wnm_num_neighbor_report = 0;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700322 os_free(wpa_s->wnm_neighbor_report_elements);
323 wpa_s->wnm_neighbor_report_elements = NULL;
324}
325
326
327static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
328 u8 id, u8 elen, const u8 *pos)
329{
330 switch (id) {
331 case WNM_NEIGHBOR_TSF:
332 if (elen < 2 + 2) {
333 wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
334 break;
335 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800336 rep->tsf_offset = WPA_GET_LE16(pos);
337 rep->beacon_int = WPA_GET_LE16(pos + 2);
338 rep->tsf_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700339 break;
340 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
341 if (elen < 2) {
342 wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
343 "country string");
344 break;
345 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800346 os_memcpy(rep->country, pos, 2);
347 rep->country_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700348 break;
349 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
350 if (elen < 1) {
351 wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
352 "candidate");
353 break;
354 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800355 rep->preference = pos[0];
356 rep->preference_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700357 break;
358 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800359 rep->bss_term_tsf = WPA_GET_LE64(pos);
360 rep->bss_term_dur = WPA_GET_LE16(pos + 8);
361 rep->bss_term_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700362 break;
363 case WNM_NEIGHBOR_BEARING:
364 if (elen < 8) {
365 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
366 "bearing");
367 break;
368 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800369 rep->bearing = WPA_GET_LE16(pos);
370 rep->distance = WPA_GET_LE32(pos + 2);
371 rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
372 rep->bearing_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700373 break;
374 case WNM_NEIGHBOR_MEASUREMENT_PILOT:
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700375 if (elen < 1) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700376 wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
377 "pilot");
378 break;
379 }
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700380 os_free(rep->meas_pilot);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700381 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
382 if (rep->meas_pilot == NULL)
383 break;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700384 rep->meas_pilot->measurement_pilot = pos[0];
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700385 rep->meas_pilot->subelem_len = elen - 1;
386 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700387 break;
388 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700389 if (elen < 5) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700390 wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
391 "capabilities");
392 break;
393 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800394 os_memcpy(rep->rm_capab, pos, 5);
395 rep->rm_capab_present = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700396 break;
397 case WNM_NEIGHBOR_MULTIPLE_BSSID:
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700398 if (elen < 1) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700399 wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
400 break;
401 }
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700402 os_free(rep->mul_bssid);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700403 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
404 if (rep->mul_bssid == NULL)
405 break;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700406 rep->mul_bssid->max_bssid_indicator = pos[0];
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700407 rep->mul_bssid->subelem_len = elen - 1;
408 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700409 break;
410 }
411}
412
413
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800414static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
415{
416 struct wpa_bss *bss = wpa_s->current_bss;
417 const char *country = NULL;
418
419 if (bss) {
420 const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
421
422 if (elem && elem[1] >= 2)
423 country = (const char *) (elem + 2);
424 }
425
426 return ieee80211_chan_to_freq(country, op_class, chan);
427}
428
429
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700430static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
431 const u8 *pos, u8 len,
432 struct neighbor_report *rep)
433{
434 u8 left = len;
435
436 if (left < 13) {
437 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
438 return;
439 }
440
441 os_memcpy(rep->bssid, pos, ETH_ALEN);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800442 rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700443 rep->regulatory_class = *(pos + 10);
444 rep->channel_number = *(pos + 11);
445 rep->phy_type = *(pos + 12);
446
447 pos += 13;
448 left -= 13;
449
450 while (left >= 2) {
451 u8 id, elen;
452
453 id = *pos++;
454 elen = *pos++;
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700455 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
456 left -= 2;
457 if (elen > left) {
458 wpa_printf(MSG_DEBUG,
459 "WNM: Truncated neighbor report subelement");
460 break;
461 }
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700462 wnm_parse_neighbor_report_elem(rep, id, elen, pos);
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700463 left -= elen;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700464 pos += elen;
465 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800466
467 rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
468 rep->channel_number);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700469}
470
471
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800472static struct wpa_bss *
473compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700474{
475
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800476 u8 i;
477 struct wpa_bss *bss = wpa_s->current_bss;
478 struct wpa_bss *target;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700479
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800480 if (!bss)
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700481 return 0;
482
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800483 wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800484 MAC2STR(wpa_s->bssid), bss->level);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800485
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800486 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
487 struct neighbor_report *nei;
488
489 nei = &wpa_s->wnm_neighbor_report_elements[i];
490 if (nei->preference_present && nei->preference == 0) {
491 wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
492 MAC2STR(nei->bssid));
493 continue;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700494 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800495
496 target = wpa_bss_get_bssid(wpa_s, nei->bssid);
497 if (!target) {
498 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
499 " (pref %d) not found in scan results",
500 MAC2STR(nei->bssid),
501 nei->preference_present ? nei->preference :
502 -1);
503 continue;
504 }
505
506 if (bss->ssid_len != target->ssid_len ||
507 os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
508 /*
509 * TODO: Could consider allowing transition to another
510 * ESS if PMF was enabled for the association.
511 */
512 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
513 " (pref %d) in different ESS",
514 MAC2STR(nei->bssid),
515 nei->preference_present ? nei->preference :
516 -1);
517 continue;
518 }
519
520 if (target->level < bss->level && target->level < -80) {
521 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
522 " (pref %d) does not have sufficient signal level (%d)",
523 MAC2STR(nei->bssid),
524 nei->preference_present ? nei->preference :
525 -1,
526 target->level);
527 continue;
528 }
529
530 wpa_printf(MSG_DEBUG,
531 "WNM: Found an acceptable preferred transition candidate BSS "
532 MACSTR " (RSSI %d)",
533 MAC2STR(nei->bssid), target->level);
534 return target;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700535 }
536
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800537 return NULL;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700538}
539
540
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700541static void wnm_send_bss_transition_mgmt_resp(
542 struct wpa_supplicant *wpa_s, u8 dialog_token,
543 enum bss_trans_mgmt_status_code status, u8 delay,
544 const u8 *target_bssid)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800545{
546 u8 buf[1000], *pos;
547 struct ieee80211_mgmt *mgmt;
548 size_t len;
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800549 int res;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800550
551 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
552 "to " MACSTR " dialog_token=%u status=%u delay=%d",
553 MAC2STR(wpa_s->bssid), dialog_token, status, delay);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800554 if (!wpa_s->current_bss) {
555 wpa_printf(MSG_DEBUG,
556 "WNM: Current BSS not known - drop response");
557 return;
558 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800559
560 mgmt = (struct ieee80211_mgmt *) buf;
561 os_memset(&buf, 0, sizeof(buf));
562 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
563 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
564 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
565 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
566 WLAN_FC_STYPE_ACTION);
567 mgmt->u.action.category = WLAN_ACTION_WNM;
568 mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
569 mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
570 mgmt->u.action.u.bss_tm_resp.status_code = status;
571 mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
572 pos = mgmt->u.action.u.bss_tm_resp.variable;
573 if (target_bssid) {
574 os_memcpy(pos, target_bssid, ETH_ALEN);
575 pos += ETH_ALEN;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800576 } else if (status == WNM_BSS_TM_ACCEPT) {
577 /*
578 * P802.11-REVmc clarifies that the Target BSSID field is always
579 * present when status code is zero, so use a fake value here if
580 * no BSSID is yet known.
581 */
582 os_memset(pos, 0, ETH_ALEN);
583 pos += ETH_ALEN;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800584 }
585
586 len = pos - (u8 *) &mgmt->u.action.category;
587
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800588 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
589 wpa_s->own_addr, wpa_s->bssid,
590 &mgmt->u.action.category, len, 0);
591 if (res < 0) {
592 wpa_printf(MSG_DEBUG,
593 "WNM: Failed to send BSS Transition Management Response");
594 }
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800595}
596
597
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800598int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700599{
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800600 struct wpa_bss *bss;
601 struct wpa_ssid *ssid = wpa_s->current_ssid;
602 enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700603
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800604 if (!wpa_s->wnm_neighbor_report_elements)
605 return 0;
606
607 if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
608 &wpa_s->scan_trigger_time)) {
609 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
610 wnm_deallocate_memory(wpa_s);
611 return 0;
612 }
613
614 if (!wpa_s->current_bss ||
615 os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
616 ETH_ALEN) != 0) {
617 wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
618 return 0;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700619 }
620
621 /* Compare the Neighbor Report and scan results */
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800622 bss = compare_scan_neighbor_results(wpa_s);
623 if (!bss) {
624 wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
625 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
626 goto send_bss_resp_fail;
627 }
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700628
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800629 /* Associate to the network */
630 /* Send the BSS Management Response - Accept */
631 if (wpa_s->wnm_reply) {
632 wpa_s->wnm_reply = 0;
633 wnm_send_bss_transition_mgmt_resp(wpa_s,
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700634 wpa_s->wnm_dialog_token,
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700635 WNM_BSS_TM_ACCEPT,
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800636 0, bss->bssid);
637 }
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700638
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800639 if (bss == wpa_s->current_bss) {
640 wpa_printf(MSG_DEBUG,
641 "WNM: Already associated with the preferred candidate");
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800642 wnm_deallocate_memory(wpa_s);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800643 return 1;
644 }
645
646 wpa_s->reassociate = 1;
647 wpa_supplicant_connect(wpa_s, bss, ssid);
648 wnm_deallocate_memory(wpa_s);
649 return 1;
650
651send_bss_resp_fail:
652 if (!reply_on_fail)
653 return 0;
654
655 /* Send reject response for all the failures */
656
657 if (wpa_s->wnm_reply) {
658 wpa_s->wnm_reply = 0;
659 wnm_send_bss_transition_mgmt_resp(wpa_s,
660 wpa_s->wnm_dialog_token,
661 status, 0, NULL);
662 }
663 wnm_deallocate_memory(wpa_s);
664
665 return 0;
666}
667
668
669static int cand_pref_compar(const void *a, const void *b)
670{
671 const struct neighbor_report *aa = a;
672 const struct neighbor_report *bb = b;
673
674 if (!aa->preference_present && !bb->preference_present)
675 return 0;
676 if (!aa->preference_present)
677 return 1;
678 if (!bb->preference_present)
679 return -1;
680 if (bb->preference > aa->preference)
681 return 1;
682 if (bb->preference < aa->preference)
683 return -1;
684 return 0;
685}
686
687
688static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
689{
690 if (!wpa_s->wnm_neighbor_report_elements)
691 return;
692 qsort(wpa_s->wnm_neighbor_report_elements,
693 wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
694 cand_pref_compar);
695}
696
697
698static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
699{
700 unsigned int i;
701
702 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
703 if (!wpa_s->wnm_neighbor_report_elements)
704 return;
705 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
706 struct neighbor_report *nei;
707
708 nei = &wpa_s->wnm_neighbor_report_elements[i];
709 wpa_printf(MSG_DEBUG, "%u: " MACSTR
710 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
711 i, MAC2STR(nei->bssid), nei->bssid_info,
712 nei->regulatory_class,
713 nei->channel_number, nei->phy_type,
714 nei->preference_present ? nei->preference : -1,
715 nei->freq);
716 }
717}
718
719
720static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
721{
722 unsigned int i;
723
724 for (i = 0; i < wpa_s->hw.num_modes; i++) {
725 struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
726 int j;
727
728 for (j = 0; j < mode->num_channels; j++) {
729 struct hostapd_channel_data *chan;
730
731 chan = &mode->channels[j];
732 if (chan->freq == freq &&
733 !(chan->flag & HOSTAPD_CHAN_DISABLED))
734 return 1;
735 }
736 }
737
738 return 0;
739}
740
741
742static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
743{
744 int *freqs;
745 int num_freqs = 0;
746 unsigned int i;
747
748 if (!wpa_s->wnm_neighbor_report_elements)
749 return;
750
751 if (wpa_s->hw.modes == NULL)
752 return;
753
754 os_free(wpa_s->next_scan_freqs);
755 wpa_s->next_scan_freqs = NULL;
756
757 freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
758 if (freqs == NULL)
759 return;
760
761 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
762 struct neighbor_report *nei;
763
764 nei = &wpa_s->wnm_neighbor_report_elements[i];
765 if (nei->freq <= 0) {
766 wpa_printf(MSG_DEBUG,
767 "WNM: Unknown neighbor operating frequency for "
768 MACSTR " - scan all channels",
769 MAC2STR(nei->bssid));
770 os_free(freqs);
771 return;
772 }
773 if (chan_supported(wpa_s, nei->freq))
774 add_freq(freqs, &num_freqs, nei->freq);
775 }
776
777 if (num_freqs == 0) {
778 os_free(freqs);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700779 return;
780 }
781
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800782 wpa_printf(MSG_DEBUG,
783 "WNM: Scan %d frequencies based on transition candidate list",
784 num_freqs);
785 wpa_s->next_scan_freqs = freqs;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700786}
787
788
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800789static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
790 const u8 *pos, const u8 *end,
791 int reply)
792{
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800793 unsigned int beacon_int;
794 u8 valid_int;
795
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800796 if (end - pos < 5)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800797 return;
798
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800799 if (wpa_s->current_bss)
800 beacon_int = wpa_s->current_bss->beacon_int;
801 else
802 beacon_int = 100; /* best guess */
803
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700804 wpa_s->wnm_dialog_token = pos[0];
805 wpa_s->wnm_mode = pos[1];
806 wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800807 valid_int = pos[4];
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700808 wpa_s->wnm_reply = reply;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800809
810 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
811 "dialog_token=%u request_mode=0x%x "
812 "disassoc_timer=%u validity_interval=%u",
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700813 wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800814 wpa_s->wnm_dissoc_timer, valid_int);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700815
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800816 pos += 5;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700817
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700818 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800819 if (end - pos < 12) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700820 wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
821 return;
822 }
823 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800824 pos += 12; /* BSS Termination Duration */
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700825 }
826
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700827 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800828 char url[256];
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700829
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800830 if (end - pos < 1 || 1 + pos[0] > end - pos) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800831 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
832 "Management Request (URL)");
833 return;
834 }
835 os_memcpy(url, pos + 1, pos[0]);
836 url[pos[0]] = '\0';
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700837 pos += 1 + pos[0];
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700838
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700839 wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
840 wpa_sm_pmf_enabled(wpa_s->wpa),
841 wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800842 }
843
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700844 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800845 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700846 "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
847 if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800848 /* TODO: mark current BSS less preferred for
849 * selection */
850 wpa_printf(MSG_DEBUG, "Trying to find another BSS");
851 wpa_supplicant_req_scan(wpa_s, 0, 0);
852 }
853 }
854
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700855 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800856 unsigned int valid_ms;
857
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700858 wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800859 wnm_deallocate_memory(wpa_s);
860 wpa_s->wnm_neighbor_report_elements = os_calloc(
861 WNM_MAX_NEIGHBOR_REPORT,
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700862 sizeof(struct neighbor_report));
863 if (wpa_s->wnm_neighbor_report_elements == NULL)
864 return;
865
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800866 while (end - pos >= 2 &&
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700867 wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
868 {
869 u8 tag = *pos++;
870 u8 len = *pos++;
871
872 wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
873 tag);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800874 if (len > end - pos) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700875 wpa_printf(MSG_DEBUG, "WNM: Truncated request");
876 return;
877 }
Dmitry Shmidtf940fbd2014-04-10 10:23:13 -0700878 if (tag == WLAN_EID_NEIGHBOR_REPORT) {
879 struct neighbor_report *rep;
880 rep = &wpa_s->wnm_neighbor_report_elements[
881 wpa_s->wnm_num_neighbor_report];
882 wnm_parse_neighbor_report(wpa_s, pos, len, rep);
883 }
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700884
885 pos += len;
886 wpa_s->wnm_num_neighbor_report++;
887 }
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800888 wnm_sort_cand_list(wpa_s);
889 wnm_dump_cand_list(wpa_s);
890 valid_ms = valid_int * beacon_int * 128 / 125;
891 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
892 valid_ms);
893 os_get_reltime(&wpa_s->wnm_cand_valid_until);
894 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
895 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
896 wpa_s->wnm_cand_valid_until.sec +=
897 wpa_s->wnm_cand_valid_until.usec / 1000000;
898 wpa_s->wnm_cand_valid_until.usec %= 1000000;
899 os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700900
Dmitry Shmidtfb45fd52015-01-05 13:08:17 -0800901 if (wpa_s->last_scan_res_used > 0) {
902 struct os_reltime now;
903
904 os_get_reltime(&now);
905 if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
906 wpa_printf(MSG_DEBUG,
907 "WNM: Try to use recent scan results");
908 if (wnm_scan_process(wpa_s, 0) > 0)
909 return;
910 wpa_printf(MSG_DEBUG,
911 "WNM: No match in previous scan results - try a new scan");
912 }
913 }
914
915 wnm_set_scan_freqs(wpa_s);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700916 wpa_supplicant_req_scan(wpa_s, 0, 0);
917 } else if (reply) {
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700918 enum bss_trans_mgmt_status_code status;
919 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
920 status = WNM_BSS_TM_ACCEPT;
921 else {
922 wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
923 status = WNM_BSS_TM_REJECT_UNSPECIFIED;
924 }
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700925 wnm_send_bss_transition_mgmt_resp(wpa_s,
926 wpa_s->wnm_dialog_token,
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -0700927 status, 0, NULL);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800928 }
929}
930
931
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700932int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
933 u8 query_reason)
934{
935 u8 buf[1000], *pos;
936 struct ieee80211_mgmt *mgmt;
937 size_t len;
938 int ret;
939
940 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
941 MACSTR " query_reason=%u",
942 MAC2STR(wpa_s->bssid), query_reason);
943
944 mgmt = (struct ieee80211_mgmt *) buf;
945 os_memset(&buf, 0, sizeof(buf));
946 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
947 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
948 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
949 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
950 WLAN_FC_STYPE_ACTION);
951 mgmt->u.action.category = WLAN_ACTION_WNM;
952 mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800953 mgmt->u.action.u.bss_tm_query.dialog_token = 1;
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700954 mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
955 pos = mgmt->u.action.u.bss_tm_query.variable;
956
957 len = pos - (u8 *) &mgmt->u.action.category;
958
959 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
960 wpa_s->own_addr, wpa_s->bssid,
961 &mgmt->u.action.category, len, 0);
962
963 return ret;
964}
965
966
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800967static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
968 const u8 *sa, const u8 *data,
969 int len)
970{
971 const u8 *pos, *end, *next;
972 u8 ie, ie_len;
973
974 pos = data;
975 end = data + len;
976
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800977 while (end - pos > 1) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800978 ie = *pos++;
979 ie_len = *pos++;
980 wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
981 ie, ie_len);
982 if (ie_len > end - pos) {
983 wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
984 "subelement");
985 break;
986 }
987 next = pos + ie_len;
988 if (ie_len < 4) {
989 pos = next;
990 continue;
991 }
992 wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
993 WPA_GET_BE24(pos), pos[3]);
994
995#ifdef CONFIG_HS20
996 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
997 WPA_GET_BE24(pos) == OUI_WFA &&
998 pos[3] == HS20_WNM_SUB_REM_NEEDED) {
999 /* Subscription Remediation subelement */
1000 const u8 *ie_end;
1001 u8 url_len;
1002 char *url;
1003 u8 osu_method;
1004
1005 wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
1006 "subelement");
1007 ie_end = pos + ie_len;
1008 pos += 4;
1009 url_len = *pos++;
1010 if (url_len == 0) {
1011 wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
1012 url = NULL;
1013 osu_method = 1;
1014 } else {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001015 if (url_len + 1 > ie_end - pos) {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001016 wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1017 url_len,
1018 (int) (ie_end - pos));
1019 break;
1020 }
1021 url = os_malloc(url_len + 1);
1022 if (url == NULL)
1023 break;
1024 os_memcpy(url, pos, url_len);
1025 url[url_len] = '\0';
1026 osu_method = pos[url_len];
1027 }
1028 hs20_rx_subscription_remediation(wpa_s, url,
1029 osu_method);
1030 os_free(url);
1031 pos = next;
1032 continue;
1033 }
1034
1035 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1036 WPA_GET_BE24(pos) == OUI_WFA &&
1037 pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1038 const u8 *ie_end;
1039 u8 url_len;
1040 char *url;
1041 u8 code;
1042 u16 reauth_delay;
1043
1044 ie_end = pos + ie_len;
1045 pos += 4;
1046 code = *pos++;
1047 reauth_delay = WPA_GET_LE16(pos);
1048 pos += 2;
1049 url_len = *pos++;
1050 wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1051 "Imminent - Reason Code %u "
1052 "Re-Auth Delay %u URL Length %u",
1053 code, reauth_delay, url_len);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001054 if (url_len > ie_end - pos)
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001055 break;
1056 url = os_malloc(url_len + 1);
1057 if (url == NULL)
1058 break;
1059 os_memcpy(url, pos, url_len);
1060 url[url_len] = '\0';
1061 hs20_rx_deauth_imminent_notice(wpa_s, code,
1062 reauth_delay, url);
1063 os_free(url);
1064 pos = next;
1065 continue;
1066 }
1067#endif /* CONFIG_HS20 */
1068
1069 pos = next;
1070 }
1071}
1072
1073
1074static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1075 const u8 *sa, const u8 *frm, int len)
1076{
1077 const u8 *pos, *end;
1078 u8 dialog_token, type;
1079
1080 /* Dialog Token [1] | Type [1] | Subelements */
1081
1082 if (len < 2 || sa == NULL)
1083 return;
1084 end = frm + len;
1085 pos = frm;
1086 dialog_token = *pos++;
1087 type = *pos++;
1088
1089 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
1090 "(dialog_token %u type %u sa " MACSTR ")",
1091 dialog_token, type, MAC2STR(sa));
1092 wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
1093 pos, end - pos);
1094
1095 if (wpa_s->wpa_state != WPA_COMPLETED ||
1096 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
1097 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
1098 "from our AP - ignore it");
1099 return;
1100 }
1101
1102 switch (type) {
1103 case 1:
1104 ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
1105 break;
1106 default:
1107 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
1108 "WNM-Notification type %u", type);
1109 break;
1110 }
1111}
1112
1113
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001114void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001115 const struct ieee80211_mgmt *mgmt, size_t len)
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001116{
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001117 const u8 *pos, *end;
1118 u8 act;
1119
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001120 if (len < IEEE80211_HDRLEN + 2)
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001121 return;
1122
Dmitry Shmidt623d63a2014-06-13 11:05:14 -07001123 pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001124 act = *pos++;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001125 end = ((const u8 *) mgmt) + len;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001126
1127 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001128 act, MAC2STR(mgmt->sa));
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001129 if (wpa_s->wpa_state < WPA_ASSOCIATED ||
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001130 os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001131 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
1132 "frame");
1133 return;
1134 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001135
1136 switch (act) {
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001137 case WNM_BSS_TRANS_MGMT_REQ:
1138 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001139 !(mgmt->da[0] & 0x01));
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001140 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001141 case WNM_SLEEP_MODE_RESP:
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001142 ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001143 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001144 case WNM_NOTIFICATION_REQ:
1145 ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
1146 break;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001147 default:
Dmitry Shmidt44c95782013-05-17 09:51:35 -07001148 wpa_printf(MSG_ERROR, "WNM: Unknown request");
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001149 break;
1150 }
1151}