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