1 /* 2 * FST module - FST Session implementation 3 * Copyright (c) 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/defs.h" 14 #include "fst/fst_internal.h" 15 #include "fst/fst_defs.h" 16 #include "fst/fst_ctrl_iface.h" 17 #ifdef CONFIG_FST_TEST 18 #include "fst/fst_ctrl_defs.h" 19 #endif /* CONFIG_FST_TEST */ 20 21 #define US_80211_TU 1024 22 23 #define US_TO_TU(m) ((m) * / US_80211_TU) 24 #define TU_TO_US(m) ((m) * US_80211_TU) 25 26 #define FST_LLT_SWITCH_IMMEDIATELY 0 27 28 #define fst_printf_session(s, level, format, ...) \ 29 fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \ 30 (s)->id, (s)->data.fsts_id, \ 31 MAC2STR((s)->data.old_peer_addr), \ 32 MAC2STR((s)->data.new_peer_addr), \ 33 ##__VA_ARGS__) 34 35 #define fst_printf_siface(s, iface, level, format, ...) \ 36 fst_printf_session((s), (level), "%s: " format, \ 37 fst_iface_get_name(iface), ##__VA_ARGS__) 38 39 #define fst_printf_sframe(s, is_old, level, format, ...) \ 40 fst_printf_siface((s), \ 41 (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \ 42 (level), format, ##__VA_ARGS__) 43 44 #define FST_LLT_MS_DEFAULT 50 45 #define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL 46 47 const char * const fst_action_names[] = { 48 [FST_ACTION_SETUP_REQUEST] = "Setup Request", 49 [FST_ACTION_SETUP_RESPONSE] = "Setup Response", 50 [FST_ACTION_TEAR_DOWN] = "Tear Down", 51 [FST_ACTION_ACK_REQUEST] = "Ack Request", 52 [FST_ACTION_ACK_RESPONSE] = "Ack Response", 53 [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel", 54 }; 55 56 struct fst_session { 57 struct { 58 /* Session configuration that can be zeroed on reset */ 59 u8 old_peer_addr[ETH_ALEN]; 60 u8 new_peer_addr[ETH_ALEN]; 61 struct fst_iface *new_iface; 62 struct fst_iface *old_iface; 63 u32 llt_ms; 64 u8 pending_setup_req_dlgt; 65 u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147 66 * Session Transition element */ 67 } data; 68 /* Session object internal fields which won't be zeroed on reset */ 69 struct dl_list global_sessions_lentry; 70 u32 id; /* Session object ID used to identify 71 * specific session object */ 72 struct fst_group *group; 73 enum fst_session_state state; 74 Boolean stt_armed; 75 }; 76 77 static struct dl_list global_sessions_list; 78 static u32 global_session_id = 0; 79 80 #define foreach_fst_session(s) \ 81 dl_list_for_each((s), &global_sessions_list, \ 82 struct fst_session, global_sessions_lentry) 83 84 #define foreach_fst_session_safe(s, temp) \ 85 dl_list_for_each_safe((s), (temp), &global_sessions_list, \ 86 struct fst_session, global_sessions_lentry) 87 88 89 static void fst_session_global_inc_id(void) 90 { 91 global_session_id++; 92 if (global_session_id == FST_INVALID_SESSION_ID) 93 global_session_id++; 94 } 95 96 97 int fst_session_global_init(void) 98 { 99 dl_list_init(&global_sessions_list); 100 return 0; 101 } 102 103 104 void fst_session_global_deinit(void) 105 { 106 WPA_ASSERT(dl_list_empty(&global_sessions_list)); 107 } 108 109 110 static inline void fst_session_notify_ctrl(struct fst_session *s, 111 enum fst_event_type event_type, 112 union fst_event_extra *extra) 113 { 114 foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra); 115 } 116 117 118 static void fst_session_set_state(struct fst_session *s, 119 enum fst_session_state state, 120 union fst_session_state_switch_extra *extra) 121 { 122 if (s->state != state) { 123 union fst_event_extra evext = { 124 .session_state = { 125 .old_state = s->state, 126 .new_state = state, 127 }, 128 }; 129 130 if (extra) 131 evext.session_state.extra = *extra; 132 fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED, 133 &evext); 134 fst_printf_session(s, MSG_INFO, "State: %s => %s", 135 fst_session_state_name(s->state), 136 fst_session_state_name(state)); 137 s->state = state; 138 } 139 } 140 141 142 static u32 fst_find_free_session_id(void) 143 { 144 u32 i, id = FST_INVALID_SESSION_ID; 145 struct fst_session *s; 146 147 for (i = 0; i < (u32) -1; i++) { 148 Boolean in_use = FALSE; 149 150 foreach_fst_session(s) { 151 if (s->id == global_session_id) { 152 fst_session_global_inc_id(); 153 in_use = TRUE; 154 break; 155 } 156 } 157 if (!in_use) { 158 id = global_session_id; 159 fst_session_global_inc_id(); 160 break; 161 } 162 } 163 164 return id; 165 } 166 167 168 static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) 169 { 170 struct fst_session *s = user_ctx; 171 union fst_session_state_switch_extra extra = { 172 .to_initial = { 173 .reason = REASON_STT, 174 }, 175 }; 176 177 fst_printf_session(s, MSG_WARNING, "Session State Timeout"); 178 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra); 179 } 180 181 182 static void fst_session_stt_arm(struct fst_session *s) 183 { 184 /* Action frames sometimes get delayed. Use relaxed timeout (2*) */ 185 eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), 186 fst_session_timeout_handler, NULL, s); 187 s->stt_armed = TRUE; 188 } 189 190 191 static void fst_session_stt_disarm(struct fst_session *s) 192 { 193 if (s->stt_armed) { 194 eloop_cancel_timeout(fst_session_timeout_handler, NULL, s); 195 s->stt_armed = FALSE; 196 } 197 } 198 199 200 static Boolean fst_session_is_in_transition(struct fst_session *s) 201 { 202 /* See spec, 10.32.2.2 Transitioning between states */ 203 return s->stt_armed; 204 } 205 206 207 static int fst_session_is_in_progress(struct fst_session *s) 208 { 209 return s->state != FST_SESSION_STATE_INITIAL; 210 } 211 212 213 static int fst_session_is_ready_pending(struct fst_session *s) 214 { 215 return s->state == FST_SESSION_STATE_SETUP_COMPLETION && 216 fst_session_is_in_transition(s); 217 } 218 219 220 static int fst_session_is_ready(struct fst_session *s) 221 { 222 return s->state == FST_SESSION_STATE_SETUP_COMPLETION && 223 !fst_session_is_in_transition(s); 224 } 225 226 227 static int fst_session_is_switch_requested(struct fst_session *s) 228 { 229 return s->state == FST_SESSION_STATE_TRANSITION_DONE && 230 fst_session_is_in_transition(s); 231 } 232 233 234 static struct fst_session * 235 fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g) 236 { 237 struct fst_session *s; 238 239 foreach_fst_session(s) { 240 if (s->group == g && 241 (os_memcmp(s->data.old_peer_addr, peer_addr, 242 ETH_ALEN) == 0 || 243 os_memcmp(s->data.new_peer_addr, peer_addr, 244 ETH_ALEN) == 0) && 245 fst_session_is_in_progress(s)) 246 return s; 247 } 248 249 return NULL; 250 } 251 252 253 static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason) 254 { 255 union fst_session_state_switch_extra evext = { 256 .to_initial = { 257 .reason = reason, 258 }, 259 }; 260 261 if (s->state == FST_SESSION_STATE_SETUP_COMPLETION || 262 s->state == FST_SESSION_STATE_TRANSITION_DONE) 263 fst_session_tear_down_setup(s); 264 fst_session_stt_disarm(s); 265 os_memset(&s->data, 0, sizeof(s->data)); 266 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 267 } 268 269 270 static int fst_session_send_action(struct fst_session *s, Boolean old_iface, 271 const void *payload, size_t size, 272 const struct wpabuf *extra_buf) 273 { 274 size_t len; 275 int res; 276 struct wpabuf *buf; 277 u8 action; 278 struct fst_iface *iface = 279 old_iface ? s->data.old_iface : s->data.new_iface; 280 281 WPA_ASSERT(payload != NULL); 282 WPA_ASSERT(size != 0); 283 284 action = *(const u8 *) payload; 285 286 WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED); 287 288 if (!iface) { 289 fst_printf_session(s, MSG_ERROR, 290 "no %s interface for FST Action '%s' sending", 291 old_iface ? "old" : "new", 292 fst_action_names[action]); 293 return -1; 294 } 295 296 len = sizeof(u8) /* category */ + size; 297 if (extra_buf) 298 len += wpabuf_size(extra_buf); 299 300 buf = wpabuf_alloc(len); 301 if (!buf) { 302 fst_printf_session(s, MSG_ERROR, 303 "cannot allocate buffer of %zu bytes for FST Action '%s' sending", 304 len, fst_action_names[action]); 305 return -1; 306 } 307 308 wpabuf_put_u8(buf, WLAN_ACTION_FST); 309 wpabuf_put_data(buf, payload, size); 310 if (extra_buf) 311 wpabuf_put_buf(buf, extra_buf); 312 313 res = fst_iface_send_action(iface, 314 old_iface ? s->data.old_peer_addr : 315 s->data.new_peer_addr, buf); 316 if (res < 0) 317 fst_printf_siface(s, iface, MSG_ERROR, 318 "failed to send FST Action '%s'", 319 fst_action_names[action]); 320 else 321 fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent", 322 fst_action_names[action]); 323 wpabuf_free(buf); 324 325 return res; 326 } 327 328 329 static int fst_session_send_tear_down(struct fst_session *s) 330 { 331 struct fst_tear_down td; 332 int res; 333 334 if (!fst_session_is_in_progress(s)) { 335 fst_printf_session(s, MSG_ERROR, "No FST setup to tear down"); 336 return -1; 337 } 338 339 WPA_ASSERT(s->data.old_iface != NULL); 340 WPA_ASSERT(s->data.new_iface != NULL); 341 342 os_memset(&td, 0, sizeof(td)); 343 344 td.action = FST_ACTION_TEAR_DOWN; 345 td.fsts_id = host_to_le32(s->data.fsts_id); 346 347 res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL); 348 if (!res) 349 fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent"); 350 else 351 fst_printf_sframe(s, TRUE, MSG_ERROR, 352 "failed to send FST TearDown"); 353 354 return res; 355 } 356 357 358 static void fst_session_handle_setup_request(struct fst_iface *iface, 359 const struct ieee80211_mgmt *mgmt, 360 size_t frame_len) 361 { 362 struct fst_session *s; 363 const struct fst_setup_req *req; 364 struct fst_iface *new_iface = NULL; 365 struct fst_group *g; 366 u8 new_iface_peer_addr[ETH_ALEN]; 367 const struct wpabuf *peer_mbies; 368 size_t plen; 369 370 if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { 371 fst_printf_iface(iface, MSG_WARNING, 372 "FST Request dropped: too short (%zu < %zu)", 373 frame_len, 374 IEEE80211_HDRLEN + 1 + sizeof(*req)); 375 return; 376 } 377 plen = frame_len - IEEE80211_HDRLEN - 1; 378 req = (const struct fst_setup_req *) 379 (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 380 if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || 381 req->stie.length < 11) { 382 fst_printf_iface(iface, MSG_WARNING, 383 "FST Request dropped: invalid STIE"); 384 return; 385 } 386 387 if (req->stie.new_band_id == req->stie.old_band_id) { 388 fst_printf_iface(iface, MSG_WARNING, 389 "FST Request dropped: new and old band IDs are the same"); 390 return; 391 } 392 393 g = fst_iface_get_group(iface); 394 395 if (plen > sizeof(*req)) { 396 fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), 397 plen - sizeof(*req)); 398 fst_printf_iface(iface, MSG_INFO, 399 "FST Request: MB IEs updated for " MACSTR, 400 MAC2STR(mgmt->sa)); 401 } 402 403 peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); 404 if (peer_mbies) { 405 new_iface = fst_group_get_new_iface_by_stie_and_mbie( 406 g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), 407 &req->stie, new_iface_peer_addr); 408 if (new_iface) 409 fst_printf_iface(iface, MSG_INFO, 410 "FST Request: new iface (%s:" MACSTR 411 ") found by MB IEs", 412 fst_iface_get_name(new_iface), 413 MAC2STR(new_iface_peer_addr)); 414 } 415 416 if (!new_iface) { 417 new_iface = fst_group_find_new_iface_by_stie( 418 g, iface, mgmt->sa, &req->stie, 419 new_iface_peer_addr); 420 if (new_iface) 421 fst_printf_iface(iface, MSG_INFO, 422 "FST Request: new iface (%s:" MACSTR 423 ") found by others", 424 fst_iface_get_name(new_iface), 425 MAC2STR(new_iface_peer_addr)); 426 } 427 428 if (!new_iface) { 429 fst_printf_iface(iface, MSG_WARNING, 430 "FST Request dropped: new iface not found"); 431 return; 432 } 433 434 s = fst_find_session_in_progress(mgmt->sa, g); 435 if (s) { 436 union fst_session_state_switch_extra evext = { 437 .to_initial = { 438 .reason = REASON_SETUP, 439 }, 440 }; 441 442 /* 443 * 10.32.2.2 Transitioning between states: 444 * Upon receipt of an FST Setup Request frame, the responder 445 * shall respond with an FST Setup Response frame unless it has 446 * a pending FST Setup Request frame addressed to the initiator 447 * and the responder has a numerically larger MAC address than 448 * the initiators MAC address, in which case, the responder 449 * shall delete the received FST Setup Request. 450 */ 451 if (fst_session_is_ready_pending(s) && 452 /* waiting for Setup Response */ 453 os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { 454 fst_printf_session(s, MSG_WARNING, 455 "FST Request dropped due to MAC comparison (our MAC is " 456 MACSTR ")", 457 MAC2STR(mgmt->da)); 458 return; 459 } 460 461 /* 462 * State is SETUP_COMPLETION (either in transition or not) or 463 * TRANSITION_DONE (in transition). 464 * Setup Request arriving in this state could mean: 465 * 1. peer sent it before receiving our Setup Request (race 466 * condition) 467 * 2. peer didn't receive our Setup Response. Peer is retrying 468 * after STT timeout 469 * 3. peer's FST state machines are out of sync due to some 470 * other reason 471 * 472 * We will reset our session and create a new one instead. 473 */ 474 475 fst_printf_session(s, MSG_WARNING, 476 "resetting due to FST request"); 477 478 /* 479 * If FST Setup Request arrived with the same FSTS ID as one we 480 * initialized before, there's no need to tear down the session. 481 * Moreover, as FSTS ID is the same, the other side will 482 * associate this tear down with the session it initiated that 483 * will break the sync. 484 */ 485 if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) 486 fst_session_send_tear_down(s); 487 else 488 fst_printf_session(s, MSG_WARNING, 489 "Skipping TearDown as the FST request has the same FSTS ID as initiated"); 490 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 491 fst_session_stt_disarm(s); 492 } 493 494 s = fst_session_create(g); 495 if (!s) { 496 fst_printf(MSG_WARNING, 497 "FST Request dropped: cannot create session for %s and %s", 498 fst_iface_get_name(iface), 499 fst_iface_get_name(new_iface)); 500 return; 501 } 502 503 fst_session_set_iface(s, iface, TRUE); 504 fst_session_set_peer_addr(s, mgmt->sa, TRUE); 505 fst_session_set_iface(s, new_iface, FALSE); 506 fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); 507 fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); 508 s->data.pending_setup_req_dlgt = req->dialog_token; 509 s->data.fsts_id = le_to_host32(req->stie.fsts_id); 510 511 fst_session_stt_arm(s); 512 513 fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); 514 515 fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); 516 } 517 518 519 static void fst_session_handle_setup_response(struct fst_session *s, 520 struct fst_iface *iface, 521 const struct ieee80211_mgmt *mgmt, 522 size_t frame_len) 523 { 524 const struct fst_setup_res *res; 525 size_t plen = frame_len - IEEE80211_HDRLEN - 1; 526 enum hostapd_hw_mode hw_mode; 527 u8 channel; 528 union fst_session_state_switch_extra evext = { 529 .to_initial = { 530 .reject_code = 0, 531 }, 532 }; 533 534 if (iface != s->data.old_iface) { 535 fst_printf_session(s, MSG_WARNING, 536 "FST Response dropped: %s is not the old iface", 537 fst_iface_get_name(iface)); 538 return; 539 } 540 541 if (!fst_session_is_ready_pending(s)) { 542 fst_printf_session(s, MSG_WARNING, 543 "FST Response dropped due to wrong state: %s", 544 fst_session_state_name(s->state)); 545 return; 546 } 547 548 if (plen < sizeof(*res)) { 549 fst_printf_session(s, MSG_WARNING, 550 "Too short FST Response dropped"); 551 return; 552 } 553 res = (const struct fst_setup_res *) 554 (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 555 if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION || 556 res->stie.length < 11) { 557 fst_printf_iface(iface, MSG_WARNING, 558 "FST Response dropped: invalid STIE"); 559 return; 560 } 561 562 if (res->dialog_token != s->data.pending_setup_req_dlgt) { 563 fst_printf_session(s, MSG_WARNING, 564 "FST Response dropped due to wrong dialog token (%u != %u)", 565 s->data.pending_setup_req_dlgt, 566 res->dialog_token); 567 return; 568 } 569 570 if (res->status_code == WLAN_STATUS_SUCCESS && 571 le_to_host32(res->stie.fsts_id) != s->data.fsts_id) { 572 fst_printf_session(s, MSG_WARNING, 573 "FST Response dropped due to wrong FST Session ID (%u)", 574 le_to_host32(res->stie.fsts_id)); 575 return; 576 } 577 578 fst_session_stt_disarm(s); 579 580 if (res->status_code != WLAN_STATUS_SUCCESS) { 581 /* 582 * 10.32.2.2 Transitioning between states 583 * The initiator shall set the STT to the value of the 584 * FSTSessionTimeOut field at ... and at each ACK frame sent in 585 * response to a received FST Setup Response with the Status 586 * Code field equal to PENDING_ADMITTING_FST_SESSION or 587 * PENDING_GAP_IN_BA_WINDOW. 588 */ 589 evext.to_initial.reason = REASON_REJECT; 590 evext.to_initial.reject_code = res->status_code; 591 evext.to_initial.initiator = FST_INITIATOR_REMOTE; 592 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 593 fst_printf_session(s, MSG_WARNING, 594 "FST Setup rejected by remote side with status %u", 595 res->status_code); 596 return; 597 } 598 599 fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel); 600 601 if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) { 602 evext.to_initial.reason = REASON_ERROR_PARAMS; 603 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 604 fst_printf_session(s, MSG_WARNING, 605 "invalid FST Setup parameters"); 606 fst_session_tear_down_setup(s); 607 return; 608 } 609 610 fst_printf_session(s, MSG_INFO, 611 "%s: FST Setup established for %s (llt=%u)", 612 fst_iface_get_name(s->data.old_iface), 613 fst_iface_get_name(s->data.new_iface), 614 s->data.llt_ms); 615 616 fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL); 617 618 if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY) 619 fst_session_initiate_switch(s); 620 } 621 622 623 static void fst_session_handle_tear_down(struct fst_session *s, 624 struct fst_iface *iface, 625 const struct ieee80211_mgmt *mgmt, 626 size_t frame_len) 627 { 628 const struct fst_tear_down *td; 629 size_t plen = frame_len - IEEE80211_HDRLEN - 1; 630 union fst_session_state_switch_extra evext = { 631 .to_initial = { 632 .reason = REASON_TEARDOWN, 633 .initiator = FST_INITIATOR_REMOTE, 634 }, 635 }; 636 637 if (plen < sizeof(*td)) { 638 fst_printf_session(s, MSG_WARNING, 639 "Too short FST Tear Down dropped"); 640 return; 641 } 642 td = (const struct fst_tear_down *) 643 (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 644 645 if (le_to_host32(td->fsts_id) != s->data.fsts_id) { 646 fst_printf_siface(s, iface, MSG_WARNING, 647 "tear down for wrong FST Setup ID (%u)", 648 le_to_host32(td->fsts_id)); 649 return; 650 } 651 652 fst_session_stt_disarm(s); 653 654 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 655 } 656 657 658 static void fst_session_handle_ack_request(struct fst_session *s, 659 struct fst_iface *iface, 660 const struct ieee80211_mgmt *mgmt, 661 size_t frame_len) 662 { 663 const struct fst_ack_req *req; 664 size_t plen = frame_len - IEEE80211_HDRLEN - 1; 665 struct fst_ack_res res; 666 union fst_session_state_switch_extra evext = { 667 .to_initial = { 668 .reason = REASON_SWITCH, 669 .initiator = FST_INITIATOR_REMOTE, 670 }, 671 }; 672 673 if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) { 674 fst_printf_siface(s, iface, MSG_ERROR, 675 "cannot initiate switch due to wrong session state (%s)", 676 fst_session_state_name(s->state)); 677 return; 678 } 679 680 WPA_ASSERT(s->data.new_iface != NULL); 681 682 if (iface != s->data.new_iface) { 683 fst_printf_siface(s, iface, MSG_ERROR, 684 "Ack received on wrong interface"); 685 return; 686 } 687 688 if (plen < sizeof(*req)) { 689 fst_printf_session(s, MSG_WARNING, 690 "Too short FST Ack Request dropped"); 691 return; 692 } 693 req = (const struct fst_ack_req *) 694 (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 695 696 if (le_to_host32(req->fsts_id) != s->data.fsts_id) { 697 fst_printf_siface(s, iface, MSG_WARNING, 698 "Ack for wrong FST Setup ID (%u)", 699 le_to_host32(req->fsts_id)); 700 return; 701 } 702 703 os_memset(&res, 0, sizeof(res)); 704 705 res.action = FST_ACTION_ACK_RESPONSE; 706 res.dialog_token = req->dialog_token; 707 res.fsts_id = req->fsts_id; 708 709 if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) { 710 fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent"); 711 fst_session_stt_disarm(s); 712 fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, 713 NULL); 714 fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, 715 NULL); 716 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 717 } 718 } 719 720 721 static void 722 fst_session_handle_ack_response(struct fst_session *s, 723 struct fst_iface *iface, 724 const struct ieee80211_mgmt *mgmt, 725 size_t frame_len) 726 { 727 const struct fst_ack_res *res; 728 size_t plen = frame_len - IEEE80211_HDRLEN - 1; 729 union fst_session_state_switch_extra evext = { 730 .to_initial = { 731 .reason = REASON_SWITCH, 732 .initiator = FST_INITIATOR_LOCAL, 733 }, 734 }; 735 736 if (!fst_session_is_switch_requested(s)) { 737 fst_printf_siface(s, iface, MSG_ERROR, 738 "Ack Response in inappropriate session state (%s)", 739 fst_session_state_name(s->state)); 740 return; 741 } 742 743 WPA_ASSERT(s->data.new_iface != NULL); 744 745 if (iface != s->data.new_iface) { 746 fst_printf_siface(s, iface, MSG_ERROR, 747 "Ack response received on wrong interface"); 748 return; 749 } 750 751 if (plen < sizeof(*res)) { 752 fst_printf_session(s, MSG_WARNING, 753 "Too short FST Ack Response dropped"); 754 return; 755 } 756 res = (const struct fst_ack_res *) 757 (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 758 759 if (le_to_host32(res->fsts_id) != s->data.fsts_id) { 760 fst_printf_siface(s, iface, MSG_ERROR, 761 "Ack response for wrong FST Setup ID (%u)", 762 le_to_host32(res->fsts_id)); 763 return; 764 } 765 766 fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL); 767 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 768 769 fst_session_stt_disarm(s); 770 } 771 772 773 struct fst_session * fst_session_create(struct fst_group *g) 774 { 775 struct fst_session *s; 776 u32 id; 777 778 WPA_ASSERT(!is_zero_ether_addr(own_addr)); 779 780 id = fst_find_free_session_id(); 781 if (id == FST_INVALID_SESSION_ID) { 782 fst_printf(MSG_ERROR, "Cannot assign new session ID"); 783 return NULL; 784 } 785 786 s = os_zalloc(sizeof(*s)); 787 if (!s) { 788 fst_printf(MSG_ERROR, "Cannot allocate new session object"); 789 return NULL; 790 } 791 792 s->id = id; 793 s->group = g; 794 s->state = FST_SESSION_STATE_INITIAL; 795 796 s->data.llt_ms = FST_LLT_MS_DEFAULT; 797 798 fst_printf(MSG_INFO, "Session %u created", s->id); 799 800 dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry); 801 802 foreach_fst_ctrl_call(on_session_added, s); 803 804 return s; 805 } 806 807 808 void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, 809 Boolean is_old) 810 { 811 if (is_old) 812 s->data.old_iface = iface; 813 else 814 s->data.new_iface = iface; 815 816 } 817 818 819 void fst_session_set_llt(struct fst_session *s, u32 llt) 820 { 821 s->data.llt_ms = llt; 822 } 823 824 825 void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, 826 Boolean is_old) 827 { 828 u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr; 829 830 os_memcpy(a, addr, ETH_ALEN); 831 } 832 833 834 int fst_session_initiate_setup(struct fst_session *s) 835 { 836 struct fst_setup_req req; 837 int res; 838 u32 fsts_id; 839 u8 dialog_token; 840 struct fst_session *_s; 841 842 if (fst_session_is_in_progress(s)) { 843 fst_printf_session(s, MSG_ERROR, "Session in progress"); 844 return -EINVAL; 845 } 846 847 if (is_zero_ether_addr(s->data.old_peer_addr)) { 848 fst_printf_session(s, MSG_ERROR, "No old peer MAC address"); 849 return -EINVAL; 850 } 851 852 if (is_zero_ether_addr(s->data.new_peer_addr)) { 853 fst_printf_session(s, MSG_ERROR, "No new peer MAC address"); 854 return -EINVAL; 855 } 856 857 if (!s->data.old_iface) { 858 fst_printf_session(s, MSG_ERROR, "No old interface defined"); 859 return -EINVAL; 860 } 861 862 if (!s->data.new_iface) { 863 fst_printf_session(s, MSG_ERROR, "No new interface defined"); 864 return -EINVAL; 865 } 866 867 if (s->data.new_iface == s->data.old_iface) { 868 fst_printf_session(s, MSG_ERROR, 869 "Same interface set as old and new"); 870 return -EINVAL; 871 } 872 873 if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr, 874 FALSE)) { 875 fst_printf_session(s, MSG_ERROR, 876 "The preset old peer address is not connected"); 877 return -EINVAL; 878 } 879 880 if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr, 881 FALSE)) { 882 fst_printf_session(s, MSG_ERROR, 883 "The preset new peer address is not connected"); 884 return -EINVAL; 885 } 886 887 _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group); 888 if (_s) { 889 fst_printf_session(s, MSG_ERROR, 890 "There is another session in progress (old): %u", 891 _s->id); 892 return -EINVAL; 893 } 894 895 _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group); 896 if (_s) { 897 fst_printf_session(s, MSG_ERROR, 898 "There is another session in progress (new): %u", 899 _s->id); 900 return -EINVAL; 901 } 902 903 dialog_token = fst_group_assign_dialog_token(s->group); 904 fsts_id = fst_group_assign_fsts_id(s->group); 905 906 os_memset(&req, 0, sizeof(req)); 907 908 fst_printf_siface(s, s->data.old_iface, MSG_INFO, 909 "initiating FST setup for %s (llt=%u ms)", 910 fst_iface_get_name(s->data.new_iface), s->data.llt_ms); 911 912 req.action = FST_ACTION_SETUP_REQUEST; 913 req.dialog_token = dialog_token; 914 req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms)); 915 /* 8.4.2.147 Session Transition element */ 916 req.stie.element_id = WLAN_EID_SESSION_TRANSITION; 917 req.stie.length = sizeof(req.stie) - 2; 918 req.stie.fsts_id = host_to_le32(fsts_id); 919 req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 920 921 req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface); 922 req.stie.new_band_op = 1; 923 req.stie.new_band_setup = 0; 924 925 req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface); 926 req.stie.old_band_op = 1; 927 req.stie.old_band_setup = 0; 928 929 res = fst_session_send_action(s, TRUE, &req, sizeof(req), 930 fst_iface_get_mbie(s->data.old_iface)); 931 if (!res) { 932 s->data.fsts_id = fsts_id; 933 s->data.pending_setup_req_dlgt = dialog_token; 934 fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent"); 935 fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, 936 NULL); 937 938 fst_session_stt_arm(s); 939 } 940 941 return res; 942 } 943 944 945 int fst_session_respond(struct fst_session *s, u8 status_code) 946 { 947 struct fst_setup_res res; 948 enum hostapd_hw_mode hw_mode; 949 u8 channel; 950 951 if (!fst_session_is_ready_pending(s)) { 952 fst_printf_session(s, MSG_ERROR, "incorrect state: %s", 953 fst_session_state_name(s->state)); 954 return -EINVAL; 955 } 956 957 if (is_zero_ether_addr(s->data.old_peer_addr)) { 958 fst_printf_session(s, MSG_ERROR, "No peer MAC address"); 959 return -EINVAL; 960 } 961 962 if (!s->data.old_iface) { 963 fst_printf_session(s, MSG_ERROR, "No old interface defined"); 964 return -EINVAL; 965 } 966 967 if (!s->data.new_iface) { 968 fst_printf_session(s, MSG_ERROR, "No new interface defined"); 969 return -EINVAL; 970 } 971 972 if (s->data.new_iface == s->data.old_iface) { 973 fst_printf_session(s, MSG_ERROR, 974 "Same interface set as old and new"); 975 return -EINVAL; 976 } 977 978 if (!fst_iface_is_connected(s->data.old_iface, 979 s->data.old_peer_addr, FALSE)) { 980 fst_printf_session(s, MSG_ERROR, 981 "The preset peer address is not in the peer list"); 982 return -EINVAL; 983 } 984 985 fst_session_stt_disarm(s); 986 987 os_memset(&res, 0, sizeof(res)); 988 989 res.action = FST_ACTION_SETUP_RESPONSE; 990 res.dialog_token = s->data.pending_setup_req_dlgt; 991 res.status_code = status_code; 992 993 res.stie.element_id = WLAN_EID_SESSION_TRANSITION; 994 res.stie.length = sizeof(res.stie) - 2; 995 996 if (status_code == WLAN_STATUS_SUCCESS) { 997 res.stie.fsts_id = s->data.fsts_id; 998 res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 999 1000 fst_iface_get_channel_info(s->data.new_iface, &hw_mode, 1001 &channel); 1002 res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 1003 res.stie.new_band_op = 1; 1004 res.stie.new_band_setup = 0; 1005 1006 fst_iface_get_channel_info(s->data.old_iface, &hw_mode, 1007 &channel); 1008 res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 1009 res.stie.old_band_op = 1; 1010 res.stie.old_band_setup = 0; 1011 1012 fst_printf_session(s, MSG_INFO, 1013 "%s: FST Setup Request accepted for %s (llt=%u)", 1014 fst_iface_get_name(s->data.old_iface), 1015 fst_iface_get_name(s->data.new_iface), 1016 s->data.llt_ms); 1017 } else { 1018 fst_printf_session(s, MSG_WARNING, 1019 "%s: FST Setup Request rejected with code %d", 1020 fst_iface_get_name(s->data.old_iface), 1021 status_code); 1022 } 1023 1024 if (fst_session_send_action(s, TRUE, &res, sizeof(res), 1025 fst_iface_get_mbie(s->data.old_iface))) { 1026 fst_printf_sframe(s, TRUE, MSG_ERROR, 1027 "cannot send FST Setup Response with code %d", 1028 status_code); 1029 return -EINVAL; 1030 } 1031 1032 fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent"); 1033 1034 if (status_code != WLAN_STATUS_SUCCESS) { 1035 union fst_session_state_switch_extra evext = { 1036 .to_initial = { 1037 .reason = REASON_REJECT, 1038 .reject_code = status_code, 1039 .initiator = FST_INITIATOR_LOCAL, 1040 }, 1041 }; 1042 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 1043 } 1044 1045 return 0; 1046 } 1047 1048 1049 int fst_session_initiate_switch(struct fst_session *s) 1050 { 1051 struct fst_ack_req req; 1052 int res; 1053 u8 dialog_token; 1054 1055 if (!fst_session_is_ready(s)) { 1056 fst_printf_session(s, MSG_ERROR, 1057 "cannot initiate switch due to wrong setup state (%d)", 1058 s->state); 1059 return -1; 1060 } 1061 1062 dialog_token = fst_group_assign_dialog_token(s->group); 1063 1064 WPA_ASSERT(s->data.new_iface != NULL); 1065 WPA_ASSERT(s->data.old_iface != NULL); 1066 1067 fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s", 1068 fst_iface_get_name(s->data.old_iface), 1069 fst_iface_get_name(s->data.new_iface)); 1070 1071 os_memset(&req, 0, sizeof(req)); 1072 1073 req.action = FST_ACTION_ACK_REQUEST; 1074 req.dialog_token = dialog_token; 1075 req.fsts_id = host_to_le32(s->data.fsts_id); 1076 1077 res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL); 1078 if (!res) { 1079 fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent"); 1080 fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, 1081 NULL); 1082 fst_session_stt_arm(s); 1083 } else { 1084 fst_printf_sframe(s, FALSE, MSG_ERROR, 1085 "Cannot send FST Ack Request"); 1086 } 1087 1088 return res; 1089 } 1090 1091 1092 void fst_session_handle_action(struct fst_session *s, 1093 struct fst_iface *iface, 1094 const struct ieee80211_mgmt *mgmt, 1095 size_t frame_len) 1096 { 1097 switch (mgmt->u.action.u.fst_action.action) { 1098 case FST_ACTION_SETUP_REQUEST: 1099 WPA_ASSERT(0); 1100 break; 1101 case FST_ACTION_SETUP_RESPONSE: 1102 fst_session_handle_setup_response(s, iface, mgmt, frame_len); 1103 break; 1104 case FST_ACTION_TEAR_DOWN: 1105 fst_session_handle_tear_down(s, iface, mgmt, frame_len); 1106 break; 1107 case FST_ACTION_ACK_REQUEST: 1108 fst_session_handle_ack_request(s, iface, mgmt, frame_len); 1109 break; 1110 case FST_ACTION_ACK_RESPONSE: 1111 fst_session_handle_ack_response(s, iface, mgmt, frame_len); 1112 break; 1113 case FST_ACTION_ON_CHANNEL_TUNNEL: 1114 default: 1115 fst_printf_sframe(s, FALSE, MSG_ERROR, 1116 "Unsupported FST Action frame"); 1117 break; 1118 } 1119 } 1120 1121 1122 int fst_session_tear_down_setup(struct fst_session *s) 1123 { 1124 int res; 1125 union fst_session_state_switch_extra evext = { 1126 .to_initial = { 1127 .reason = REASON_TEARDOWN, 1128 .initiator = FST_INITIATOR_LOCAL, 1129 }, 1130 }; 1131 1132 res = fst_session_send_tear_down(s); 1133 1134 fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 1135 1136 return res; 1137 } 1138 1139 1140 void fst_session_reset(struct fst_session *s) 1141 { 1142 fst_session_reset_ex(s, REASON_RESET); 1143 } 1144 1145 1146 void fst_session_delete(struct fst_session *s) 1147 { 1148 fst_printf(MSG_INFO, "Session %u deleted", s->id); 1149 dl_list_del(&s->global_sessions_lentry); 1150 foreach_fst_ctrl_call(on_session_removed, s); 1151 os_free(s); 1152 } 1153 1154 1155 struct fst_group * fst_session_get_group(struct fst_session *s) 1156 { 1157 return s->group; 1158 } 1159 1160 1161 struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old) 1162 { 1163 return is_old ? s->data.old_iface : s->data.new_iface; 1164 } 1165 1166 1167 u32 fst_session_get_id(struct fst_session *s) 1168 { 1169 return s->id; 1170 } 1171 1172 1173 const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old) 1174 { 1175 return is_old ? s->data.old_peer_addr : s->data.new_peer_addr; 1176 } 1177 1178 1179 u32 fst_session_get_llt(struct fst_session *s) 1180 { 1181 return s->data.llt_ms; 1182 } 1183 1184 1185 enum fst_session_state fst_session_get_state(struct fst_session *s) 1186 { 1187 return s->state; 1188 } 1189 1190 1191 struct fst_session * fst_session_get_by_id(u32 id) 1192 { 1193 struct fst_session *s; 1194 1195 foreach_fst_session(s) { 1196 if (id == s->id) 1197 return s; 1198 } 1199 1200 return NULL; 1201 } 1202 1203 1204 void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx) 1205 { 1206 struct fst_session *s; 1207 1208 foreach_fst_session(s) { 1209 if (!g || s->group == g) 1210 clb(s->group, s, ctx); 1211 } 1212 } 1213 1214 1215 void fst_session_on_action_rx(struct fst_iface *iface, 1216 const struct ieee80211_mgmt *mgmt, 1217 size_t len) 1218 { 1219 struct fst_session *s; 1220 1221 if (len < IEEE80211_HDRLEN + 2 || 1222 mgmt->u.action.category != WLAN_ACTION_FST) { 1223 fst_printf_iface(iface, MSG_ERROR, 1224 "invalid Action frame received"); 1225 return; 1226 } 1227 1228 if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) { 1229 fst_printf_iface(iface, MSG_DEBUG, 1230 "FST Action '%s' received!", 1231 fst_action_names[mgmt->u.action.u.fst_action.action]); 1232 } else { 1233 fst_printf_iface(iface, MSG_WARNING, 1234 "unknown FST Action (%u) received!", 1235 mgmt->u.action.u.fst_action.action); 1236 return; 1237 } 1238 1239 if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) { 1240 fst_session_handle_setup_request(iface, mgmt, len); 1241 return; 1242 } 1243 1244 s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface)); 1245 if (s) { 1246 fst_session_handle_action(s, iface, mgmt, len); 1247 } else { 1248 fst_printf_iface(iface, MSG_WARNING, 1249 "FST Action '%s' dropped: no session in progress found", 1250 fst_action_names[mgmt->u.action.u.fst_action.action]); 1251 } 1252 } 1253 1254 1255 int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, 1256 Boolean is_old) 1257 { 1258 struct fst_group *g = fst_session_get_group(s); 1259 struct fst_iface *i; 1260 1261 i = fst_group_get_iface_by_name(g, ifname); 1262 if (!i) { 1263 fst_printf_session(s, MSG_WARNING, 1264 "Cannot set iface %s: no such iface within group '%s'", 1265 ifname, fst_group_get_id(g)); 1266 return -1; 1267 } 1268 1269 fst_session_set_iface(s, i, is_old); 1270 1271 return 0; 1272 } 1273 1274 1275 int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, 1276 Boolean is_old) 1277 { 1278 u8 peer_addr[ETH_ALEN]; 1279 int res = fst_read_peer_addr(mac, peer_addr); 1280 1281 if (res) 1282 return res; 1283 1284 fst_session_set_peer_addr(s, peer_addr, is_old); 1285 1286 return 0; 1287 } 1288 1289 1290 int fst_session_set_str_llt(struct fst_session *s, const char *llt_str) 1291 { 1292 char *endp; 1293 long int llt = strtol(llt_str, &endp, 0); 1294 1295 if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) { 1296 fst_printf_session(s, MSG_WARNING, 1297 "Cannot set llt %s: Invalid llt value (1..%u expected)", 1298 llt_str, FST_MAX_LLT_MS); 1299 return -1; 1300 } 1301 fst_session_set_llt(s, (u32) llt); 1302 1303 return 0; 1304 } 1305 1306 1307 void fst_session_global_on_iface_detached(struct fst_iface *iface) 1308 { 1309 struct fst_session *s; 1310 1311 foreach_fst_session(s) { 1312 if (fst_session_is_in_progress(s) && 1313 (s->data.new_iface == iface || 1314 s->data.old_iface == iface)) 1315 fst_session_reset_ex(s, REASON_DETACH_IFACE); 1316 } 1317 } 1318 1319 1320 struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) 1321 { 1322 struct fst_session *s; 1323 1324 foreach_fst_session(s) { 1325 if (s->group == g) 1326 return s; 1327 } 1328 1329 return NULL; 1330 } 1331 1332 1333 #ifdef CONFIG_FST_TEST 1334 1335 static int get_group_fill_session(struct fst_group **g, struct fst_session *s) 1336 { 1337 const u8 *old_addr, *new_addr; 1338 struct fst_get_peer_ctx *ctx; 1339 1340 os_memset(s, 0, sizeof(*s)); 1341 foreach_fst_group(*g) { 1342 s->data.new_iface = fst_group_first_iface(*g); 1343 if (s->data.new_iface) 1344 break; 1345 } 1346 if (!s->data.new_iface) 1347 return -EINVAL; 1348 1349 s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, 1350 struct fst_iface, group_lentry); 1351 if (!s->data.old_iface) 1352 return -EINVAL; 1353 1354 old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); 1355 if (!old_addr) 1356 return -EINVAL; 1357 1358 new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); 1359 if (!new_addr) 1360 return -EINVAL; 1361 1362 os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); 1363 os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); 1364 1365 return 0; 1366 } 1367 1368 1369 #define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 1370 1371 int fst_test_req_send_fst_request(const char *params) 1372 { 1373 int fsts_id; 1374 Boolean is_valid; 1375 char *endp; 1376 struct fst_setup_req req; 1377 struct fst_session s; 1378 struct fst_group *g; 1379 enum hostapd_hw_mode hw_mode; 1380 u8 channel; 1381 char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1382 1383 if (params[0] != ' ') 1384 return -EINVAL; 1385 params++; 1386 fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1387 if (!is_valid) 1388 return -EINVAL; 1389 1390 if (get_group_fill_session(&g, &s)) 1391 return -EINVAL; 1392 1393 req.action = FST_ACTION_SETUP_REQUEST; 1394 req.dialog_token = g->dialog_token; 1395 req.llt = host_to_le32(FST_LLT_MS_DEFAULT); 1396 /* 8.4.2.147 Session Transition element */ 1397 req.stie.element_id = WLAN_EID_SESSION_TRANSITION; 1398 req.stie.length = sizeof(req.stie) - 2; 1399 req.stie.fsts_id = host_to_le32(fsts_id); 1400 req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 1401 1402 fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); 1403 req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 1404 req.stie.new_band_op = 1; 1405 req.stie.new_band_setup = 0; 1406 1407 fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); 1408 req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 1409 req.stie.old_band_op = 1; 1410 req.stie.old_band_setup = 0; 1411 1412 if (!fst_read_next_text_param(endp, additional_param, 1413 sizeof(additional_param), &endp)) { 1414 if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) 1415 req.stie.new_band_id = req.stie.old_band_id; 1416 } 1417 1418 return fst_session_send_action(&s, TRUE, &req, sizeof(req), 1419 s.data.old_iface->mb_ie); 1420 } 1421 1422 1423 int fst_test_req_send_fst_response(const char *params) 1424 { 1425 int fsts_id; 1426 Boolean is_valid; 1427 char *endp; 1428 struct fst_setup_res res; 1429 struct fst_session s; 1430 struct fst_group *g; 1431 enum hostapd_hw_mode hw_mode; 1432 u8 status_code; 1433 u8 channel; 1434 char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1435 struct fst_session *_s; 1436 1437 if (params[0] != ' ') 1438 return -EINVAL; 1439 params++; 1440 fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1441 if (!is_valid) 1442 return -EINVAL; 1443 1444 if (get_group_fill_session(&g, &s)) 1445 return -EINVAL; 1446 1447 status_code = WLAN_STATUS_SUCCESS; 1448 if (!fst_read_next_text_param(endp, response, sizeof(response), 1449 &endp)) { 1450 if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) 1451 status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; 1452 } 1453 1454 os_memset(&res, 0, sizeof(res)); 1455 1456 res.action = FST_ACTION_SETUP_RESPONSE; 1457 /* 1458 * If some session has just received an FST Setup Request, then 1459 * use the correct dialog token copied from this request. 1460 */ 1461 _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), 1462 g); 1463 res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? 1464 _s->data.pending_setup_req_dlgt : g->dialog_token; 1465 res.status_code = status_code; 1466 1467 res.stie.element_id = WLAN_EID_SESSION_TRANSITION; 1468 res.stie.length = sizeof(res.stie) - 2; 1469 1470 if (res.status_code == WLAN_STATUS_SUCCESS) { 1471 res.stie.fsts_id = fsts_id; 1472 res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 1473 1474 fst_iface_get_channel_info(s.data.new_iface, &hw_mode, 1475 &channel); 1476 res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 1477 res.stie.new_band_op = 1; 1478 res.stie.new_band_setup = 0; 1479 1480 fst_iface_get_channel_info(s.data.old_iface, &hw_mode, 1481 &channel); 1482 res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 1483 res.stie.old_band_op = 1; 1484 res.stie.old_band_setup = 0; 1485 } 1486 1487 if (!fst_read_next_text_param(endp, response, sizeof(response), 1488 &endp)) { 1489 if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) 1490 res.stie.new_band_id = res.stie.old_band_id; 1491 } 1492 1493 return fst_session_send_action(&s, TRUE, &res, sizeof(res), 1494 s.data.old_iface->mb_ie); 1495 } 1496 1497 1498 int fst_test_req_send_ack_request(const char *params) 1499 { 1500 int fsts_id; 1501 Boolean is_valid; 1502 char *endp; 1503 struct fst_ack_req req; 1504 struct fst_session s; 1505 struct fst_group *g; 1506 1507 if (params[0] != ' ') 1508 return -EINVAL; 1509 params++; 1510 fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1511 if (!is_valid) 1512 return -EINVAL; 1513 1514 if (get_group_fill_session(&g, &s)) 1515 return -EINVAL; 1516 1517 os_memset(&req, 0, sizeof(req)); 1518 req.action = FST_ACTION_ACK_REQUEST; 1519 req.dialog_token = g->dialog_token; 1520 req.fsts_id = fsts_id; 1521 1522 return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); 1523 } 1524 1525 1526 int fst_test_req_send_ack_response(const char *params) 1527 { 1528 int fsts_id; 1529 Boolean is_valid; 1530 char *endp; 1531 struct fst_ack_res res; 1532 struct fst_session s; 1533 struct fst_group *g; 1534 1535 if (params[0] != ' ') 1536 return -EINVAL; 1537 params++; 1538 fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1539 if (!is_valid) 1540 return -EINVAL; 1541 1542 if (get_group_fill_session(&g, &s)) 1543 return -EINVAL; 1544 1545 os_memset(&res, 0, sizeof(res)); 1546 res.action = FST_ACTION_ACK_RESPONSE; 1547 res.dialog_token = g->dialog_token; 1548 res.fsts_id = fsts_id; 1549 1550 return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); 1551 } 1552 1553 1554 int fst_test_req_send_tear_down(const char *params) 1555 { 1556 int fsts_id; 1557 Boolean is_valid; 1558 char *endp; 1559 struct fst_tear_down td; 1560 struct fst_session s; 1561 struct fst_group *g; 1562 1563 if (params[0] != ' ') 1564 return -EINVAL; 1565 params++; 1566 fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1567 if (!is_valid) 1568 return -EINVAL; 1569 1570 if (get_group_fill_session(&g, &s)) 1571 return -EINVAL; 1572 1573 os_memset(&td, 0, sizeof(td)); 1574 td.action = FST_ACTION_TEAR_DOWN; 1575 td.fsts_id = fsts_id; 1576 1577 return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); 1578 } 1579 1580 1581 u32 fst_test_req_get_fsts_id(const char *params) 1582 { 1583 int sid; 1584 Boolean is_valid; 1585 char *endp; 1586 struct fst_session *s; 1587 1588 if (params[0] != ' ') 1589 return FST_FSTS_ID_NOT_FOUND; 1590 params++; 1591 sid = fst_read_next_int_param(params, &is_valid, &endp); 1592 if (!is_valid) 1593 return FST_FSTS_ID_NOT_FOUND; 1594 1595 s = fst_session_get_by_id(sid); 1596 if (!s) 1597 return FST_FSTS_ID_NOT_FOUND; 1598 1599 return s->data.fsts_id; 1600 } 1601 1602 1603 int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) 1604 { 1605 char *endp; 1606 char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1607 struct fst_group *g; 1608 struct fst_iface *iface; 1609 1610 if (request[0] != ' ') 1611 return -EINVAL; 1612 request++; 1613 if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || 1614 !*ifname) 1615 goto problem; 1616 g = dl_list_first(&fst_global_groups_list, struct fst_group, 1617 global_groups_lentry); 1618 if (!g) 1619 goto problem; 1620 iface = fst_group_get_iface_by_name(g, ifname); 1621 if (!iface || !iface->mb_ie) 1622 goto problem; 1623 return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie), 1624 wpabuf_len(iface->mb_ie)); 1625 1626 problem: 1627 return os_snprintf(buf, buflen, "FAIL\n"); 1628 } 1629 1630 #endif /* CONFIG_FST_TEST */ 1631