• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * wpa_supplicant - WNM
3  * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
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"
13 #include "common/wpa_ctrl.h"
14 #include "rsn_supp/wpa.h"
15 #include "wpa_supplicant_i.h"
16 #include "driver_i.h"
17 #include "scan.h"
18 #include "ctrl_iface.h"
19 #include "bss.h"
20 #include "wnm_sta.h"
21 #include "hs20_supplicant.h"
22 
23 #define MAX_TFS_IE_LEN  1024
24 #define WNM_MAX_NEIGHBOR_REPORT 10
25 
26 
27 /* get the TFS IE from driver */
ieee80211_11_get_tfs_ie(struct wpa_supplicant * wpa_s,u8 * buf,u16 * buf_len,enum wnm_oper oper)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 */
ieee80211_11_set_tfs_ie(struct wpa_supplicant * wpa_s,const u8 * addr,u8 * buf,u16 * buf_len,enum wnm_oper oper)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 */
ieee802_11_send_wnmsleep_req(struct wpa_supplicant * wpa_s,u8 action,u16 intval,struct wpabuf * tfs_req)49 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
50 				 u8 action, u16 intval, struct wpabuf *tfs_req)
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 
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 
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;
76 	wnmsleep_ie->intval = host_to_le16(intval);
77 	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
78 		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
79 
80 	/* TFS IE(s) */
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 		}
101 	}
102 	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
103 		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
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");
109 		os_free(wnmsleep_ie);
110 		os_free(wnmtfs_ie);
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;
121 	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
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 
wnm_sleep_mode_enter_success(struct wpa_supplicant * wpa_s,u8 * tfsresp_ie_start,u8 * tfsresp_ie_end)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 
wnm_sleep_mode_exit_success(struct wpa_supplicant * wpa_s,const u8 * frm,u16 key_len_total)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 */
185 	ptr = (u8 *) frm + 1 + 2;
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 
ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant * wpa_s,const u8 * frm,int len)234 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
235 					const u8 *frm, int len)
236 {
237 	/*
238 	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
239 	 * WNM-Sleep Mode IE | TFS Response IE
240 	 */
241 	u8 *pos = (u8 *) frm; /* point to payload after the action field */
242 	u16 key_len_total;
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;
247 
248 	if (len < 3)
249 		return;
250 	key_len_total = WPA_GET_LE16(frm + 1);
251 
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;
255 	if (pos > frm + len) {
256 		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
257 		return;
258 	}
259 	while (pos - frm < len) {
260 		u8 ie_len = *(pos + 1);
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);
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 
282 	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
283 	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
284 		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
285 			   "frame (action=%d, intval=%d)",
286 			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
287 		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
288 			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
289 						     tfsresp_ie_end);
290 		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
291 			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
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);
297 		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
298 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
299 					 wpa_s->bssid, NULL, NULL);
300 		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
301 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
302 					 wpa_s->bssid, NULL, NULL);
303 	}
304 }
305 
306 
wnm_deallocate_memory(struct wpa_supplicant * wpa_s)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++) {
312 		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
313 		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
314 		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
315 		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
316 		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
317 		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
318 		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
319 		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
320 	}
321 
322 	wpa_s->wnm_num_neighbor_report = 0;
323 	os_free(wpa_s->wnm_neighbor_report_elements);
324 	wpa_s->wnm_neighbor_report_elements = NULL;
325 }
326 
327 
wnm_parse_neighbor_report_elem(struct neighbor_report * rep,u8 id,u8 elen,const u8 * pos)328 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
329 					   u8 id, u8 elen, const u8 *pos)
330 {
331 	switch (id) {
332 	case WNM_NEIGHBOR_TSF:
333 		if (elen < 2 + 2) {
334 			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
335 			break;
336 		}
337 		os_free(rep->tsf_info);
338 		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
339 		if (rep->tsf_info == NULL)
340 			break;
341 		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
342 		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
343 		break;
344 	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
345 		if (elen < 2) {
346 			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
347 				   "country string");
348 			break;
349 		}
350 		os_free(rep->con_coun_str);
351 		rep->con_coun_str =
352 			os_zalloc(sizeof(struct condensed_country_string));
353 		if (rep->con_coun_str == NULL)
354 			break;
355 		os_memcpy(rep->con_coun_str->country_string, pos, 2);
356 		break;
357 	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
358 		if (elen < 1) {
359 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
360 				   "candidate");
361 			break;
362 		}
363 		os_free(rep->bss_tran_can);
364 		rep->bss_tran_can =
365 			os_zalloc(sizeof(struct bss_transition_candidate));
366 		if (rep->bss_tran_can == NULL)
367 			break;
368 		rep->bss_tran_can->preference = pos[0];
369 		break;
370 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
371 		if (elen < 10) {
372 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
373 				   "duration");
374 			break;
375 		}
376 		os_free(rep->bss_term_dur);
377 		rep->bss_term_dur =
378 			os_zalloc(sizeof(struct bss_termination_duration));
379 		if (rep->bss_term_dur == NULL)
380 			break;
381 		os_memcpy(rep->bss_term_dur->duration, pos, 10);
382 		break;
383 	case WNM_NEIGHBOR_BEARING:
384 		if (elen < 8) {
385 			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
386 				   "bearing");
387 			break;
388 		}
389 		os_free(rep->bearing);
390 		rep->bearing = os_zalloc(sizeof(struct bearing));
391 		if (rep->bearing == NULL)
392 			break;
393 		os_memcpy(rep->bearing->bearing, pos, 8);
394 		break;
395 	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
396 		if (elen < 1) {
397 			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
398 				   "pilot");
399 			break;
400 		}
401 		os_free(rep->meas_pilot);
402 		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
403 		if (rep->meas_pilot == NULL)
404 			break;
405 		rep->meas_pilot->measurement_pilot = pos[0];
406 		rep->meas_pilot->subelem_len = elen - 1;
407 		os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
408 		break;
409 	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
410 		if (elen < 5) {
411 			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
412 				   "capabilities");
413 			break;
414 		}
415 		os_free(rep->rrm_cap);
416 		rep->rrm_cap =
417 			os_zalloc(sizeof(struct rrm_enabled_capabilities));
418 		if (rep->rrm_cap == NULL)
419 			break;
420 		os_memcpy(rep->rrm_cap->capabilities, pos, 5);
421 		break;
422 	case WNM_NEIGHBOR_MULTIPLE_BSSID:
423 		if (elen < 1) {
424 			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
425 			break;
426 		}
427 		os_free(rep->mul_bssid);
428 		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
429 		if (rep->mul_bssid == NULL)
430 			break;
431 		rep->mul_bssid->max_bssid_indicator = pos[0];
432 		rep->mul_bssid->subelem_len = elen - 1;
433 		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
434 		break;
435 	}
436 }
437 
438 
wnm_parse_neighbor_report(struct wpa_supplicant * wpa_s,const u8 * pos,u8 len,struct neighbor_report * rep)439 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
440 				      const u8 *pos, u8 len,
441 				      struct neighbor_report *rep)
442 {
443 	u8 left = len;
444 
445 	if (left < 13) {
446 		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
447 		return;
448 	}
449 
450 	os_memcpy(rep->bssid, pos, ETH_ALEN);
451 	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
452 	rep->regulatory_class = *(pos + 10);
453 	rep->channel_number = *(pos + 11);
454 	rep->phy_type = *(pos + 12);
455 
456 	pos += 13;
457 	left -= 13;
458 
459 	while (left >= 2) {
460 		u8 id, elen;
461 
462 		id = *pos++;
463 		elen = *pos++;
464 		wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
465 		left -= 2;
466 		if (elen > left) {
467 			wpa_printf(MSG_DEBUG,
468 				   "WNM: Truncated neighbor report subelement");
469 			break;
470 		}
471 		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
472 		left -= elen;
473 		pos += elen;
474 	}
475 }
476 
477 
compare_scan_neighbor_results(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res,struct neighbor_report * neigh_rep,u8 num_neigh_rep,u8 * bssid_to_connect)478 static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
479 					 struct wpa_scan_results *scan_res,
480 					 struct neighbor_report *neigh_rep,
481 					 u8 num_neigh_rep, u8 *bssid_to_connect)
482 {
483 
484 	u8 i, j;
485 
486 	if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss)
487 		return 0;
488 
489 	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
490 		   MAC2STR(wpa_s->bssid), wpa_s->current_bss->level);
491 
492 	for (i = 0; i < num_neigh_rep; i++) {
493 		for (j = 0; j < scan_res->num; j++) {
494 			/* Check for a better RSSI AP */
495 			if (os_memcmp(scan_res->res[j]->bssid,
496 				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
497 			    scan_res->res[j]->level >
498 			    wpa_s->current_bss->level) {
499 				/* Got a BSSID with better RSSI value */
500 				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
501 					  ETH_ALEN);
502 				wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR
503 					   " with better scan RSSI %d",
504 					   MAC2STR(scan_res->res[j]->bssid),
505 					   scan_res->res[j]->level);
506 				return 1;
507 			}
508 			wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR
509 				   " RSSI %d", j,
510 				   MAC2STR(scan_res->res[j]->bssid),
511 				   scan_res->res[j]->level);
512 		}
513 	}
514 
515 	return 0;
516 }
517 
518 
wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant * wpa_s,u8 dialog_token,enum bss_trans_mgmt_status_code status,u8 delay,const u8 * target_bssid)519 static void wnm_send_bss_transition_mgmt_resp(
520 	struct wpa_supplicant *wpa_s, u8 dialog_token,
521 	enum bss_trans_mgmt_status_code status, u8 delay,
522 	const u8 *target_bssid)
523 {
524 	u8 buf[1000], *pos;
525 	struct ieee80211_mgmt *mgmt;
526 	size_t len;
527 
528 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
529 		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
530 		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
531 
532 	mgmt = (struct ieee80211_mgmt *) buf;
533 	os_memset(&buf, 0, sizeof(buf));
534 	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
535 	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
536 	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
537 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
538 					   WLAN_FC_STYPE_ACTION);
539 	mgmt->u.action.category = WLAN_ACTION_WNM;
540 	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
541 	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
542 	mgmt->u.action.u.bss_tm_resp.status_code = status;
543 	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
544 	pos = mgmt->u.action.u.bss_tm_resp.variable;
545 	if (target_bssid) {
546 		os_memcpy(pos, target_bssid, ETH_ALEN);
547 		pos += ETH_ALEN;
548 	} else if (status == WNM_BSS_TM_ACCEPT) {
549 		/*
550 		 * P802.11-REVmc clarifies that the Target BSSID field is always
551 		 * present when status code is zero, so use a fake value here if
552 		 * no BSSID is yet known.
553 		 */
554 		os_memset(pos, 0, ETH_ALEN);
555 		pos += ETH_ALEN;
556 	}
557 
558 	len = pos - (u8 *) &mgmt->u.action.category;
559 
560 	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
561 			    wpa_s->own_addr, wpa_s->bssid,
562 			    &mgmt->u.action.category, len, 0);
563 }
564 
565 
wnm_scan_response(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res)566 void wnm_scan_response(struct wpa_supplicant *wpa_s,
567 		       struct wpa_scan_results *scan_res)
568 {
569 	u8 bssid[ETH_ALEN];
570 
571 	if (scan_res == NULL) {
572 		wpa_printf(MSG_ERROR, "Scan result is NULL");
573 		goto send_bss_resp_fail;
574 	}
575 
576 	/* Compare the Neighbor Report and scan results */
577 	if (compare_scan_neighbor_results(wpa_s, scan_res,
578 					  wpa_s->wnm_neighbor_report_elements,
579 					  wpa_s->wnm_num_neighbor_report,
580 					  bssid) == 1) {
581 		/* Associate to the network */
582 		struct wpa_bss *bss;
583 		struct wpa_ssid *ssid = wpa_s->current_ssid;
584 
585 		bss = wpa_bss_get_bssid(wpa_s, bssid);
586 		if (!bss) {
587 			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
588 				   "BSS table");
589 			goto send_bss_resp_fail;
590 		}
591 
592 		/* Send the BSS Management Response - Accept */
593 		if (wpa_s->wnm_reply) {
594 			wnm_send_bss_transition_mgmt_resp(wpa_s,
595 						  wpa_s->wnm_dialog_token,
596 						  WNM_BSS_TM_ACCEPT,
597 						  0, bssid);
598 		}
599 
600 		wpa_s->reassociate = 1;
601 		wpa_supplicant_connect(wpa_s, bss, ssid);
602 		wnm_deallocate_memory(wpa_s);
603 		return;
604 	}
605 
606 	/* Send reject response for all the failures */
607 send_bss_resp_fail:
608 	wnm_deallocate_memory(wpa_s);
609 	if (wpa_s->wnm_reply) {
610 		wnm_send_bss_transition_mgmt_resp(wpa_s,
611 						  wpa_s->wnm_dialog_token,
612 						  WNM_BSS_TM_REJECT_UNSPECIFIED,
613 						  0, NULL);
614 	}
615 	return;
616 }
617 
618 
ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant * wpa_s,const u8 * pos,const u8 * end,int reply)619 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
620 					     const u8 *pos, const u8 *end,
621 					     int reply)
622 {
623 	if (pos + 5 > end)
624 		return;
625 
626 	wpa_s->wnm_dialog_token = pos[0];
627 	wpa_s->wnm_mode = pos[1];
628 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
629 	wpa_s->wnm_validity_interval = pos[4];
630 	wpa_s->wnm_reply = reply;
631 
632 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
633 		   "dialog_token=%u request_mode=0x%x "
634 		   "disassoc_timer=%u validity_interval=%u",
635 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
636 		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
637 
638 	pos += 5;
639 
640 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
641 		if (pos + 12 > end) {
642 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
643 			return;
644 		}
645 		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
646 		pos += 12; /* BSS Termination Duration */
647 	}
648 
649 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
650 		char url[256];
651 		unsigned int beacon_int;
652 
653 		if (pos + 1 > end || pos + 1 + pos[0] > end) {
654 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
655 				   "Management Request (URL)");
656 			return;
657 		}
658 		os_memcpy(url, pos + 1, pos[0]);
659 		url[pos[0]] = '\0';
660 		pos += 1 + pos[0];
661 
662 		if (wpa_s->current_bss)
663 			beacon_int = wpa_s->current_bss->beacon_int;
664 		else
665 			beacon_int = 100; /* best guess */
666 
667 		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
668 			wpa_sm_pmf_enabled(wpa_s->wpa),
669 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
670 	}
671 
672 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
673 		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
674 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
675 		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
676 			/* TODO: mark current BSS less preferred for
677 			 * selection */
678 			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
679 			wpa_supplicant_req_scan(wpa_s, 0, 0);
680 		}
681 	}
682 
683 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
684 		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
685 		wpa_s->wnm_num_neighbor_report = 0;
686 		os_free(wpa_s->wnm_neighbor_report_elements);
687 		wpa_s->wnm_neighbor_report_elements = os_zalloc(
688 			WNM_MAX_NEIGHBOR_REPORT *
689 			sizeof(struct neighbor_report));
690 		if (wpa_s->wnm_neighbor_report_elements == NULL)
691 			return;
692 
693 		while (pos + 2 <= end &&
694 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
695 		{
696 			u8 tag = *pos++;
697 			u8 len = *pos++;
698 
699 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
700 				   tag);
701 			if (pos + len > end) {
702 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
703 				return;
704 			}
705 			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
706 				struct neighbor_report *rep;
707 				rep = &wpa_s->wnm_neighbor_report_elements[
708 					wpa_s->wnm_num_neighbor_report];
709 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
710 			}
711 
712 			pos += len;
713 			wpa_s->wnm_num_neighbor_report++;
714 		}
715 
716 		wpa_s->scan_res_handler = wnm_scan_response;
717 		wpa_supplicant_req_scan(wpa_s, 0, 0);
718 	} else if (reply) {
719 		enum bss_trans_mgmt_status_code status;
720 		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
721 			status = WNM_BSS_TM_ACCEPT;
722 		else {
723 			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
724 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
725 		}
726 		wnm_send_bss_transition_mgmt_resp(wpa_s,
727 						  wpa_s->wnm_dialog_token,
728 						  status, 0, NULL);
729 	}
730 }
731 
732 
wnm_send_bss_transition_mgmt_query(struct wpa_supplicant * wpa_s,u8 query_reason)733 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
734 				       u8 query_reason)
735 {
736 	u8 buf[1000], *pos;
737 	struct ieee80211_mgmt *mgmt;
738 	size_t len;
739 	int ret;
740 
741 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
742 		   MACSTR " query_reason=%u",
743 		   MAC2STR(wpa_s->bssid), query_reason);
744 
745 	mgmt = (struct ieee80211_mgmt *) buf;
746 	os_memset(&buf, 0, sizeof(buf));
747 	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
748 	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
749 	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
750 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
751 					   WLAN_FC_STYPE_ACTION);
752 	mgmt->u.action.category = WLAN_ACTION_WNM;
753 	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
754 	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
755 	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
756 	pos = mgmt->u.action.u.bss_tm_query.variable;
757 
758 	len = pos - (u8 *) &mgmt->u.action.category;
759 
760 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
761 				  wpa_s->own_addr, wpa_s->bssid,
762 				  &mgmt->u.action.category, len, 0);
763 
764 	return ret;
765 }
766 
767 
ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant * wpa_s,const u8 * sa,const u8 * data,int len)768 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
769 					    const u8 *sa, const u8 *data,
770 					    int len)
771 {
772 	const u8 *pos, *end, *next;
773 	u8 ie, ie_len;
774 
775 	pos = data;
776 	end = data + len;
777 
778 	while (pos + 1 < end) {
779 		ie = *pos++;
780 		ie_len = *pos++;
781 		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
782 			   ie, ie_len);
783 		if (ie_len > end - pos) {
784 			wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
785 				   "subelement");
786 			break;
787 		}
788 		next = pos + ie_len;
789 		if (ie_len < 4) {
790 			pos = next;
791 			continue;
792 		}
793 		wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
794 			   WPA_GET_BE24(pos), pos[3]);
795 
796 #ifdef CONFIG_HS20
797 		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
798 		    WPA_GET_BE24(pos) == OUI_WFA &&
799 		    pos[3] == HS20_WNM_SUB_REM_NEEDED) {
800 			/* Subscription Remediation subelement */
801 			const u8 *ie_end;
802 			u8 url_len;
803 			char *url;
804 			u8 osu_method;
805 
806 			wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
807 				   "subelement");
808 			ie_end = pos + ie_len;
809 			pos += 4;
810 			url_len = *pos++;
811 			if (url_len == 0) {
812 				wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
813 				url = NULL;
814 				osu_method = 1;
815 			} else {
816 				if (pos + url_len + 1 > ie_end) {
817 					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
818 						   url_len,
819 						   (int) (ie_end - pos));
820 					break;
821 				}
822 				url = os_malloc(url_len + 1);
823 				if (url == NULL)
824 					break;
825 				os_memcpy(url, pos, url_len);
826 				url[url_len] = '\0';
827 				osu_method = pos[url_len];
828 			}
829 			hs20_rx_subscription_remediation(wpa_s, url,
830 							 osu_method);
831 			os_free(url);
832 			pos = next;
833 			continue;
834 		}
835 
836 		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
837 		    WPA_GET_BE24(pos) == OUI_WFA &&
838 		    pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
839 			const u8 *ie_end;
840 			u8 url_len;
841 			char *url;
842 			u8 code;
843 			u16 reauth_delay;
844 
845 			ie_end = pos + ie_len;
846 			pos += 4;
847 			code = *pos++;
848 			reauth_delay = WPA_GET_LE16(pos);
849 			pos += 2;
850 			url_len = *pos++;
851 			wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
852 				   "Imminent - Reason Code %u   "
853 				   "Re-Auth Delay %u  URL Length %u",
854 				   code, reauth_delay, url_len);
855 			if (pos + url_len > ie_end)
856 				break;
857 			url = os_malloc(url_len + 1);
858 			if (url == NULL)
859 				break;
860 			os_memcpy(url, pos, url_len);
861 			url[url_len] = '\0';
862 			hs20_rx_deauth_imminent_notice(wpa_s, code,
863 						       reauth_delay, url);
864 			os_free(url);
865 			pos = next;
866 			continue;
867 		}
868 #endif /* CONFIG_HS20 */
869 
870 		pos = next;
871 	}
872 }
873 
874 
ieee802_11_rx_wnm_notif_req(struct wpa_supplicant * wpa_s,const u8 * sa,const u8 * frm,int len)875 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
876 					const u8 *sa, const u8 *frm, int len)
877 {
878 	const u8 *pos, *end;
879 	u8 dialog_token, type;
880 
881 	/* Dialog Token [1] | Type [1] | Subelements */
882 
883 	if (len < 2 || sa == NULL)
884 		return;
885 	end = frm + len;
886 	pos = frm;
887 	dialog_token = *pos++;
888 	type = *pos++;
889 
890 	wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
891 		"(dialog_token %u type %u sa " MACSTR ")",
892 		dialog_token, type, MAC2STR(sa));
893 	wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
894 		    pos, end - pos);
895 
896 	if (wpa_s->wpa_state != WPA_COMPLETED ||
897 	    os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
898 		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
899 			"from our AP - ignore it");
900 		return;
901 	}
902 
903 	switch (type) {
904 	case 1:
905 		ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
906 		break;
907 	default:
908 		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
909 			"WNM-Notification type %u", type);
910 		break;
911 	}
912 }
913 
914 
ieee802_11_rx_wnm_action(struct wpa_supplicant * wpa_s,const struct ieee80211_mgmt * mgmt,size_t len)915 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
916 			      const struct ieee80211_mgmt *mgmt, size_t len)
917 {
918 	const u8 *pos, *end;
919 	u8 act;
920 
921 	if (len < IEEE80211_HDRLEN + 2)
922 		return;
923 
924 	pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
925 	act = *pos++;
926 	end = ((const u8 *) mgmt) + len;
927 
928 	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
929 		   act, MAC2STR(mgmt->sa));
930 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
931 	    os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
932 		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
933 			   "frame");
934 		return;
935 	}
936 
937 	switch (act) {
938 	case WNM_BSS_TRANS_MGMT_REQ:
939 		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
940 						 !(mgmt->da[0] & 0x01));
941 		break;
942 	case WNM_SLEEP_MODE_RESP:
943 		ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
944 		break;
945 	case WNM_NOTIFICATION_REQ:
946 		ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
947 		break;
948 	default:
949 		wpa_printf(MSG_ERROR, "WNM: Unknown request");
950 		break;
951 	}
952 }
953