wnm_sta.c revision 324740
1/* 2 * wpa_supplicant - WNM 3 * Copyright (c) 2011-2012, 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 "rsn_supp/wpa.h" 14#include "wpa_supplicant_i.h" 15#include "driver_i.h" 16#include "scan.h" 17 18#define MAX_TFS_IE_LEN 1024 19 20 21/* get the TFS IE from driver */ 22static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, 23 u16 *buf_len, enum wnm_oper oper) 24{ 25 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 26 27 return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); 28} 29 30 31/* set the TFS IE to driver */ 32static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, 33 const u8 *addr, u8 *buf, u16 *buf_len, 34 enum wnm_oper oper) 35{ 36 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 37 38 return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); 39} 40 41 42/* MLME-SLEEPMODE.request */ 43int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, 44 u8 action, u16 intval, struct wpabuf *tfs_req) 45{ 46 struct ieee80211_mgmt *mgmt; 47 int res; 48 size_t len; 49 struct wnm_sleep_element *wnmsleep_ie; 50 u8 *wnmtfs_ie; 51 u8 wnmsleep_ie_len; 52 u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ 53 enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : 54 WNM_SLEEP_TFS_REQ_IE_NONE; 55 56 wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " 57 "action=%s to " MACSTR, 58 action == 0 ? "enter" : "exit", 59 MAC2STR(wpa_s->bssid)); 60 61 /* WNM-Sleep Mode IE */ 62 wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 63 wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); 64 if (wnmsleep_ie == NULL) 65 return -1; 66 wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; 67 wnmsleep_ie->len = wnmsleep_ie_len - 2; 68 wnmsleep_ie->action_type = action; 69 wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; 70 wnmsleep_ie->intval = host_to_le16(intval); 71 wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", 72 (u8 *) wnmsleep_ie, wnmsleep_ie_len); 73 74 /* TFS IE(s) */ 75 if (tfs_req) { 76 wnmtfs_ie_len = wpabuf_len(tfs_req); 77 wnmtfs_ie = os_malloc(wnmtfs_ie_len); 78 if (wnmtfs_ie == NULL) { 79 os_free(wnmsleep_ie); 80 return -1; 81 } 82 os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); 83 } else { 84 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 85 if (wnmtfs_ie == NULL) { 86 os_free(wnmsleep_ie); 87 return -1; 88 } 89 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, 90 tfs_oper)) { 91 wnmtfs_ie_len = 0; 92 os_free(wnmtfs_ie); 93 wnmtfs_ie = NULL; 94 } 95 } 96 wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", 97 (u8 *) wnmtfs_ie, wnmtfs_ie_len); 98 99 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); 100 if (mgmt == NULL) { 101 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 102 "WNM-Sleep Request action frame"); 103 os_free(wnmsleep_ie); 104 os_free(wnmtfs_ie); 105 return -1; 106 } 107 108 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 109 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 110 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 111 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 112 WLAN_FC_STYPE_ACTION); 113 mgmt->u.action.category = WLAN_ACTION_WNM; 114 mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; 115 mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; 116 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, 117 wnmsleep_ie_len); 118 /* copy TFS IE here */ 119 if (wnmtfs_ie_len > 0) { 120 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + 121 wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); 122 } 123 124 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + 125 wnmtfs_ie_len; 126 127 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 128 wpa_s->own_addr, wpa_s->bssid, 129 &mgmt->u.action.category, len, 0); 130 if (res < 0) 131 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " 132 "(action=%d, intval=%d)", action, intval); 133 else 134 wpa_s->wnmsleep_used = 1; 135 136 os_free(wnmsleep_ie); 137 os_free(wnmtfs_ie); 138 os_free(mgmt); 139 140 return res; 141} 142 143 144static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, 145 u8 *tfsresp_ie_start, 146 u8 *tfsresp_ie_end) 147{ 148 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, 149 wpa_s->bssid, NULL, NULL); 150 /* remove GTK/IGTK ?? */ 151 152 /* set the TFS Resp IE(s) */ 153 if (tfsresp_ie_start && tfsresp_ie_end && 154 tfsresp_ie_end - tfsresp_ie_start >= 0) { 155 u16 tfsresp_ie_len; 156 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - 157 tfsresp_ie_start; 158 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); 159 /* pass the TFS Resp IE(s) to driver for processing */ 160 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, 161 tfsresp_ie_start, 162 &tfsresp_ie_len, 163 WNM_SLEEP_TFS_RESP_IE_SET)) 164 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); 165 } 166} 167 168 169static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 170 const u8 *frm, u16 key_len_total) 171{ 172 u8 *ptr, *end; 173 u8 gtk_len; 174 175 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, 176 NULL, NULL); 177 178 /* Install GTK/IGTK */ 179 180 /* point to key data field */ 181 ptr = (u8 *) frm + 1 + 1 + 2; 182 end = ptr + key_len_total; 183 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); 184 185 if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { 186 wpa_msg(wpa_s, MSG_INFO, 187 "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); 188 return; 189 } 190 191 while (ptr + 1 < end) { 192 if (ptr + 2 + ptr[1] > end) { 193 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " 194 "length"); 195 if (end > ptr) { 196 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", 197 ptr, end - ptr); 198 } 199 break; 200 } 201 if (*ptr == WNM_SLEEP_SUBELEM_GTK) { 202 if (ptr[1] < 11 + 5) { 203 wpa_printf(MSG_DEBUG, "WNM: Too short GTK " 204 "subelem"); 205 break; 206 } 207 gtk_len = *(ptr + 4); 208 if (ptr[1] < 11 + gtk_len || 209 gtk_len < 5 || gtk_len > 32) { 210 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " 211 "subelem"); 212 break; 213 } 214 wpa_wnmsleep_install_key( 215 wpa_s->wpa, 216 WNM_SLEEP_SUBELEM_GTK, 217 ptr); 218 ptr += 13 + gtk_len; 219#ifdef CONFIG_IEEE80211W 220 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { 221 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { 222 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " 223 "subelem"); 224 break; 225 } 226 wpa_wnmsleep_install_key(wpa_s->wpa, 227 WNM_SLEEP_SUBELEM_IGTK, ptr); 228 ptr += 10 + WPA_IGTK_LEN; 229#endif /* CONFIG_IEEE80211W */ 230 } else 231 break; /* skip the loop */ 232 } 233} 234 235 236static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, 237 const u8 *frm, int len) 238{ 239 /* 240 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | 241 * WNM-Sleep Mode IE | TFS Response IE 242 */ 243 u8 *pos = (u8 *) frm; /* point to action field */ 244 u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); 245 struct wnm_sleep_element *wnmsleep_ie = NULL; 246 /* multiple TFS Resp IE (assuming consecutive) */ 247 u8 *tfsresp_ie_start = NULL; 248 u8 *tfsresp_ie_end = NULL; 249 250 if (!wpa_s->wnmsleep_used) { 251 wpa_printf(MSG_DEBUG, 252 "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); 253 return; 254 } 255 256 wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", 257 frm[0], frm[1], key_len_total); 258 pos += 4 + key_len_total; 259 if (pos > frm + len) { 260 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); 261 return; 262 } 263 while (pos - frm < len) { 264 u8 ie_len = *(pos + 1); 265 if (pos + 2 + ie_len > frm + len) { 266 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); 267 break; 268 } 269 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); 270 if (*pos == WLAN_EID_WNMSLEEP) 271 wnmsleep_ie = (struct wnm_sleep_element *) pos; 272 else if (*pos == WLAN_EID_TFS_RESP) { 273 if (!tfsresp_ie_start) 274 tfsresp_ie_start = pos; 275 tfsresp_ie_end = pos; 276 } else 277 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); 278 pos += ie_len + 2; 279 } 280 281 if (!wnmsleep_ie) { 282 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 283 return; 284 } 285 286 wpa_s->wnmsleep_used = 0; 287 288 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || 289 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { 290 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " 291 "frame (action=%d, intval=%d)", 292 wnmsleep_ie->action_type, wnmsleep_ie->intval); 293 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { 294 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, 295 tfsresp_ie_end); 296 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 297 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); 298 } 299 } else { 300 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " 301 "(action=%d, intval=%d)", 302 wnmsleep_ie->action_type, wnmsleep_ie->intval); 303 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) 304 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, 305 wpa_s->bssid, NULL, NULL); 306 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) 307 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, 308 wpa_s->bssid, NULL, NULL); 309 } 310} 311 312 313static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, 314 u8 dialog_token, u8 status, 315 u8 delay, const u8 *target_bssid) 316{ 317 u8 buf[1000], *pos; 318 struct ieee80211_mgmt *mgmt; 319 size_t len; 320 321 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " 322 "to " MACSTR " dialog_token=%u status=%u delay=%d", 323 MAC2STR(wpa_s->bssid), dialog_token, status, delay); 324 325 mgmt = (struct ieee80211_mgmt *) buf; 326 os_memset(&buf, 0, sizeof(buf)); 327 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 328 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 329 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 330 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 331 WLAN_FC_STYPE_ACTION); 332 mgmt->u.action.category = WLAN_ACTION_WNM; 333 mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; 334 mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; 335 mgmt->u.action.u.bss_tm_resp.status_code = status; 336 mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; 337 pos = mgmt->u.action.u.bss_tm_resp.variable; 338 if (target_bssid) { 339 os_memcpy(pos, target_bssid, ETH_ALEN); 340 pos += ETH_ALEN; 341 } 342 343 len = pos - (u8 *) &mgmt->u.action.category; 344 345 wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 346 wpa_s->own_addr, wpa_s->bssid, 347 &mgmt->u.action.category, len, 0); 348} 349 350 351static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 352 const u8 *pos, const u8 *end, 353 int reply) 354{ 355 u8 dialog_token; 356 u8 mode; 357 u16 disassoc_timer; 358 359 if (pos + 5 > end) 360 return; 361 362 dialog_token = pos[0]; 363 mode = pos[1]; 364 disassoc_timer = WPA_GET_LE16(pos + 2); 365 366 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " 367 "dialog_token=%u request_mode=0x%x " 368 "disassoc_timer=%u validity_interval=%u", 369 dialog_token, mode, disassoc_timer, pos[4]); 370 pos += 5; 371 if (mode & 0x08) 372 pos += 12; /* BSS Termination Duration */ 373 if (mode & 0x10) { 374 char url[256]; 375 if (pos + 1 > end || pos + 1 + pos[0] > end) { 376 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " 377 "Management Request (URL)"); 378 return; 379 } 380 os_memcpy(url, pos + 1, pos[0]); 381 url[pos[0]] = '\0'; 382 wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - " 383 "session_info_url=%s", url); 384 } 385 386 if (mode & 0x04) { 387 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " 388 "Disassociation Timer %u", disassoc_timer); 389 if (disassoc_timer && !wpa_s->scanning) { 390 /* TODO: mark current BSS less preferred for 391 * selection */ 392 wpa_printf(MSG_DEBUG, "Trying to find another BSS"); 393 wpa_supplicant_req_scan(wpa_s, 0, 0); 394 } 395 } 396 397 if (reply) { 398 /* TODO: add support for reporting Accept */ 399 wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, 400 1 /* Reject - unspecified */, 401 0, NULL); 402 } 403} 404 405 406void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, 407 struct rx_action *action) 408{ 409 const u8 *pos, *end; 410 u8 act; 411 412 if (action->data == NULL || action->len == 0) 413 return; 414 415 pos = action->data; 416 end = pos + action->len; 417 act = *pos++; 418 419 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, 420 act, MAC2STR(action->sa)); 421 if (wpa_s->wpa_state < WPA_ASSOCIATED || 422 os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) { 423 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " 424 "frame"); 425 return; 426 } 427 428 switch (act) { 429 case WNM_BSS_TRANS_MGMT_REQ: 430 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, 431 !(action->da[0] & 0x01)); 432 break; 433 case WNM_SLEEP_MODE_RESP: 434 ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); 435 break; 436 default: 437 break; 438 } 439} 440