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 */
28static 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 */
38static 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 */
49int 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
148static 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
173static 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
234static 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
307void 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
328static 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
439static 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
478static 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
519static 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
566void 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 */
607send_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
619static 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
733int 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
768static 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
875static 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
915void 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