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 */ 22 static 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 */ 32 static 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 */ 43 int 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 134 os_free(wnmsleep_ie); 135 os_free(wnmtfs_ie); 136 os_free(mgmt); 137 138 return res; 139 } 140 141 142 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, 143 u8 *tfsresp_ie_start, 144 u8 *tfsresp_ie_end) 145 { 146 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, 147 wpa_s->bssid, NULL, NULL); 148 /* remove GTK/IGTK ?? */ 149 150 /* set the TFS Resp IE(s) */ 151 if (tfsresp_ie_start && tfsresp_ie_end && 152 tfsresp_ie_end - tfsresp_ie_start >= 0) { 153 u16 tfsresp_ie_len; 154 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - 155 tfsresp_ie_start; 156 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); 157 /* pass the TFS Resp IE(s) to driver for processing */ 158 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, 159 tfsresp_ie_start, 160 &tfsresp_ie_len, 161 WNM_SLEEP_TFS_RESP_IE_SET)) 162 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); 163 } 164 } 165 166 167 static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 168 const u8 *frm, u16 key_len_total) 169 { 170 u8 *ptr, *end; 171 u8 gtk_len; 172 173 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, 174 NULL, NULL); 175 176 /* Install GTK/IGTK */ 177 178 /* point to key data field */ 179 ptr = (u8 *) frm + 1 + 1 + 2; 180 end = ptr + key_len_total; 181 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); 182 183 while (ptr + 1 < end) { 184 if (ptr + 2 + ptr[1] > end) { 185 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " 186 "length"); 187 if (end > ptr) { 188 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", 189 ptr, end - ptr); 190 } 191 break; 192 } 193 if (*ptr == WNM_SLEEP_SUBELEM_GTK) { 194 if (ptr[1] < 11 + 5) { 195 wpa_printf(MSG_DEBUG, "WNM: Too short GTK " 196 "subelem"); 197 break; 198 } 199 gtk_len = *(ptr + 4); 200 if (ptr[1] < 11 + gtk_len || 201 gtk_len < 5 || gtk_len > 32) { 202 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " 203 "subelem"); 204 break; 205 } 206 wpa_wnmsleep_install_key( 207 wpa_s->wpa, 208 WNM_SLEEP_SUBELEM_GTK, 209 ptr); 210 ptr += 13 + gtk_len; 211 #ifdef CONFIG_IEEE80211W 212 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { 213 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { 214 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " 215 "subelem"); 216 break; 217 } 218 wpa_wnmsleep_install_key(wpa_s->wpa, 219 WNM_SLEEP_SUBELEM_IGTK, ptr); 220 ptr += 10 + WPA_IGTK_LEN; 221 #endif /* CONFIG_IEEE80211W */ 222 } else 223 break; /* skip the loop */ 224 } 225 } 226 227 228 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, 229 const u8 *frm, int len) 230 { 231 /* 232 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | 233 * WNM-Sleep Mode IE | TFS Response IE 234 */ 235 u8 *pos = (u8 *) frm; /* point to action field */ 236 u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); 237 struct wnm_sleep_element *wnmsleep_ie = NULL; 238 /* multiple TFS Resp IE (assuming consecutive) */ 239 u8 *tfsresp_ie_start = NULL; 240 u8 *tfsresp_ie_end = NULL; 241 242 wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", 243 frm[0], frm[1], key_len_total); 244 pos += 4 + key_len_total; 245 if (pos > frm + len) { 246 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); 247 return; 248 } 249 while (pos - frm < len) { 250 u8 ie_len = *(pos + 1); 251 if (pos + 2 + ie_len > frm + len) { 252 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); 253 break; 254 } 255 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); 256 if (*pos == WLAN_EID_WNMSLEEP) 257 wnmsleep_ie = (struct wnm_sleep_element *) pos; 258 else if (*pos == WLAN_EID_TFS_RESP) { 259 if (!tfsresp_ie_start) 260 tfsresp_ie_start = pos; 261 tfsresp_ie_end = pos; 262 } else 263 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); 264 pos += ie_len + 2; 265 } 266 267 if (!wnmsleep_ie) { 268 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 269 return; 270 } 271 272 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || 273 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { 274 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " 275 "frame (action=%d, intval=%d)", 276 wnmsleep_ie->action_type, wnmsleep_ie->intval); 277 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { 278 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, 279 tfsresp_ie_end); 280 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 281 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); 282 } 283 } else { 284 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " 285 "(action=%d, intval=%d)", 286 wnmsleep_ie->action_type, wnmsleep_ie->intval); 287 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) 288 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, 289 wpa_s->bssid, NULL, NULL); 290 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) 291 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, 292 wpa_s->bssid, NULL, NULL); 293 } 294 } 295 296 297 static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, 298 u8 dialog_token, u8 status, 299 u8 delay, const u8 *target_bssid) 300 { 301 u8 buf[1000], *pos; 302 struct ieee80211_mgmt *mgmt; 303 size_t len; 304 305 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " 306 "to " MACSTR " dialog_token=%u status=%u delay=%d", 307 MAC2STR(wpa_s->bssid), dialog_token, status, delay); 308 309 mgmt = (struct ieee80211_mgmt *) buf; 310 os_memset(&buf, 0, sizeof(buf)); 311 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 312 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 313 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 314 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 315 WLAN_FC_STYPE_ACTION); 316 mgmt->u.action.category = WLAN_ACTION_WNM; 317 mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; 318 mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; 319 mgmt->u.action.u.bss_tm_resp.status_code = status; 320 mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; 321 pos = mgmt->u.action.u.bss_tm_resp.variable; 322 if (target_bssid) { 323 os_memcpy(pos, target_bssid, ETH_ALEN); 324 pos += ETH_ALEN; 325 } 326 327 len = pos - (u8 *) &mgmt->u.action.category; 328 329 wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 330 wpa_s->own_addr, wpa_s->bssid, 331 &mgmt->u.action.category, len, 0); 332 } 333 334 335 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 336 const u8 *pos, const u8 *end, 337 int reply) 338 { 339 u8 dialog_token; 340 u8 mode; 341 u16 disassoc_timer; 342 343 if (pos + 5 > end) 344 return; 345 346 dialog_token = pos[0]; 347 mode = pos[1]; 348 disassoc_timer = WPA_GET_LE16(pos + 2); 349 350 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " 351 "dialog_token=%u request_mode=0x%x " 352 "disassoc_timer=%u validity_interval=%u", 353 dialog_token, mode, disassoc_timer, pos[4]); 354 pos += 5; 355 if (mode & 0x08) 356 pos += 12; /* BSS Termination Duration */ 357 if (mode & 0x10) { 358 char url[256]; 359 if (pos + 1 > end || pos + 1 + pos[0] > end) { 360 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " 361 "Management Request (URL)"); 362 return; 363 } 364 os_memcpy(url, pos + 1, pos[0]); 365 url[pos[0]] = '\0'; 366 wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - " 367 "session_info_url=%s", url); 368 } 369 370 if (mode & 0x04) { 371 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " 372 "Disassociation Timer %u", disassoc_timer); 373 if (disassoc_timer && !wpa_s->scanning) { 374 /* TODO: mark current BSS less preferred for 375 * selection */ 376 wpa_printf(MSG_DEBUG, "Trying to find another BSS"); 377 wpa_supplicant_req_scan(wpa_s, 0, 0); 378 } 379 } 380 381 if (reply) { 382 /* TODO: add support for reporting Accept */ 383 wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, 384 1 /* Reject - unspecified */, 385 0, NULL); 386 } 387 } 388 389 390 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, 391 struct rx_action *action) 392 { 393 const u8 *pos, *end; 394 u8 act; 395 396 if (action->data == NULL || action->len == 0) 397 return; 398 399 pos = action->data; 400 end = pos + action->len; 401 act = *pos++; 402 403 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, 404 act, MAC2STR(action->sa)); 405 if (wpa_s->wpa_state < WPA_ASSOCIATED || 406 os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) { 407 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " 408 "frame"); 409 return; 410 } 411 412 switch (act) { 413 case WNM_BSS_TRANS_MGMT_REQ: 414 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, 415 !(action->da[0] & 0x01)); 416 break; 417 case WNM_SLEEP_MODE_RESP: 418 ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); 419 break; 420 default: 421 break; 422 } 423 } 424