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"
2161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt#define MAX_TFS_IE_LEN  1024
2344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt#define WNM_MAX_NEIGHBOR_REPORT 10
2461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
2661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* get the TFS IE from driver */
2761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
2861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   u16 *buf_len, enum wnm_oper oper)
2961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
3061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
3161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
3361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
3461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
3661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* set the TFS IE to driver */
3761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
3861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   const u8 *addr, u8 *buf, u16 *buf_len,
3961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				   enum wnm_oper oper)
4061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
4161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
4261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
4461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
4561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
4761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt/* MLME-SLEEPMODE.request */
4861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtint ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
49a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				 u8 action, u16 intval, struct wpabuf *tfs_req)
5061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
5161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
5261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	int res;
5361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	size_t len;
5461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct wnm_sleep_element *wnmsleep_ie;
5561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *wnmtfs_ie;
5661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 wnmsleep_ie_len;
5761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
5861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
5961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		WNM_SLEEP_TFS_REQ_IE_NONE;
6061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
61a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
62a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "action=%s to " MACSTR,
63a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   action == 0 ? "enter" : "exit",
64a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   MAC2STR(wpa_s->bssid));
65a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
6661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* WNM-Sleep Mode IE */
6761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
6861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
6961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (wnmsleep_ie == NULL)
7061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
7161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
7261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->len = wnmsleep_ie_len - 2;
7361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->action_type = action;
7461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
75a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wnmsleep_ie->intval = host_to_le16(intval);
76a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
77a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
7861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
7961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* TFS IE(s) */
80a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (tfs_req) {
81a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie_len = wpabuf_len(tfs_req);
82a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
83a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmtfs_ie == NULL) {
84a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmsleep_ie);
85a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return -1;
86a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
87a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
88a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	} else {
89a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
90a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmtfs_ie == NULL) {
91a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmsleep_ie);
92a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return -1;
93a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
94a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
95a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    tfs_oper)) {
96a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnmtfs_ie_len = 0;
97a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			os_free(wnmtfs_ie);
98a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnmtfs_ie = NULL;
99a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
10061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
101a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
102a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
10361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
10461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
10561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (mgmt == NULL) {
10661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
10761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "WNM-Sleep Request action frame");
108a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_free(wnmsleep_ie);
109a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_free(wnmtfs_ie);
11061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return -1;
11161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
11261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
11361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
11461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
11561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
11661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
11761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
11861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
11961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
120a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
12161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
12261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		  wnmsleep_ie_len);
12361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* copy TFS IE here */
12461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (wnmtfs_ie_len > 0) {
12561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
12661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
12761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
12861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
12961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wnmtfs_ie_len;
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  wpa_s->own_addr, wpa_s->bssid,
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				  &mgmt->u.action.category, len, 0);
13561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (res < 0)
13661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
13761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(action=%d, intval=%d)", action, intval);
13861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
13961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(wnmsleep_ie);
14061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(wnmtfs_ie);
14161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	os_free(mgmt);
14261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	return res;
14461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
14561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
14661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
147a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
148a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					 u8 *tfsresp_ie_start,
149a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					 u8 *tfsresp_ie_end)
150a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
151a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
152a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 wpa_s->bssid, NULL, NULL);
153a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* remove GTK/IGTK ?? */
154a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
155a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* set the TFS Resp IE(s) */
156a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (tfsresp_ie_start && tfsresp_ie_end &&
157a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
158a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		u16 tfsresp_ie_len;
159a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
160a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			tfsresp_ie_start;
161a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
162a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		/* pass the TFS Resp IE(s) to driver for processing */
163a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
164a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    tfsresp_ie_start,
165a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    &tfsresp_ie_len,
166a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    WNM_SLEEP_TFS_RESP_IE_SET))
167a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
168a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
169a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
170a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
171a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
172a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
173a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					const u8 *frm, u16 key_len_total)
174a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
175a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 *ptr, *end;
176a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 gtk_len;
177a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
178a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
179a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 NULL, NULL);
180a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
181a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* Install GTK/IGTK */
182a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
183a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	/* point to key data field */
184a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	ptr = (u8 *) frm + 1 + 1 + 2;
185a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	end = ptr + key_len_total;
186a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
187a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
188a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	while (ptr + 1 < end) {
189a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (ptr + 2 + ptr[1] > end) {
190a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
191a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				   "length");
192a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (end > ptr) {
193a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
194a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					    ptr, end - ptr);
195a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
196a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break;
197a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
198a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
199a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 11 + 5) {
200a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
201a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
202a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
203a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
204a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			gtk_len = *(ptr + 4);
205a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 11 + gtk_len ||
206a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    gtk_len < 5 || gtk_len > 32) {
207a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
208a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
209a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
210a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
211a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_wnmsleep_install_key(
212a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_s->wpa,
213a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				WNM_SLEEP_SUBELEM_GTK,
214a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				ptr);
215a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			ptr += 13 + gtk_len;
216a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#ifdef CONFIG_IEEE80211W
217a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
218a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
219a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
220a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   "subelem");
221a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				break;
222a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			}
223a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_wnmsleep_install_key(wpa_s->wpa,
224a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt						 WNM_SLEEP_SUBELEM_IGTK, ptr);
225a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			ptr += 10 + WPA_IGTK_LEN;
226a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt#endif /* CONFIG_IEEE80211W */
227a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else
228a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break; /* skip the loop */
229a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
230a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
231a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
232a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
23361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtstatic void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
23461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					const u8 *frm, int len)
23561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
23661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/*
23761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
23861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 * WNM-Sleep Mode IE | TFS Response IE
23961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	 */
24061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *pos = (u8 *) frm; /* point to action field */
24161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
24261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	struct wnm_sleep_element *wnmsleep_ie = NULL;
24361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	/* multiple TFS Resp IE (assuming consecutive) */
24461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *tfsresp_ie_start = NULL;
24561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	u8 *tfsresp_ie_end = NULL;
24661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
24761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
24861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		   frm[0], frm[1], key_len_total);
24961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	pos += 4 + key_len_total;
250a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (pos > frm + len) {
251a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
252a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
253a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
25461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	while (pos - frm < len) {
25561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		u8 ie_len = *(pos + 1);
256a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (pos + 2 + ie_len > frm + len) {
257a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
258a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			break;
259a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
260a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
26161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (*pos == WLAN_EID_WNMSLEEP)
26261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wnmsleep_ie = (struct wnm_sleep_element *) pos;
26361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		else if (*pos == WLAN_EID_TFS_RESP) {
26461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			if (!tfsresp_ie_start)
26561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt				tfsresp_ie_start = pos;
26661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			tfsresp_ie_end = pos;
26761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		} else
26861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
26961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		pos += ie_len + 2;
27061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
27161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
27261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (!wnmsleep_ie) {
27361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
27461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		return;
27561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
27661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
277a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
278a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
27961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
28061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "frame (action=%d, intval=%d)",
28161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
282a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
283a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
284a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt						     tfsresp_ie_end);
285a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
286a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
28761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
28861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	} else {
28961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
29061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   "(action=%d, intval=%d)",
29161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
292a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
29361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
29461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					 wpa_s->bssid, NULL, NULL);
295a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
29661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
29761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					 wpa_s->bssid, NULL, NULL);
29861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
29961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
30061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
30244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtvoid wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
30344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
30444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	int i;
30544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
30644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
30744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
30844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
30944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
31044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
31144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
31244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
31344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
31444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
31544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
31644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
31744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_free(wpa_s->wnm_neighbor_report_elements);
31844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_neighbor_report_elements = NULL;
31944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
32044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
32144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
32244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
32344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					   u8 id, u8 elen, const u8 *pos)
32444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
32544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	switch (id) {
32644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_TSF:
32744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2 + 2) {
32844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
32944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
33044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
33144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
33244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->tsf_info == NULL)
33344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
33444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->tsf_info->present = 1;
33544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
33644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
33744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
33844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
33944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2) {
34044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
34144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "country string");
34244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
34344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
34444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->con_coun_str =
34544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct condensed_country_string));
34644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->con_coun_str == NULL)
34744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
34844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->con_coun_str->present = 1;
34944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->con_coun_str->country_string, pos, 2);
35044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
35144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
35244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 1) {
35344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
35444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "candidate");
35544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
35644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
35744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_tran_can =
35844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct bss_transition_candidate));
35944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bss_tran_can == NULL)
36044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
36144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_tran_can->present = 1;
36244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_tran_can->preference = pos[0];
36344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
36444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
36544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 12) {
36644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
36744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "duration");
36844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
36944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
37044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_term_dur =
37144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct bss_termination_duration));
37244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bss_term_dur == NULL)
37344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
37444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bss_term_dur->present = 1;
37544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->bss_term_dur->duration, pos, 12);
37644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
37744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_BEARING:
37844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 8) {
37944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
38044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "bearing");
38144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
38244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
38344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bearing = os_zalloc(sizeof(struct bearing));
38444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->bearing == NULL)
38544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
38644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->bearing->present = 1;
38744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->bearing->bearing, pos, 8);
38844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
38944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
39044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2) {
39144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
39244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "pilot");
39344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
39444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
39544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
39644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->meas_pilot == NULL)
39744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
39844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot->present = 1;
39944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot->measurement_pilot = pos[0];
40044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->meas_pilot->num_vendor_specific = pos[1];
40144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
40244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
40344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
40444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 4) {
40544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
40644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "capabilities");
40744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
40844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
40944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->rrm_cap =
41044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			os_zalloc(sizeof(struct rrm_enabled_capabilities));
41144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->rrm_cap == NULL)
41244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
41344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->rrm_cap->present = 1;
41444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->rrm_cap->capabilities, pos, 4);
41544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
41644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	case WNM_NEIGHBOR_MULTIPLE_BSSID:
41744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (elen < 2) {
41844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
41944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
42044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
42144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
42244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (rep->mul_bssid == NULL)
42344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			break;
42444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid->present = 1;
42544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid->max_bssid_indicator = pos[0];
42644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		rep->mul_bssid->num_vendor_specific = pos[1];
42744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
42844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		break;
42944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
43044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
43144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
43244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
43344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
43444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      const u8 *pos, u8 len,
43544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      struct neighbor_report *rep)
43644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
43744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 left = len;
43844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
43944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (left < 13) {
44044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
44144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return;
44244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
44344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
44444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(rep->bssid, pos, ETH_ALEN);
44544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
44644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->regulatory_class = *(pos + 10);
44744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->channel_number = *(pos + 11);
44844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	rep->phy_type = *(pos + 12);
44944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	pos += 13;
45144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	left -= 13;
45244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	while (left >= 2) {
45444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		u8 id, elen;
45544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
45644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		id = *pos++;
45744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		elen = *pos++;
45844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
45944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		left -= 2 + elen;
46044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		pos += elen;
46144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
46244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
46344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
46444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
46544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtstatic int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
46644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 struct wpa_scan_results *scan_res,
46744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 struct neighbor_report *neigh_rep,
46844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					 u8 num_neigh_rep, u8 *bssid_to_connect)
46944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
47044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
47144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 i, j;
47244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
47344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (scan_res == NULL || num_neigh_rep == 0)
47444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return 0;
47544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
47644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	for (i = 0; i < num_neigh_rep; i++) {
47744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		for (j = 0; j < scan_res->num; j++) {
47844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			/* Check for a better RSSI AP */
47944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			if (os_memcmp(scan_res->res[j]->bssid,
48044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
48144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			    scan_res->res[j]->level >
48244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			    wpa_s->current_bss->level) {
48344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				/* Got a BSSID with better RSSI value */
48444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
48544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  ETH_ALEN);
48644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				return 1;
48744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			}
48844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
48944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
49044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
49144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return 0;
49244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
49344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
49444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
495f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidtstatic void wnm_send_bss_transition_mgmt_resp(
496f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	struct wpa_supplicant *wpa_s, u8 dialog_token,
497f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	enum bss_trans_mgmt_status_code status, u8 delay,
498f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	const u8 *target_bssid)
499a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
500a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 buf[1000], *pos;
501a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
502a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	size_t len;
503a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
504a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
505a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
506a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
507a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
508a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt = (struct ieee80211_mgmt *) buf;
509a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memset(&buf, 0, sizeof(buf));
510a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
511a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
512a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
513a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
514a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
515a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
516a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
517a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
518a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.status_code = status;
519a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
520a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	pos = mgmt->u.action.u.bss_tm_resp.variable;
521a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (target_bssid) {
522a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(pos, target_bssid, ETH_ALEN);
523a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		pos += ETH_ALEN;
524a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
525a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
526a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	len = pos - (u8 *) &mgmt->u.action.category;
527a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
528a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
529a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    wpa_s->own_addr, wpa_s->bssid,
530a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			    &mgmt->u.action.category, len, 0);
531a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
532a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
533a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
53444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtvoid wnm_scan_response(struct wpa_supplicant *wpa_s,
53544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		       struct wpa_scan_results *scan_res)
53644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
53744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 bssid[ETH_ALEN];
53844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
53944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (scan_res == NULL) {
54044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_ERROR, "Scan result is NULL");
54144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		goto send_bss_resp_fail;
54244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
54344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
54444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	/* Compare the Neighbor Report and scan results */
54544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (compare_scan_neighbor_results(wpa_s, scan_res,
54644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  wpa_s->wnm_neighbor_report_elements,
54744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  wpa_s->wnm_num_neighbor_report,
54844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					  bssid) == 1) {
54944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		/* Associate to the network */
55044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		struct wpa_bss *bss;
55144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		struct wpa_ssid *ssid = wpa_s->current_ssid;
55244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
55344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		bss = wpa_bss_get_bssid(wpa_s, bssid);
55444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (!bss) {
55544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
55644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   "BSS table");
55744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			goto send_bss_resp_fail;
55844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
55944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
56044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		/* Send the BSS Management Response - Accept */
56144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_reply) {
56244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wnm_send_bss_transition_mgmt_resp(wpa_s,
56344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
564f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  WNM_BSS_TM_ACCEPT,
56544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  0, NULL);
56644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
56744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
56844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->reassociate = 1;
56944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_supplicant_connect(wpa_s, bss, ssid);
57044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_deallocate_memory(wpa_s);
57144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		return;
57244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
57344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
57444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	/* Send reject response for all the failures */
57544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtsend_bss_resp_fail:
57644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wnm_deallocate_memory(wpa_s);
57744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	if (wpa_s->wnm_reply) {
57844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_send_bss_transition_mgmt_resp(wpa_s,
57944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
580f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  WNM_BSS_TM_REJECT_UNSPECIFIED,
58144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  0, NULL);
58244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
58344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return;
58444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
58544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
58644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
587a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidtstatic void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
588a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					     const u8 *pos, const u8 *end,
589a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt					     int reply)
590a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt{
591a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (pos + 5 > end)
592a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
593a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
59444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_dialog_token = pos[0];
59544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_mode = pos[1];
59644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
59744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_validity_interval = pos[4];
59844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_s->wnm_reply = reply;
599a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
600a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
601a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "dialog_token=%u request_mode=0x%x "
602a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   "disassoc_timer=%u validity_interval=%u",
60344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
60444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
60544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
606a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	pos += 5;
60744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
608f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
60944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (pos + 12 > end) {
61044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
61144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			return;
61244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
61344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
614a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		pos += 12; /* BSS Termination Duration */
61544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	}
61644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
617f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
618a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		char url[256];
619f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		unsigned int beacon_int;
620f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
621a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		if (pos + 1 > end || pos + 1 + pos[0] > end) {
622a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
623a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt				   "Management Request (URL)");
624a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			return;
625a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
626a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		os_memcpy(url, pos + 1, pos[0]);
627a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		url[pos[0]] = '\0';
62844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		pos += 1 + pos[0];
629f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
630f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		if (wpa_s->current_bss)
631f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			beacon_int = wpa_s->current_bss->beacon_int;
632f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		else
633f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			beacon_int = 100; /* best guess */
634f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt
635f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
636f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_sm_pmf_enabled(wpa_s->wpa),
637f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
638a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
639a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
640f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
641a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
64244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
64344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
644a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			/* TODO: mark current BSS less preferred for
645a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			 * selection */
646a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
647a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			wpa_supplicant_req_scan(wpa_s, 0, 0);
648a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		}
649a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
650a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
651f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
65244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
65344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->wnm_num_neighbor_report = 0;
65444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		os_free(wpa_s->wnm_neighbor_report_elements);
65544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->wnm_neighbor_report_elements = os_zalloc(
65644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			WNM_MAX_NEIGHBOR_REPORT *
65744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			sizeof(struct neighbor_report));
65844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		if (wpa_s->wnm_neighbor_report_elements == NULL)
65944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			return;
66044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
66144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		while (pos + 2 <= end &&
66244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
66344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		{
66444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			u8 tag = *pos++;
66544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			u8 len = *pos++;
66644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
66744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
66844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				   tag);
66944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			if (pos + len > end) {
67044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
67144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				return;
67244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			}
67344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wnm_parse_neighbor_report(
67444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				wpa_s, pos, len,
67544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				&wpa_s->wnm_neighbor_report_elements[
67644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					wpa_s->wnm_num_neighbor_report]);
67744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
67844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			pos += len;
67944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt			wpa_s->wnm_num_neighbor_report++;
68044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		}
68144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
68244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_s->scan_res_handler = wnm_scan_response;
68344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_supplicant_req_scan(wpa_s, 0, 0);
68444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	} else if (reply) {
685f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		enum bss_trans_mgmt_status_code status;
686f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
687f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			status = WNM_BSS_TM_ACCEPT;
688f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		else {
689f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
690f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
691f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt		}
69244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wnm_send_bss_transition_mgmt_resp(wpa_s,
69344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt						  wpa_s->wnm_dialog_token,
694f7e0a9905988e62e4f70fed8b795722abeab719bDmitry Shmidt						  status, 0, NULL);
695a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
696a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt}
697a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
698a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
69944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidtint wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
70044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				       u8 query_reason)
70144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt{
70244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	u8 buf[1000], *pos;
70344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	struct ieee80211_mgmt *mgmt;
70444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	size_t len;
70544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	int ret;
70644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
70744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
70844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   MACSTR " query_reason=%u",
70944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		   MAC2STR(wpa_s->bssid), query_reason);
71044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
71144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt = (struct ieee80211_mgmt *) buf;
71244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memset(&buf, 0, sizeof(buf));
71344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
71444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
71544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
71644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
71744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					   WLAN_FC_STYPE_ACTION);
71844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.category = WLAN_ACTION_WNM;
71944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
72044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.dialog_token = 0;
72144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
72244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	pos = mgmt->u.action.u.bss_tm_query.variable;
72344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
72444c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	len = pos - (u8 *) &mgmt->u.action.category;
72544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
72644c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
72744c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				  wpa_s->own_addr, wpa_s->bssid,
72844c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt				  &mgmt->u.action.category, len, 0);
72944c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
73044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt	return ret;
73144c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt}
73244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
73344c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt
73461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidtvoid ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
73561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			      struct rx_action *action)
73661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt{
737a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	const u8 *pos, *end;
738a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	u8 act;
739a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
740a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (action->data == NULL || action->len == 0)
741a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
742a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
743a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	pos = action->data;
744a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	end = pos + action->len;
745a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	act = *pos++;
746a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt
747a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
748a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		   act, MAC2STR(action->sa));
749a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
750a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	    os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
751a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
752a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt			   "frame");
753a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		return;
754a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	}
75561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
75661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	switch (act) {
757a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt	case WNM_BSS_TRANS_MGMT_REQ:
758a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
759a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt						 !(action->da[0] & 0x01));
760a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt		break;
76161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	case WNM_SLEEP_MODE_RESP:
76261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
76361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	default:
76544c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt		wpa_printf(MSG_ERROR, "WNM: Unknown request");
76661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		break;
76761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
76861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt}
769