161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/*
261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * wpa_supplicant - WNM
3f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt *
561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt * See README for more details.
761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt */
861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "utils/includes.h"
1061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
1161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "utils/common.h"
1261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "common/ieee802_11_defs.h"
13f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt#include "common/wpa_ctrl.h"
1461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#include "rsn_supp/wpa.h"
15a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#include "wpa_supplicant_i.h"
16a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#include "driver_i.h"
17a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#include "scan.h"
1844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt#include "ctrl_iface.h"
1944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt#include "bss.h"
2044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt#include "wnm_sta.h"
21f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt#include "hs20_supplicant.h"
2261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#define MAX_TFS_IE_LEN  1024
2444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt#define WNM_MAX_NEIGHBOR_REPORT 10
2561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* get the TFS IE from driver */
2861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
2961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   u16 *buf_len, enum wnm_oper oper)
3061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
3161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
3261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
3461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
3561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* set the TFS IE to driver */
3861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
3961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   const u8 *addr, u8 *buf, u16 *buf_len,
4061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   enum wnm_oper oper)
4161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
4261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
4361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
4561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
4661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* MLME-SLEEPMODE.request */
4961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtint ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
50a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				 u8 action, u16 intval, struct wpabuf *tfs_req)
5161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
5261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
5361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	int res;
5461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	size_t len;
5561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct wnm_sleep_element *wnmsleep_ie;
5661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *wnmtfs_ie;
5761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 wnmsleep_ie_len;
5861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
5961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
6061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		WNM_SLEEP_TFS_REQ_IE_NONE;
6161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
62a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
63a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "action=%s to " MACSTR,
64a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   action == 0 ? "enter" : "exit",
65a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   MAC2STR(wpa_s->bssid));
66a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
6761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* WNM-Sleep Mode IE */
6861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
6961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
7061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (wnmsleep_ie == NULL)
7161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
7261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
7361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->len = wnmsleep_ie_len - 2;
7461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->action_type = action;
7561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
76a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wnmsleep_ie->intval = host_to_le16(intval);
77a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
78a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
7961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
8061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* TFS IE(s) */
81a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (tfs_req) {
82a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie_len = wpabuf_len(tfs_req);
83a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
84a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmtfs_ie == NULL) {
85a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmsleep_ie);
86a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return -1;
87a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
88a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
89a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	} else {
90a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
91a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmtfs_ie == NULL) {
92a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmsleep_ie);
93a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return -1;
94a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
95a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
96a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    tfs_oper)) {
97a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnmtfs_ie_len = 0;
98a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmtfs_ie);
99a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnmtfs_ie = NULL;
100a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
10161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
102a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
103a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
10461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
10561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
10661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (mgmt == NULL) {
10761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
10861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "WNM-Sleep Request action frame");
109a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_free(wnmsleep_ie);
110a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_free(wnmtfs_ie);
11161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
11261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
11361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
11461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
11561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
11661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
11761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
11861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
11961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
12061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
121a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
12261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
12361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		  wnmsleep_ie_len);
12461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* copy TFS IE here */
12561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (wnmtfs_ie_len > 0) {
12661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
12761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
12861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
12961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wnmtfs_ie_len;
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  wpa_s->own_addr, wpa_s->bssid,
13561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  &mgmt->u.action.category, len, 0);
13661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (res < 0)
13761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
13861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(action=%d, intval=%d)", action, intval);
13961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(wnmsleep_ie);
14161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(wnmtfs_ie);
14261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(mgmt);
14361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return res;
14561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
14661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
148a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
149a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					 u8 *tfsresp_ie_start,
150a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					 u8 *tfsresp_ie_end)
151a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
152a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
153a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 wpa_s->bssid, NULL, NULL);
154a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* remove GTK/IGTK ?? */
155a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
156a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* set the TFS Resp IE(s) */
157a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (tfsresp_ie_start && tfsresp_ie_end &&
158a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
159a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		u16 tfsresp_ie_len;
160a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
161a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			tfsresp_ie_start;
162a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
163a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		/* pass the TFS Resp IE(s) to driver for processing */
164a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
165a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    tfsresp_ie_start,
166a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    &tfsresp_ie_len,
167a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    WNM_SLEEP_TFS_RESP_IE_SET))
168a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
169a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
170a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
171a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
172a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
173a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
174a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					const u8 *frm, u16 key_len_total)
175a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
176a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 *ptr, *end;
177a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 gtk_len;
178a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
179a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
180a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 NULL, NULL);
181a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
182a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* Install GTK/IGTK */
183a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
184a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* point to key data field */
185fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	ptr = (u8 *) frm + 1 + 2;
186a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	end = ptr + key_len_total;
187a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
188a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
189a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	while (ptr + 1 < end) {
190a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ptr + 2 + ptr[1] > end) {
191a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
192a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				   "length");
193a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (end > ptr) {
194a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
195a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    ptr, end - ptr);
196a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
197a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break;
198a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
199a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
200a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 11 + 5) {
201a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
202a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
203a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
204a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
205a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			gtk_len = *(ptr + 4);
206a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 11 + gtk_len ||
207a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    gtk_len < 5 || gtk_len > 32) {
208a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
209a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
210a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
211a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
212a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_wnmsleep_install_key(
213a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_s->wpa,
214a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				WNM_SLEEP_SUBELEM_GTK,
215a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				ptr);
216a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			ptr += 13 + gtk_len;
217a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#ifdef CONFIG_IEEE80211W
218a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
219a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
220a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
221a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
222a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
223a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
224a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_wnmsleep_install_key(wpa_s->wpa,
225a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt						 WNM_SLEEP_SUBELEM_IGTK, ptr);
226a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			ptr += 10 + WPA_IGTK_LEN;
227a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#endif /* CONFIG_IEEE80211W */
228a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else
229a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break; /* skip the loop */
230a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
231a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
232a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
233a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
23461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
23561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					const u8 *frm, int len)
23661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
23761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/*
23821de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
23961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * WNM-Sleep Mode IE | TFS Response IE
24061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 */
241fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	u8 *pos = (u8 *) frm; /* point to payload after the action field */
24221de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt	u16 key_len_total;
24361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct wnm_sleep_element *wnmsleep_ie = NULL;
24461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* multiple TFS Resp IE (assuming consecutive) */
24561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *tfsresp_ie_start = NULL;
24661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *tfsresp_ie_end = NULL;
24761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
24821de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt	if (len < 3)
24921de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt		return;
25021de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt	key_len_total = WPA_GET_LE16(frm + 1);
25121de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt
252fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
253fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		   frm[0], key_len_total);
254fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	pos += 3 + key_len_total;
255a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (pos > frm + len) {
256a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
257a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
258a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
25961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	while (pos - frm < len) {
26061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 ie_len = *(pos + 1);
261a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (pos + 2 + ie_len > frm + len) {
262a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
263a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break;
264a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
265a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
26661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (*pos == WLAN_EID_WNMSLEEP)
26761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wnmsleep_ie = (struct wnm_sleep_element *) pos;
26861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		else if (*pos == WLAN_EID_TFS_RESP) {
26961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			if (!tfsresp_ie_start)
27061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				tfsresp_ie_start = pos;
27161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			tfsresp_ie_end = pos;
27261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		} else
27361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
27461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		pos += ie_len + 2;
27561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
27661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
27761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!wnmsleep_ie) {
27861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
27961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
28061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
28161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
282a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
283a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
28461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
28561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "frame (action=%d, intval=%d)",
28661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
287a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
288a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
289a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt						     tfsresp_ie_end);
290a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
291a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
29261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
29361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else {
29461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
29561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(action=%d, intval=%d)",
29661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
297a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
29861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
29961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					 wpa_s->bssid, NULL, NULL);
300a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
30161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
30261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					 wpa_s->bssid, NULL, NULL);
30361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
30461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
30561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtvoid wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
30844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
30944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	int i;
31044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
31144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
31244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
31344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
31444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
31544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
31644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
31744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
31844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
31944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
32044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
32144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
32221de214b4ba4271ca20843f3b8fba9f1501b2a89Dmitry Shmidt	wpa_s->wnm_num_neighbor_report = 0;
32344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_free(wpa_s->wnm_neighbor_report_elements);
32444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_neighbor_report_elements = NULL;
32544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
32644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
32744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
32844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
32944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					   u8 id, u8 elen, const u8 *pos)
33044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
33144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	switch (id) {
33244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_TSF:
33344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2 + 2) {
33444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
33544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
33644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
337f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->tsf_info);
33844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
33944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->tsf_info == NULL)
34044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
34144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
34244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
34344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
34444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
34544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2) {
34644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
34744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "country string");
34844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
34944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
350f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->con_coun_str);
35144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->con_coun_str =
35244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct condensed_country_string));
35344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->con_coun_str == NULL)
35444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
35544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->con_coun_str->country_string, pos, 2);
35644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
35744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
35844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 1) {
35944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
36044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "candidate");
36144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
36244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
363f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->bss_tran_can);
36444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_tran_can =
36544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct bss_transition_candidate));
36644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bss_tran_can == NULL)
36744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
36844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_tran_can->preference = pos[0];
36944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
37044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
371f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		if (elen < 10) {
37244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
37344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "duration");
37444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
37544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
376f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->bss_term_dur);
37744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_term_dur =
37844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct bss_termination_duration));
37944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bss_term_dur == NULL)
38044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
381f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_memcpy(rep->bss_term_dur->duration, pos, 10);
38244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
38344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BEARING:
38444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 8) {
38544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
38644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "bearing");
38744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
38844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
389f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->bearing);
39044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bearing = os_zalloc(sizeof(struct bearing));
39144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bearing == NULL)
39244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
39344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->bearing->bearing, pos, 8);
39444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
39544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
396f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		if (elen < 1) {
39744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
39844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "pilot");
39944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
40044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
401f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->meas_pilot);
40244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
40344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->meas_pilot == NULL)
40444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
40544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot->measurement_pilot = pos[0];
406f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		rep->meas_pilot->subelem_len = elen - 1;
407f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
40844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
40944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
410f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		if (elen < 5) {
41144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
41244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "capabilities");
41344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
41444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
415f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->rrm_cap);
41644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->rrm_cap =
41744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct rrm_enabled_capabilities));
41844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->rrm_cap == NULL)
41944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
420f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_memcpy(rep->rrm_cap->capabilities, pos, 5);
42144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
42244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_MULTIPLE_BSSID:
423f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		if (elen < 1) {
42444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
42544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
42644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
427f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_free(rep->mul_bssid);
42844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
42944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->mul_bssid == NULL)
43044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
43144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid->max_bssid_indicator = pos[0];
432f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		rep->mul_bssid->subelem_len = elen - 1;
433f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
43444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
43544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
43644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
43744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
43844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
43944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
44044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      const u8 *pos, u8 len,
44144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      struct neighbor_report *rep)
44244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
44344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 left = len;
44444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
44544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (left < 13) {
44644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
44744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return;
44844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
44944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(rep->bssid, pos, ETH_ALEN);
45144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
45244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->regulatory_class = *(pos + 10);
45344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->channel_number = *(pos + 11);
45444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->phy_type = *(pos + 12);
45544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	pos += 13;
45744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	left -= 13;
45844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	while (left >= 2) {
46044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		u8 id, elen;
46144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
46244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		id = *pos++;
46344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		elen = *pos++;
464f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
465f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		left -= 2;
466f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		if (elen > left) {
467f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt			wpa_printf(MSG_DEBUG,
468f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt				   "WNM: Truncated neighbor report subelement");
469f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt			break;
470f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		}
47144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
472f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt		left -= elen;
47344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		pos += elen;
47444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
47544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
47644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
47744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
47844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
47944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 struct wpa_scan_results *scan_res,
48044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 struct neighbor_report *neigh_rep,
48144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 u8 num_neigh_rep, u8 *bssid_to_connect)
48244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
48344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
48444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 i, j;
48544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
4867d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt	if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss)
48744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return 0;
48844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
489fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
4907d5c8f257a74ac0d12828962a492e8b84ef83923Dmitry Shmidt		   MAC2STR(wpa_s->bssid), wpa_s->current_bss->level);
491fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt
49244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	for (i = 0; i < num_neigh_rep; i++) {
49344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		for (j = 0; j < scan_res->num; j++) {
49444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			/* Check for a better RSSI AP */
49544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			if (os_memcmp(scan_res->res[j]->bssid,
49644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
49744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			    scan_res->res[j]->level >
49844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			    wpa_s->current_bss->level) {
49944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				/* Got a BSSID with better RSSI value */
50044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
50144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  ETH_ALEN);
502fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR
503fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt					   " with better scan RSSI %d",
504fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt					   MAC2STR(scan_res->res[j]->bssid),
505fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt					   scan_res->res[j]->level);
50644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				return 1;
50744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			}
508fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt			wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR
509fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				   " RSSI %d", j,
510fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				   MAC2STR(scan_res->res[j]->bssid),
511fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				   scan_res->res[j]->level);
51244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
51344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
51444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
51544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return 0;
51644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
51744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
51844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
519f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidtstatic void wnm_send_bss_transition_mgmt_resp(
520f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	struct wpa_supplicant *wpa_s, u8 dialog_token,
521f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	enum bss_trans_mgmt_status_code status, u8 delay,
522f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	const u8 *target_bssid)
523a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
524a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 buf[1000], *pos;
525a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
526a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	size_t len;
527a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
528a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
529a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
530a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
531a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
532a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt = (struct ieee80211_mgmt *) buf;
533a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memset(&buf, 0, sizeof(buf));
534a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
535a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
536a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
537a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
538a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
539a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
540a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
541a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
542a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.status_code = status;
543a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
544a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	pos = mgmt->u.action.u.bss_tm_resp.variable;
545a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (target_bssid) {
546a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(pos, target_bssid, ETH_ALEN);
547a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		pos += ETH_ALEN;
548fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	} else if (status == WNM_BSS_TM_ACCEPT) {
549fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		/*
550fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		 * P802.11-REVmc clarifies that the Target BSSID field is always
551fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		 * present when status code is zero, so use a fake value here if
552fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		 * no BSSID is yet known.
553fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		 */
554fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		os_memset(pos, 0, ETH_ALEN);
555fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		pos += ETH_ALEN;
556a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
557a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
558a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	len = pos - (u8 *) &mgmt->u.action.category;
559a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
560a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
561a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    wpa_s->own_addr, wpa_s->bssid,
562a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    &mgmt->u.action.category, len, 0);
563a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
564a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
565a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
56644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtvoid wnm_scan_response(struct wpa_supplicant *wpa_s,
56744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		       struct wpa_scan_results *scan_res)
56844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
56944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 bssid[ETH_ALEN];
57044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
57144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (scan_res == NULL) {
57244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_ERROR, "Scan result is NULL");
57344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		goto send_bss_resp_fail;
57444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
57544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
57644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	/* Compare the Neighbor Report and scan results */
57744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (compare_scan_neighbor_results(wpa_s, scan_res,
57844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  wpa_s->wnm_neighbor_report_elements,
57944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  wpa_s->wnm_num_neighbor_report,
58044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  bssid) == 1) {
58144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		/* Associate to the network */
58244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		struct wpa_bss *bss;
58344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		struct wpa_ssid *ssid = wpa_s->current_ssid;
58444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
58544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		bss = wpa_bss_get_bssid(wpa_s, bssid);
58644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (!bss) {
58744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
58844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "BSS table");
58944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			goto send_bss_resp_fail;
59044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
59144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
59244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		/* Send the BSS Management Response - Accept */
59344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_reply) {
59444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wnm_send_bss_transition_mgmt_resp(wpa_s,
59544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
596f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  WNM_BSS_TM_ACCEPT,
597fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt						  0, bssid);
59844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
59944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
60044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->reassociate = 1;
60144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_supplicant_connect(wpa_s, bss, ssid);
60244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_deallocate_memory(wpa_s);
60344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return;
60444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
60544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
60644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	/* Send reject response for all the failures */
60744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtsend_bss_resp_fail:
60844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wnm_deallocate_memory(wpa_s);
60944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (wpa_s->wnm_reply) {
61044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_send_bss_transition_mgmt_resp(wpa_s,
61144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
612f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  WNM_BSS_TM_REJECT_UNSPECIFIED,
61344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  0, NULL);
61444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
61544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return;
61644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
61744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
61844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
619a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
620a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					     const u8 *pos, const u8 *end,
621a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					     int reply)
622a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
623a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (pos + 5 > end)
624a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
625a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
62644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_dialog_token = pos[0];
62744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_mode = pos[1];
62844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
62944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_validity_interval = pos[4];
63044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_reply = reply;
631a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
632a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
633a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "dialog_token=%u request_mode=0x%x "
634a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "disassoc_timer=%u validity_interval=%u",
63544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
63644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
63744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
638a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	pos += 5;
63944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
640f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
64144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (pos + 12 > end) {
64244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
64344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			return;
64444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
64544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
646a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		pos += 12; /* BSS Termination Duration */
64744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
64844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
649f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
650a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		char url[256];
651f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		unsigned int beacon_int;
652f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
653a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (pos + 1 > end || pos + 1 + pos[0] > end) {
654a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
655a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				   "Management Request (URL)");
656a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return;
657a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
658a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(url, pos + 1, pos[0]);
659a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		url[pos[0]] = '\0';
66044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		pos += 1 + pos[0];
661f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
662f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		if (wpa_s->current_bss)
663f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			beacon_int = wpa_s->current_bss->beacon_int;
664f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		else
665f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			beacon_int = 100; /* best guess */
666f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
667f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
668f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_sm_pmf_enabled(wpa_s->wpa),
669f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
670a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
671a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
672f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
673a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
67444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
67544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
676a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			/* TODO: mark current BSS less preferred for
677a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 * selection */
678a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
679a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_supplicant_req_scan(wpa_s, 0, 0);
680a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
681a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
682a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
683f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
68444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
68544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->wnm_num_neighbor_report = 0;
68644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements);
68744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->wnm_neighbor_report_elements = os_zalloc(
68844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			WNM_MAX_NEIGHBOR_REPORT *
68944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			sizeof(struct neighbor_report));
69044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_neighbor_report_elements == NULL)
69144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			return;
69244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
69344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		while (pos + 2 <= end &&
69444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
69544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		{
69644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			u8 tag = *pos++;
69744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			u8 len = *pos++;
69844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
69944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
70044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   tag);
70144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			if (pos + len > end) {
70244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
70344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				return;
70444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			}
705f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
706f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt				struct neighbor_report *rep;
707f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt				rep = &wpa_s->wnm_neighbor_report_elements[
708f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt					wpa_s->wnm_num_neighbor_report];
709f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
710f940fbdc849eba19de7b63a74ced85e550bf4572Dmitry Shmidt			}
71144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
71244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			pos += len;
71344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_s->wnm_num_neighbor_report++;
71444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
71544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
71644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->scan_res_handler = wnm_scan_response;
71744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_supplicant_req_scan(wpa_s, 0, 0);
71844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	} else if (reply) {
719f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		enum bss_trans_mgmt_status_code status;
720f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
721f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			status = WNM_BSS_TM_ACCEPT;
722f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		else {
723f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
724f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
725f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		}
72644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_send_bss_transition_mgmt_resp(wpa_s,
72744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
728f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  status, 0, NULL);
729a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
730a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
731a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
732a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
73344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtint wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
73444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				       u8 query_reason)
73544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
73644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 buf[1000], *pos;
73744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
73844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	size_t len;
73944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	int ret;
74044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
74144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
74244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   MACSTR " query_reason=%u",
74344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   MAC2STR(wpa_s->bssid), query_reason);
74444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
74544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt = (struct ieee80211_mgmt *) buf;
74644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memset(&buf, 0, sizeof(buf));
74744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
74844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
74944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
75044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
75144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
75244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
75344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
754fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.dialog_token = 1;
75544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
75644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	pos = mgmt->u.action.u.bss_tm_query.variable;
75744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
75844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	len = pos - (u8 *) &mgmt->u.action.category;
75944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
76044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
76144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				  wpa_s->own_addr, wpa_s->bssid,
76244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				  &mgmt->u.action.category, len, 0);
76344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
76444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return ret;
76544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
76644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
76744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
768f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidtstatic void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
769f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					    const u8 *sa, const u8 *data,
770f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					    int len)
771f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt{
772f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	const u8 *pos, *end, *next;
773f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	u8 ie, ie_len;
774f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
775f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	pos = data;
776f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	end = data + len;
777f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
778f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	while (pos + 1 < end) {
779f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		ie = *pos++;
780f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		ie_len = *pos++;
781f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
782f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			   ie, ie_len);
783f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		if (ie_len > end - pos) {
784f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
785f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				   "subelement");
786f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			break;
787f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		}
788f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		next = pos + ie_len;
789f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		if (ie_len < 4) {
790f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos = next;
791f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			continue;
792f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		}
793f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
794f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			   WPA_GET_BE24(pos), pos[3]);
795f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
796f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt#ifdef CONFIG_HS20
797f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
798f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		    WPA_GET_BE24(pos) == OUI_WFA &&
799f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		    pos[3] == HS20_WNM_SUB_REM_NEEDED) {
800f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			/* Subscription Remediation subelement */
801f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			const u8 *ie_end;
802f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			u8 url_len;
803f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			char *url;
804f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			u8 osu_method;
805f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
806f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
807f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				   "subelement");
808f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			ie_end = pos + ie_len;
809f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos += 4;
810f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			url_len = *pos++;
811f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			if (url_len == 0) {
812f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
813f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				url = NULL;
814f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				osu_method = 1;
815f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			} else {
816f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				if (pos + url_len + 1 > ie_end) {
817f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
818f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt						   url_len,
819f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt						   (int) (ie_end - pos));
820f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					break;
821f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				}
822f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				url = os_malloc(url_len + 1);
823f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				if (url == NULL)
824f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					break;
825f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				os_memcpy(url, pos, url_len);
826f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				url[url_len] = '\0';
827f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				osu_method = pos[url_len];
828f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			}
829f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			hs20_rx_subscription_remediation(wpa_s, url,
830f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt							 osu_method);
831f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			os_free(url);
832f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos = next;
833f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			continue;
834f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		}
835f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
836f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
837f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		    WPA_GET_BE24(pos) == OUI_WFA &&
838f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		    pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
839f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			const u8 *ie_end;
840f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			u8 url_len;
841f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			char *url;
842f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			u8 code;
843f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			u16 reauth_delay;
844f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
845f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			ie_end = pos + ie_len;
846f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos += 4;
847f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			code = *pos++;
848f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			reauth_delay = WPA_GET_LE16(pos);
849f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos += 2;
850f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			url_len = *pos++;
851f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
852f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				   "Imminent - Reason Code %u   "
853f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				   "Re-Auth Delay %u  URL Length %u",
854f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				   code, reauth_delay, url_len);
855f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			if (pos + url_len > ie_end)
856f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				break;
857f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			url = os_malloc(url_len + 1);
858f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			if (url == NULL)
859f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt				break;
860f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			os_memcpy(url, pos, url_len);
861f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			url[url_len] = '\0';
862f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			hs20_rx_deauth_imminent_notice(wpa_s, code,
863f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt						       reauth_delay, url);
864f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			os_free(url);
865f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			pos = next;
866f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			continue;
867f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		}
868f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt#endif /* CONFIG_HS20 */
869f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
870f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		pos = next;
871f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	}
872f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt}
873f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
874f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
875f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidtstatic void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
876f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt					const u8 *sa, const u8 *frm, int len)
877f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt{
878f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	const u8 *pos, *end;
879f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	u8 dialog_token, type;
880f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
881f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	/* Dialog Token [1] | Type [1] | Subelements */
882f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
883f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	if (len < 2 || sa == NULL)
884f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		return;
885f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	end = frm + len;
886f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	pos = frm;
887f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	dialog_token = *pos++;
888f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	type = *pos++;
889f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
890f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
891f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		"(dialog_token %u type %u sa " MACSTR ")",
892f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		dialog_token, type, MAC2STR(sa));
893f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
894f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		    pos, end - pos);
895f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
896f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	if (wpa_s->wpa_state != WPA_COMPLETED ||
897f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	    os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
898f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
899f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			"from our AP - ignore it");
900f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		return;
901f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	}
902f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
903f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	switch (type) {
904f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	case 1:
905f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
906f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		break;
907f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	default:
908f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
909f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt			"WNM-Notification type %u", type);
910f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		break;
911f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	}
912f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt}
913f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
914f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt
91561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtvoid ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
916fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt			      const struct ieee80211_mgmt *mgmt, size_t len)
91761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
918a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	const u8 *pos, *end;
919a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 act;
920a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
921fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	if (len < IEEE80211_HDRLEN + 2)
922a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
923a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
924623d63a3a443027e50efdaaec027befcc3882527Dmitry Shmidt	pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
925a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	act = *pos++;
926fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	end = ((const u8 *) mgmt) + len;
927a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
928a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
929fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		   act, MAC2STR(mgmt->sa));
930a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
931fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	    os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
932a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
933a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			   "frame");
934a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
935a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
93661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
93761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (act) {
938a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	case WNM_BSS_TRANS_MGMT_REQ:
939a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
940fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt						 !(mgmt->da[0] & 0x01));
941a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		break;
94261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case WNM_SLEEP_MODE_RESP:
943fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
94461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
945f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt	case WNM_NOTIFICATION_REQ:
946f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
947f21452aea786ac056eb01f1cbba4f553bd502747Dmitry Shmidt		break;
94861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
94944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_ERROR, "WNM: Unknown request");
95061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
95161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
95261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
953