1/* 2 * hostapd - WNM 3 * Copyright (c) 2011-2014, 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 "utils/eloop.h" 13#include "common/ieee802_11_defs.h" 14#include "common/wpa_ctrl.h" 15#include "ap/hostapd.h" 16#include "ap/sta_info.h" 17#include "ap/ap_config.h" 18#include "ap/ap_drv_ops.h" 19#include "ap/wpa_auth.h" 20#include "mbo_ap.h" 21#include "wnm_ap.h" 22 23#define MAX_TFS_IE_LEN 1024 24 25 26/* get the TFS IE from driver */ 27static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, 28 u8 *buf, u16 *buf_len, enum wnm_oper oper) 29{ 30 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 31 32 return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); 33} 34 35 36/* set the TFS IE to driver */ 37static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, 38 u8 *buf, u16 *buf_len, enum wnm_oper oper) 39{ 40 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 41 42 return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); 43} 44 45 46/* MLME-SLEEPMODE.response */ 47static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, 48 const u8 *addr, u8 dialog_token, 49 u8 action_type, u16 intval) 50{ 51 struct ieee80211_mgmt *mgmt; 52 int res; 53 size_t len; 54 size_t gtk_elem_len = 0; 55 size_t igtk_elem_len = 0; 56 struct wnm_sleep_element wnmsleep_ie; 57 u8 *wnmtfs_ie; 58 u8 wnmsleep_ie_len; 59 u16 wnmtfs_ie_len; 60 u8 *pos; 61 struct sta_info *sta; 62 enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? 63 WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; 64 65 sta = ap_get_sta(hapd, addr); 66 if (sta == NULL) { 67 wpa_printf(MSG_DEBUG, "%s: station not found", __func__); 68 return -EINVAL; 69 } 70 71 /* WNM-Sleep Mode IE */ 72 os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); 73 wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 74 wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; 75 wnmsleep_ie.len = wnmsleep_ie_len - 2; 76 wnmsleep_ie.action_type = action_type; 77 wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; 78 wnmsleep_ie.intval = host_to_le16(intval); 79 80 /* TFS IE(s) */ 81 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 82 if (wnmtfs_ie == NULL) 83 return -1; 84 if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, 85 tfs_oper)) { 86 wnmtfs_ie_len = 0; 87 os_free(wnmtfs_ie); 88 wnmtfs_ie = NULL; 89 } 90 91#define MAX_GTK_SUBELEM_LEN 45 92#define MAX_IGTK_SUBELEM_LEN 26 93 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + 94 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); 95 if (mgmt == NULL) { 96 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 97 "WNM-Sleep Response action frame"); 98 os_free(wnmtfs_ie); 99 return -1; 100 } 101 os_memcpy(mgmt->da, addr, ETH_ALEN); 102 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 103 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 104 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 105 WLAN_FC_STYPE_ACTION); 106 mgmt->u.action.category = WLAN_ACTION_WNM; 107 mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; 108 mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; 109 pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; 110 /* add key data if MFP is enabled */ 111 if (!wpa_auth_uses_mfp(sta->wpa_sm) || 112 action_type != WNM_SLEEP_MODE_EXIT) { 113 mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; 114 } else { 115 gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); 116 pos += gtk_elem_len; 117 wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", 118 (int) gtk_elem_len); 119#ifdef CONFIG_IEEE80211W 120 res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); 121 if (res < 0) { 122 os_free(wnmtfs_ie); 123 os_free(mgmt); 124 return -1; 125 } 126 igtk_elem_len = res; 127 pos += igtk_elem_len; 128 wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", 129 (int) igtk_elem_len); 130#endif /* CONFIG_IEEE80211W */ 131 132 WPA_PUT_LE16((u8 *) 133 &mgmt->u.action.u.wnm_sleep_resp.keydata_len, 134 gtk_elem_len + igtk_elem_len); 135 } 136 os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); 137 /* copy TFS IE here */ 138 pos += wnmsleep_ie_len; 139 if (wnmtfs_ie) 140 os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); 141 142 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + 143 igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; 144 145 /* In driver, response frame should be forced to sent when STA is in 146 * PS mode */ 147 res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, 148 mgmt->da, &mgmt->u.action.category, len); 149 150 if (!res) { 151 wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " 152 "frame"); 153 154 /* when entering wnmsleep 155 * 1. pause the node in driver 156 * 2. mark the node so that AP won't update GTK/IGTK during 157 * WNM Sleep 158 */ 159 if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && 160 wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { 161 sta->flags |= WLAN_STA_WNM_SLEEP_MODE; 162 hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, 163 addr, NULL, NULL); 164 wpa_set_wnmsleep(sta->wpa_sm, 1); 165 } 166 /* when exiting wnmsleep 167 * 1. unmark the node 168 * 2. start GTK/IGTK update if MFP is not used 169 * 3. unpause the node in driver 170 */ 171 if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || 172 wnmsleep_ie.status == 173 WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && 174 wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { 175 sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; 176 wpa_set_wnmsleep(sta->wpa_sm, 0); 177 hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, 178 addr, NULL, NULL); 179 if (!wpa_auth_uses_mfp(sta->wpa_sm)) 180 wpa_wnmsleep_rekey_gtk(sta->wpa_sm); 181 } 182 } else 183 wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); 184 185#undef MAX_GTK_SUBELEM_LEN 186#undef MAX_IGTK_SUBELEM_LEN 187 os_free(wnmtfs_ie); 188 os_free(mgmt); 189 return res; 190} 191 192 193static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, 194 const u8 *addr, const u8 *frm, int len) 195{ 196 /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ 197 const u8 *pos = frm; 198 u8 dialog_token; 199 struct wnm_sleep_element *wnmsleep_ie = NULL; 200 /* multiple TFS Req IE (assuming consecutive) */ 201 u8 *tfsreq_ie_start = NULL; 202 u8 *tfsreq_ie_end = NULL; 203 u16 tfsreq_ie_len = 0; 204 205 dialog_token = *pos++; 206 while (pos + 1 < frm + len) { 207 u8 ie_len = pos[1]; 208 if (pos + 2 + ie_len > frm + len) 209 break; 210 if (*pos == WLAN_EID_WNMSLEEP) 211 wnmsleep_ie = (struct wnm_sleep_element *) pos; 212 else if (*pos == WLAN_EID_TFS_REQ) { 213 if (!tfsreq_ie_start) 214 tfsreq_ie_start = (u8 *) pos; 215 tfsreq_ie_end = (u8 *) pos; 216 } else 217 wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", 218 *pos); 219 pos += ie_len + 2; 220 } 221 222 if (!wnmsleep_ie) { 223 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 224 return; 225 } 226 227 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && 228 tfsreq_ie_start && tfsreq_ie_end && 229 tfsreq_ie_end - tfsreq_ie_start >= 0) { 230 tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - 231 tfsreq_ie_start; 232 wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); 233 /* pass the TFS Req IE(s) to driver for processing */ 234 if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, 235 &tfsreq_ie_len, 236 WNM_SLEEP_TFS_REQ_IE_SET)) 237 wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); 238 } 239 240 ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, 241 wnmsleep_ie->action_type, 242 le_to_host16(wnmsleep_ie->intval)); 243 244 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 245 /* clear the tfs after sending the resp frame */ 246 ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, 247 &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); 248 } 249} 250 251 252static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, 253 const u8 *addr, 254 u8 dialog_token, 255 const char *url) 256{ 257 struct ieee80211_mgmt *mgmt; 258 size_t url_len, len; 259 u8 *pos; 260 int res; 261 262 if (url) 263 url_len = os_strlen(url); 264 else 265 url_len = 0; 266 267 mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); 268 if (mgmt == NULL) 269 return -1; 270 os_memcpy(mgmt->da, addr, ETH_ALEN); 271 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 272 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 273 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 274 WLAN_FC_STYPE_ACTION); 275 mgmt->u.action.category = WLAN_ACTION_WNM; 276 mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 277 mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; 278 mgmt->u.action.u.bss_tm_req.req_mode = 0; 279 mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); 280 mgmt->u.action.u.bss_tm_req.validity_interval = 1; 281 pos = mgmt->u.action.u.bss_tm_req.variable; 282 if (url) { 283 *pos++ += url_len; 284 os_memcpy(pos, url, url_len); 285 pos += url_len; 286 } 287 288 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " 289 MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " 290 "validity_interval=%u", 291 MAC2STR(addr), dialog_token, 292 mgmt->u.action.u.bss_tm_req.req_mode, 293 le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), 294 mgmt->u.action.u.bss_tm_req.validity_interval); 295 296 len = pos - &mgmt->u.action.category; 297 res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, 298 mgmt->da, &mgmt->u.action.category, len); 299 os_free(mgmt); 300 return res; 301} 302 303 304static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, 305 const u8 *addr, const u8 *frm, 306 size_t len) 307{ 308 u8 dialog_token, reason; 309 const u8 *pos, *end; 310 311 if (len < 2) { 312 wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " 313 MACSTR, MAC2STR(addr)); 314 return; 315 } 316 317 pos = frm; 318 end = pos + len; 319 dialog_token = *pos++; 320 reason = *pos++; 321 322 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " 323 MACSTR " dialog_token=%u reason=%u", 324 MAC2STR(addr), dialog_token, reason); 325 326 wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", 327 pos, end - pos); 328 329 ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); 330} 331 332 333static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, 334 const u8 *addr, const u8 *frm, 335 size_t len) 336{ 337 u8 dialog_token, status_code, bss_termination_delay; 338 const u8 *pos, *end; 339 340 if (len < 3) { 341 wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " 342 MACSTR, MAC2STR(addr)); 343 return; 344 } 345 346 pos = frm; 347 end = pos + len; 348 dialog_token = *pos++; 349 status_code = *pos++; 350 bss_termination_delay = *pos++; 351 352 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " 353 MACSTR " dialog_token=%u status_code=%u " 354 "bss_termination_delay=%u", MAC2STR(addr), dialog_token, 355 status_code, bss_termination_delay); 356 357 if (status_code == WNM_BSS_TM_ACCEPT) { 358 if (end - pos < ETH_ALEN) { 359 wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); 360 return; 361 } 362 wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, 363 MAC2STR(pos)); 364 wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR 365 " status_code=%u bss_termination_delay=%u target_bssid=" 366 MACSTR, 367 MAC2STR(addr), status_code, bss_termination_delay, 368 MAC2STR(pos)); 369 pos += ETH_ALEN; 370 } else { 371 wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR 372 " status_code=%u bss_termination_delay=%u", 373 MAC2STR(addr), status_code, bss_termination_delay); 374 } 375 376 wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", 377 pos, end - pos); 378} 379 380 381static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, 382 const u8 *addr, const u8 *buf, 383 size_t len) 384{ 385 u8 dialog_token, type; 386 387 if (len < 2) 388 return; 389 dialog_token = *buf++; 390 type = *buf++; 391 len -= 2; 392 393 wpa_printf(MSG_DEBUG, 394 "WNM: Received WNM Notification Request frame from " 395 MACSTR " (dialog_token=%u type=%u)", 396 MAC2STR(addr), dialog_token, type); 397 wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements", 398 buf, len); 399 if (type == WLAN_EID_VENDOR_SPECIFIC) 400 mbo_ap_wnm_notification_req(hapd, addr, buf, len); 401} 402 403 404int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, 405 const struct ieee80211_mgmt *mgmt, size_t len) 406{ 407 u8 action; 408 const u8 *payload; 409 size_t plen; 410 411 if (len < IEEE80211_HDRLEN + 2) 412 return -1; 413 414 payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; 415 action = *payload++; 416 plen = len - IEEE80211_HDRLEN - 2; 417 418 switch (action) { 419 case WNM_BSS_TRANS_MGMT_QUERY: 420 ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, 421 plen); 422 return 0; 423 case WNM_BSS_TRANS_MGMT_RESP: 424 ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, 425 plen); 426 return 0; 427 case WNM_SLEEP_MODE_REQ: 428 ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); 429 return 0; 430 case WNM_NOTIFICATION_REQ: 431 ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, 432 plen); 433 return 0; 434 } 435 436 wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, 437 action, MAC2STR(mgmt->sa)); 438 return -1; 439} 440 441 442int wnm_send_disassoc_imminent(struct hostapd_data *hapd, 443 struct sta_info *sta, int disassoc_timer) 444{ 445 u8 buf[1000], *pos; 446 struct ieee80211_mgmt *mgmt; 447 448 os_memset(buf, 0, sizeof(buf)); 449 mgmt = (struct ieee80211_mgmt *) buf; 450 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 451 WLAN_FC_STYPE_ACTION); 452 os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 453 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 454 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 455 mgmt->u.action.category = WLAN_ACTION_WNM; 456 mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 457 mgmt->u.action.u.bss_tm_req.dialog_token = 1; 458 mgmt->u.action.u.bss_tm_req.req_mode = 459 WNM_BSS_TM_REQ_DISASSOC_IMMINENT; 460 mgmt->u.action.u.bss_tm_req.disassoc_timer = 461 host_to_le16(disassoc_timer); 462 mgmt->u.action.u.bss_tm_req.validity_interval = 0; 463 464 pos = mgmt->u.action.u.bss_tm_req.variable; 465 466 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " 467 MACSTR, disassoc_timer, MAC2STR(sta->addr)); 468 if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 469 wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " 470 "Management Request frame"); 471 return -1; 472 } 473 474 return 0; 475} 476 477 478static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta, 479 int disassoc_timer) 480{ 481 int timeout, beacon_int; 482 483 /* 484 * Prevent STA from reconnecting using cached PMKSA to force 485 * full authentication with the authentication server (which may 486 * decide to reject the connection), 487 */ 488 wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); 489 490 beacon_int = hapd->iconf->beacon_int; 491 if (beacon_int < 1) 492 beacon_int = 100; /* best guess */ 493 /* Calculate timeout in ms based on beacon_int in TU */ 494 timeout = disassoc_timer * beacon_int * 128 / 125; 495 wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR 496 " set to %d ms", MAC2STR(sta->addr), timeout); 497 498 sta->timeout_next = STA_DISASSOC_FROM_CLI; 499 eloop_cancel_timeout(ap_handle_timer, hapd, sta); 500 eloop_register_timeout(timeout / 1000, 501 timeout % 1000 * 1000, 502 ap_handle_timer, hapd, sta); 503} 504 505 506int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, 507 struct sta_info *sta, const char *url, 508 int disassoc_timer) 509{ 510 u8 buf[1000], *pos; 511 struct ieee80211_mgmt *mgmt; 512 size_t url_len; 513 514 os_memset(buf, 0, sizeof(buf)); 515 mgmt = (struct ieee80211_mgmt *) buf; 516 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 517 WLAN_FC_STYPE_ACTION); 518 os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 519 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 520 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 521 mgmt->u.action.category = WLAN_ACTION_WNM; 522 mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 523 mgmt->u.action.u.bss_tm_req.dialog_token = 1; 524 mgmt->u.action.u.bss_tm_req.req_mode = 525 WNM_BSS_TM_REQ_DISASSOC_IMMINENT | 526 WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; 527 mgmt->u.action.u.bss_tm_req.disassoc_timer = 528 host_to_le16(disassoc_timer); 529 mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; 530 531 pos = mgmt->u.action.u.bss_tm_req.variable; 532 533 /* Session Information URL */ 534 url_len = os_strlen(url); 535 if (url_len > 255) 536 return -1; 537 *pos++ = url_len; 538 os_memcpy(pos, url, url_len); 539 pos += url_len; 540 541 if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 542 wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " 543 "Management Request frame"); 544 return -1; 545 } 546 547 if (disassoc_timer) { 548 /* send disassociation frame after time-out */ 549 set_disassoc_timer(hapd, sta, disassoc_timer); 550 } 551 552 return 0; 553} 554 555 556int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, 557 u8 req_mode, int disassoc_timer, u8 valid_int, 558 const u8 *bss_term_dur, const char *url, 559 const u8 *nei_rep, size_t nei_rep_len, 560 const u8 *mbo_attrs, size_t mbo_len) 561{ 562 u8 *buf, *pos; 563 struct ieee80211_mgmt *mgmt; 564 size_t url_len; 565 566 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " 567 MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", 568 MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); 569 buf = os_zalloc(1000 + nei_rep_len + mbo_len); 570 if (buf == NULL) 571 return -1; 572 mgmt = (struct ieee80211_mgmt *) buf; 573 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 574 WLAN_FC_STYPE_ACTION); 575 os_memcpy(mgmt->da, sta->addr, ETH_ALEN); 576 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 577 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 578 mgmt->u.action.category = WLAN_ACTION_WNM; 579 mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; 580 mgmt->u.action.u.bss_tm_req.dialog_token = 1; 581 mgmt->u.action.u.bss_tm_req.req_mode = req_mode; 582 mgmt->u.action.u.bss_tm_req.disassoc_timer = 583 host_to_le16(disassoc_timer); 584 mgmt->u.action.u.bss_tm_req.validity_interval = valid_int; 585 586 pos = mgmt->u.action.u.bss_tm_req.variable; 587 588 if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) && 589 bss_term_dur) { 590 os_memcpy(pos, bss_term_dur, 12); 591 pos += 12; 592 } 593 594 if (url) { 595 /* Session Information URL */ 596 url_len = os_strlen(url); 597 if (url_len > 255) { 598 os_free(buf); 599 return -1; 600 } 601 602 *pos++ = url_len; 603 os_memcpy(pos, url, url_len); 604 pos += url_len; 605 } 606 607 if (nei_rep) { 608 os_memcpy(pos, nei_rep, nei_rep_len); 609 pos += nei_rep_len; 610 } 611 612 if (mbo_len > 0) { 613 pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs, 614 mbo_len); 615 } 616 617 if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { 618 wpa_printf(MSG_DEBUG, 619 "Failed to send BSS Transition Management Request frame"); 620 os_free(buf); 621 return -1; 622 } 623 os_free(buf); 624 625 if (disassoc_timer) { 626 /* send disassociation frame after time-out */ 627 set_disassoc_timer(hapd, sta, disassoc_timer); 628 } 629 630 return 0; 631} 632