Home | History | Annotate | Download | only in wpa_supplicant
      1 /*
      2  * wpa_supplicant - WNM
      3  * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "utils/includes.h"
     10 
     11 #include "utils/common.h"
     12 #include "common/ieee802_11_defs.h"
     13 #include "common/wpa_ctrl.h"
     14 #include "rsn_supp/wpa.h"
     15 #include "wpa_supplicant_i.h"
     16 #include "driver_i.h"
     17 #include "scan.h"
     18 #include "ctrl_iface.h"
     19 #include "bss.h"
     20 #include "wnm_sta.h"
     21 
     22 #define MAX_TFS_IE_LEN  1024
     23 #define WNM_MAX_NEIGHBOR_REPORT 10
     24 
     25 
     26 /* get the TFS IE from driver */
     27 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
     28 				   u16 *buf_len, enum wnm_oper oper)
     29 {
     30 	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
     31 
     32 	return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
     33 }
     34 
     35 
     36 /* set the TFS IE to driver */
     37 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
     38 				   const u8 *addr, u8 *buf, u16 *buf_len,
     39 				   enum wnm_oper oper)
     40 {
     41 	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
     42 
     43 	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
     44 }
     45 
     46 
     47 /* MLME-SLEEPMODE.request */
     48 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
     49 				 u8 action, u16 intval, struct wpabuf *tfs_req)
     50 {
     51 	struct ieee80211_mgmt *mgmt;
     52 	int res;
     53 	size_t len;
     54 	struct wnm_sleep_element *wnmsleep_ie;
     55 	u8 *wnmtfs_ie;
     56 	u8 wnmsleep_ie_len;
     57 	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
     58 	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
     59 		WNM_SLEEP_TFS_REQ_IE_NONE;
     60 
     61 	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
     62 		   "action=%s to " MACSTR,
     63 		   action == 0 ? "enter" : "exit",
     64 		   MAC2STR(wpa_s->bssid));
     65 
     66 	/* WNM-Sleep Mode IE */
     67 	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
     68 	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
     69 	if (wnmsleep_ie == NULL)
     70 		return -1;
     71 	wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
     72 	wnmsleep_ie->len = wnmsleep_ie_len - 2;
     73 	wnmsleep_ie->action_type = action;
     74 	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
     75 	wnmsleep_ie->intval = host_to_le16(intval);
     76 	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
     77 		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
     78 
     79 	/* TFS IE(s) */
     80 	if (tfs_req) {
     81 		wnmtfs_ie_len = wpabuf_len(tfs_req);
     82 		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
     83 		if (wnmtfs_ie == NULL) {
     84 			os_free(wnmsleep_ie);
     85 			return -1;
     86 		}
     87 		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
     88 	} else {
     89 		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
     90 		if (wnmtfs_ie == NULL) {
     91 			os_free(wnmsleep_ie);
     92 			return -1;
     93 		}
     94 		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
     95 					    tfs_oper)) {
     96 			wnmtfs_ie_len = 0;
     97 			os_free(wnmtfs_ie);
     98 			wnmtfs_ie = NULL;
     99 		}
    100 	}
    101 	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
    102 		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
    103 
    104 	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
    105 	if (mgmt == NULL) {
    106 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
    107 			   "WNM-Sleep Request action frame");
    108 		os_free(wnmsleep_ie);
    109 		os_free(wnmtfs_ie);
    110 		return -1;
    111 	}
    112 
    113 	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
    114 	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
    115 	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
    116 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
    117 					   WLAN_FC_STYPE_ACTION);
    118 	mgmt->u.action.category = WLAN_ACTION_WNM;
    119 	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
    120 	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
    121 	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
    122 		  wnmsleep_ie_len);
    123 	/* copy TFS IE here */
    124 	if (wnmtfs_ie_len > 0) {
    125 		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
    126 			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
    127 	}
    128 
    129 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
    130 		wnmtfs_ie_len;
    131 
    132 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
    133 				  wpa_s->own_addr, wpa_s->bssid,
    134 				  &mgmt->u.action.category, len, 0);
    135 	if (res < 0)
    136 		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
    137 			   "(action=%d, intval=%d)", action, intval);
    138 
    139 	os_free(wnmsleep_ie);
    140 	os_free(wnmtfs_ie);
    141 	os_free(mgmt);
    142 
    143 	return res;
    144 }
    145 
    146 
    147 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
    148 					 u8 *tfsresp_ie_start,
    149 					 u8 *tfsresp_ie_end)
    150 {
    151 	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
    152 			 wpa_s->bssid, NULL, NULL);
    153 	/* remove GTK/IGTK ?? */
    154 
    155 	/* set the TFS Resp IE(s) */
    156 	if (tfsresp_ie_start && tfsresp_ie_end &&
    157 	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
    158 		u16 tfsresp_ie_len;
    159 		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
    160 			tfsresp_ie_start;
    161 		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
    162 		/* pass the TFS Resp IE(s) to driver for processing */
    163 		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
    164 					    tfsresp_ie_start,
    165 					    &tfsresp_ie_len,
    166 					    WNM_SLEEP_TFS_RESP_IE_SET))
    167 			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
    168 	}
    169 }
    170 
    171 
    172 static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
    173 					const u8 *frm, u16 key_len_total)
    174 {
    175 	u8 *ptr, *end;
    176 	u8 gtk_len;
    177 
    178 	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
    179 			 NULL, NULL);
    180 
    181 	/* Install GTK/IGTK */
    182 
    183 	/* point to key data field */
    184 	ptr = (u8 *) frm + 1 + 1 + 2;
    185 	end = ptr + key_len_total;
    186 	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
    187 
    188 	while (ptr + 1 < end) {
    189 		if (ptr + 2 + ptr[1] > end) {
    190 			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
    191 				   "length");
    192 			if (end > ptr) {
    193 				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
    194 					    ptr, end - ptr);
    195 			}
    196 			break;
    197 		}
    198 		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
    199 			if (ptr[1] < 11 + 5) {
    200 				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
    201 					   "subelem");
    202 				break;
    203 			}
    204 			gtk_len = *(ptr + 4);
    205 			if (ptr[1] < 11 + gtk_len ||
    206 			    gtk_len < 5 || gtk_len > 32) {
    207 				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
    208 					   "subelem");
    209 				break;
    210 			}
    211 			wpa_wnmsleep_install_key(
    212 				wpa_s->wpa,
    213 				WNM_SLEEP_SUBELEM_GTK,
    214 				ptr);
    215 			ptr += 13 + gtk_len;
    216 #ifdef CONFIG_IEEE80211W
    217 		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
    218 			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
    219 				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
    220 					   "subelem");
    221 				break;
    222 			}
    223 			wpa_wnmsleep_install_key(wpa_s->wpa,
    224 						 WNM_SLEEP_SUBELEM_IGTK, ptr);
    225 			ptr += 10 + WPA_IGTK_LEN;
    226 #endif /* CONFIG_IEEE80211W */
    227 		} else
    228 			break; /* skip the loop */
    229 	}
    230 }
    231 
    232 
    233 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
    234 					const u8 *frm, int len)
    235 {
    236 	/*
    237 	 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
    238 	 * WNM-Sleep Mode IE | TFS Response IE
    239 	 */
    240 	u8 *pos = (u8 *) frm; /* point to action field */
    241 	u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
    242 	struct wnm_sleep_element *wnmsleep_ie = NULL;
    243 	/* multiple TFS Resp IE (assuming consecutive) */
    244 	u8 *tfsresp_ie_start = NULL;
    245 	u8 *tfsresp_ie_end = NULL;
    246 
    247 	wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
    248 		   frm[0], frm[1], key_len_total);
    249 	pos += 4 + key_len_total;
    250 	if (pos > frm + len) {
    251 		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
    252 		return;
    253 	}
    254 	while (pos - frm < len) {
    255 		u8 ie_len = *(pos + 1);
    256 		if (pos + 2 + ie_len > frm + len) {
    257 			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
    258 			break;
    259 		}
    260 		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
    261 		if (*pos == WLAN_EID_WNMSLEEP)
    262 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
    263 		else if (*pos == WLAN_EID_TFS_RESP) {
    264 			if (!tfsresp_ie_start)
    265 				tfsresp_ie_start = pos;
    266 			tfsresp_ie_end = pos;
    267 		} else
    268 			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
    269 		pos += ie_len + 2;
    270 	}
    271 
    272 	if (!wnmsleep_ie) {
    273 		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
    274 		return;
    275 	}
    276 
    277 	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
    278 	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
    279 		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
    280 			   "frame (action=%d, intval=%d)",
    281 			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
    282 		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
    283 			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
    284 						     tfsresp_ie_end);
    285 		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
    286 			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
    287 		}
    288 	} else {
    289 		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
    290 			   "(action=%d, intval=%d)",
    291 			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
    292 		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
    293 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
    294 					 wpa_s->bssid, NULL, NULL);
    295 		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
    296 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
    297 					 wpa_s->bssid, NULL, NULL);
    298 	}
    299 }
    300 
    301 
    302 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
    303 {
    304 	int i;
    305 
    306 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
    307 		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
    308 		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
    309 		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
    310 		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
    311 		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
    312 		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
    313 		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
    314 		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
    315 	}
    316 
    317 	os_free(wpa_s->wnm_neighbor_report_elements);
    318 	wpa_s->wnm_neighbor_report_elements = NULL;
    319 }
    320 
    321 
    322 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
    323 					   u8 id, u8 elen, const u8 *pos)
    324 {
    325 	switch (id) {
    326 	case WNM_NEIGHBOR_TSF:
    327 		if (elen < 2 + 2) {
    328 			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
    329 			break;
    330 		}
    331 		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
    332 		if (rep->tsf_info == NULL)
    333 			break;
    334 		rep->tsf_info->present = 1;
    335 		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
    336 		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
    337 		break;
    338 	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
    339 		if (elen < 2) {
    340 			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
    341 				   "country string");
    342 			break;
    343 		}
    344 		rep->con_coun_str =
    345 			os_zalloc(sizeof(struct condensed_country_string));
    346 		if (rep->con_coun_str == NULL)
    347 			break;
    348 		rep->con_coun_str->present = 1;
    349 		os_memcpy(rep->con_coun_str->country_string, pos, 2);
    350 		break;
    351 	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
    352 		if (elen < 1) {
    353 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
    354 				   "candidate");
    355 			break;
    356 		}
    357 		rep->bss_tran_can =
    358 			os_zalloc(sizeof(struct bss_transition_candidate));
    359 		if (rep->bss_tran_can == NULL)
    360 			break;
    361 		rep->bss_tran_can->present = 1;
    362 		rep->bss_tran_can->preference = pos[0];
    363 		break;
    364 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
    365 		if (elen < 12) {
    366 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
    367 				   "duration");
    368 			break;
    369 		}
    370 		rep->bss_term_dur =
    371 			os_zalloc(sizeof(struct bss_termination_duration));
    372 		if (rep->bss_term_dur == NULL)
    373 			break;
    374 		rep->bss_term_dur->present = 1;
    375 		os_memcpy(rep->bss_term_dur->duration, pos, 12);
    376 		break;
    377 	case WNM_NEIGHBOR_BEARING:
    378 		if (elen < 8) {
    379 			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
    380 				   "bearing");
    381 			break;
    382 		}
    383 		rep->bearing = os_zalloc(sizeof(struct bearing));
    384 		if (rep->bearing == NULL)
    385 			break;
    386 		rep->bearing->present = 1;
    387 		os_memcpy(rep->bearing->bearing, pos, 8);
    388 		break;
    389 	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
    390 		if (elen < 2) {
    391 			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
    392 				   "pilot");
    393 			break;
    394 		}
    395 		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
    396 		if (rep->meas_pilot == NULL)
    397 			break;
    398 		rep->meas_pilot->present = 1;
    399 		rep->meas_pilot->measurement_pilot = pos[0];
    400 		rep->meas_pilot->num_vendor_specific = pos[1];
    401 		os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
    402 		break;
    403 	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
    404 		if (elen < 4) {
    405 			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
    406 				   "capabilities");
    407 			break;
    408 		}
    409 		rep->rrm_cap =
    410 			os_zalloc(sizeof(struct rrm_enabled_capabilities));
    411 		if (rep->rrm_cap == NULL)
    412 			break;
    413 		rep->rrm_cap->present = 1;
    414 		os_memcpy(rep->rrm_cap->capabilities, pos, 4);
    415 		break;
    416 	case WNM_NEIGHBOR_MULTIPLE_BSSID:
    417 		if (elen < 2) {
    418 			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
    419 			break;
    420 		}
    421 		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
    422 		if (rep->mul_bssid == NULL)
    423 			break;
    424 		rep->mul_bssid->present = 1;
    425 		rep->mul_bssid->max_bssid_indicator = pos[0];
    426 		rep->mul_bssid->num_vendor_specific = pos[1];
    427 		os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
    428 		break;
    429 	}
    430 }
    431 
    432 
    433 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
    434 				      const u8 *pos, u8 len,
    435 				      struct neighbor_report *rep)
    436 {
    437 	u8 left = len;
    438 
    439 	if (left < 13) {
    440 		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
    441 		return;
    442 	}
    443 
    444 	os_memcpy(rep->bssid, pos, ETH_ALEN);
    445 	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
    446 	rep->regulatory_class = *(pos + 10);
    447 	rep->channel_number = *(pos + 11);
    448 	rep->phy_type = *(pos + 12);
    449 
    450 	pos += 13;
    451 	left -= 13;
    452 
    453 	while (left >= 2) {
    454 		u8 id, elen;
    455 
    456 		id = *pos++;
    457 		elen = *pos++;
    458 		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
    459 		left -= 2 + elen;
    460 		pos += elen;
    461 	}
    462 }
    463 
    464 
    465 static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
    466 					 struct wpa_scan_results *scan_res,
    467 					 struct neighbor_report *neigh_rep,
    468 					 u8 num_neigh_rep, u8 *bssid_to_connect)
    469 {
    470 
    471 	u8 i, j;
    472 
    473 	if (scan_res == NULL || num_neigh_rep == 0)
    474 		return 0;
    475 
    476 	for (i = 0; i < num_neigh_rep; i++) {
    477 		for (j = 0; j < scan_res->num; j++) {
    478 			/* Check for a better RSSI AP */
    479 			if (os_memcmp(scan_res->res[j]->bssid,
    480 				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
    481 			    scan_res->res[j]->level >
    482 			    wpa_s->current_bss->level) {
    483 				/* Got a BSSID with better RSSI value */
    484 				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
    485 					  ETH_ALEN);
    486 				return 1;
    487 			}
    488 		}
    489 	}
    490 
    491 	return 0;
    492 }
    493 
    494 
    495 static void wnm_send_bss_transition_mgmt_resp(
    496 	struct wpa_supplicant *wpa_s, u8 dialog_token,
    497 	enum bss_trans_mgmt_status_code status, u8 delay,
    498 	const u8 *target_bssid)
    499 {
    500 	u8 buf[1000], *pos;
    501 	struct ieee80211_mgmt *mgmt;
    502 	size_t len;
    503 
    504 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
    505 		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
    506 		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
    507 
    508 	mgmt = (struct ieee80211_mgmt *) buf;
    509 	os_memset(&buf, 0, sizeof(buf));
    510 	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
    511 	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
    512 	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
    513 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
    514 					   WLAN_FC_STYPE_ACTION);
    515 	mgmt->u.action.category = WLAN_ACTION_WNM;
    516 	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
    517 	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
    518 	mgmt->u.action.u.bss_tm_resp.status_code = status;
    519 	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
    520 	pos = mgmt->u.action.u.bss_tm_resp.variable;
    521 	if (target_bssid) {
    522 		os_memcpy(pos, target_bssid, ETH_ALEN);
    523 		pos += ETH_ALEN;
    524 	}
    525 
    526 	len = pos - (u8 *) &mgmt->u.action.category;
    527 
    528 	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
    529 			    wpa_s->own_addr, wpa_s->bssid,
    530 			    &mgmt->u.action.category, len, 0);
    531 }
    532 
    533 
    534 void wnm_scan_response(struct wpa_supplicant *wpa_s,
    535 		       struct wpa_scan_results *scan_res)
    536 {
    537 	u8 bssid[ETH_ALEN];
    538 
    539 	if (scan_res == NULL) {
    540 		wpa_printf(MSG_ERROR, "Scan result is NULL");
    541 		goto send_bss_resp_fail;
    542 	}
    543 
    544 	/* Compare the Neighbor Report and scan results */
    545 	if (compare_scan_neighbor_results(wpa_s, scan_res,
    546 					  wpa_s->wnm_neighbor_report_elements,
    547 					  wpa_s->wnm_num_neighbor_report,
    548 					  bssid) == 1) {
    549 		/* Associate to the network */
    550 		struct wpa_bss *bss;
    551 		struct wpa_ssid *ssid = wpa_s->current_ssid;
    552 
    553 		bss = wpa_bss_get_bssid(wpa_s, bssid);
    554 		if (!bss) {
    555 			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
    556 				   "BSS table");
    557 			goto send_bss_resp_fail;
    558 		}
    559 
    560 		/* Send the BSS Management Response - Accept */
    561 		if (wpa_s->wnm_reply) {
    562 			wnm_send_bss_transition_mgmt_resp(wpa_s,
    563 						  wpa_s->wnm_dialog_token,
    564 						  WNM_BSS_TM_ACCEPT,
    565 						  0, NULL);
    566 		}
    567 
    568 		wpa_s->reassociate = 1;
    569 		wpa_supplicant_connect(wpa_s, bss, ssid);
    570 		wnm_deallocate_memory(wpa_s);
    571 		return;
    572 	}
    573 
    574 	/* Send reject response for all the failures */
    575 send_bss_resp_fail:
    576 	wnm_deallocate_memory(wpa_s);
    577 	if (wpa_s->wnm_reply) {
    578 		wnm_send_bss_transition_mgmt_resp(wpa_s,
    579 						  wpa_s->wnm_dialog_token,
    580 						  WNM_BSS_TM_REJECT_UNSPECIFIED,
    581 						  0, NULL);
    582 	}
    583 	return;
    584 }
    585 
    586 
    587 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
    588 					     const u8 *pos, const u8 *end,
    589 					     int reply)
    590 {
    591 	if (pos + 5 > end)
    592 		return;
    593 
    594 	wpa_s->wnm_dialog_token = pos[0];
    595 	wpa_s->wnm_mode = pos[1];
    596 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
    597 	wpa_s->wnm_validity_interval = pos[4];
    598 	wpa_s->wnm_reply = reply;
    599 
    600 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
    601 		   "dialog_token=%u request_mode=0x%x "
    602 		   "disassoc_timer=%u validity_interval=%u",
    603 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
    604 		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
    605 
    606 	pos += 5;
    607 
    608 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
    609 		if (pos + 12 > end) {
    610 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
    611 			return;
    612 		}
    613 		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
    614 		pos += 12; /* BSS Termination Duration */
    615 	}
    616 
    617 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
    618 		char url[256];
    619 		unsigned int beacon_int;
    620 
    621 		if (pos + 1 > end || pos + 1 + pos[0] > end) {
    622 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
    623 				   "Management Request (URL)");
    624 			return;
    625 		}
    626 		os_memcpy(url, pos + 1, pos[0]);
    627 		url[pos[0]] = '\0';
    628 		pos += 1 + pos[0];
    629 
    630 		if (wpa_s->current_bss)
    631 			beacon_int = wpa_s->current_bss->beacon_int;
    632 		else
    633 			beacon_int = 100; /* best guess */
    634 
    635 		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
    636 			wpa_sm_pmf_enabled(wpa_s->wpa),
    637 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
    638 	}
    639 
    640 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
    641 		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
    642 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
    643 		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
    644 			/* TODO: mark current BSS less preferred for
    645 			 * selection */
    646 			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
    647 			wpa_supplicant_req_scan(wpa_s, 0, 0);
    648 		}
    649 	}
    650 
    651 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
    652 		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
    653 		wpa_s->wnm_num_neighbor_report = 0;
    654 		os_free(wpa_s->wnm_neighbor_report_elements);
    655 		wpa_s->wnm_neighbor_report_elements = os_zalloc(
    656 			WNM_MAX_NEIGHBOR_REPORT *
    657 			sizeof(struct neighbor_report));
    658 		if (wpa_s->wnm_neighbor_report_elements == NULL)
    659 			return;
    660 
    661 		while (pos + 2 <= end &&
    662 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
    663 		{
    664 			u8 tag = *pos++;
    665 			u8 len = *pos++;
    666 
    667 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
    668 				   tag);
    669 			if (pos + len > end) {
    670 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
    671 				return;
    672 			}
    673 			wnm_parse_neighbor_report(
    674 				wpa_s, pos, len,
    675 				&wpa_s->wnm_neighbor_report_elements[
    676 					wpa_s->wnm_num_neighbor_report]);
    677 
    678 			pos += len;
    679 			wpa_s->wnm_num_neighbor_report++;
    680 		}
    681 
    682 		wpa_s->scan_res_handler = wnm_scan_response;
    683 		wpa_supplicant_req_scan(wpa_s, 0, 0);
    684 	} else if (reply) {
    685 		enum bss_trans_mgmt_status_code status;
    686 		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
    687 			status = WNM_BSS_TM_ACCEPT;
    688 		else {
    689 			wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
    690 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
    691 		}
    692 		wnm_send_bss_transition_mgmt_resp(wpa_s,
    693 						  wpa_s->wnm_dialog_token,
    694 						  status, 0, NULL);
    695 	}
    696 }
    697 
    698 
    699 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
    700 				       u8 query_reason)
    701 {
    702 	u8 buf[1000], *pos;
    703 	struct ieee80211_mgmt *mgmt;
    704 	size_t len;
    705 	int ret;
    706 
    707 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
    708 		   MACSTR " query_reason=%u",
    709 		   MAC2STR(wpa_s->bssid), query_reason);
    710 
    711 	mgmt = (struct ieee80211_mgmt *) buf;
    712 	os_memset(&buf, 0, sizeof(buf));
    713 	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
    714 	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
    715 	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
    716 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
    717 					   WLAN_FC_STYPE_ACTION);
    718 	mgmt->u.action.category = WLAN_ACTION_WNM;
    719 	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
    720 	mgmt->u.action.u.bss_tm_query.dialog_token = 0;
    721 	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
    722 	pos = mgmt->u.action.u.bss_tm_query.variable;
    723 
    724 	len = pos - (u8 *) &mgmt->u.action.category;
    725 
    726 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
    727 				  wpa_s->own_addr, wpa_s->bssid,
    728 				  &mgmt->u.action.category, len, 0);
    729 
    730 	return ret;
    731 }
    732 
    733 
    734 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
    735 			      struct rx_action *action)
    736 {
    737 	const u8 *pos, *end;
    738 	u8 act;
    739 
    740 	if (action->data == NULL || action->len == 0)
    741 		return;
    742 
    743 	pos = action->data;
    744 	end = pos + action->len;
    745 	act = *pos++;
    746 
    747 	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
    748 		   act, MAC2STR(action->sa));
    749 	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
    750 	    os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
    751 		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
    752 			   "frame");
    753 		return;
    754 	}
    755 
    756 	switch (act) {
    757 	case WNM_BSS_TRANS_MGMT_REQ:
    758 		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
    759 						 !(action->da[0] & 0x01));
    760 		break;
    761 	case WNM_SLEEP_MODE_RESP:
    762 		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
    763 		break;
    764 	default:
    765 		wpa_printf(MSG_ERROR, "WNM: Unknown request");
    766 		break;
    767 	}
    768 }
    769