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