1289284Srpaulo/* 2289284Srpaulo * FST module - FST Session implementation 3289284Srpaulo * Copyright (c) 2014, Qualcomm Atheros, Inc. 4289284Srpaulo * 5289284Srpaulo * This software may be distributed under the terms of the BSD license. 6289284Srpaulo * See README for more details. 7289284Srpaulo */ 8289284Srpaulo 9289284Srpaulo#include "utils/includes.h" 10289284Srpaulo 11289284Srpaulo#include "utils/common.h" 12289284Srpaulo#include "utils/eloop.h" 13289284Srpaulo#include "common/defs.h" 14289284Srpaulo#include "fst/fst_internal.h" 15289284Srpaulo#include "fst/fst_defs.h" 16289284Srpaulo#include "fst/fst_ctrl_iface.h" 17289284Srpaulo#ifdef CONFIG_FST_TEST 18289284Srpaulo#include "fst/fst_ctrl_defs.h" 19289284Srpaulo#endif /* CONFIG_FST_TEST */ 20289284Srpaulo 21289284Srpaulo#define US_80211_TU 1024 22289284Srpaulo 23289284Srpaulo#define US_TO_TU(m) ((m) * / US_80211_TU) 24289284Srpaulo#define TU_TO_US(m) ((m) * US_80211_TU) 25289284Srpaulo 26289284Srpaulo#define FST_LLT_SWITCH_IMMEDIATELY 0 27289284Srpaulo 28289284Srpaulo#define fst_printf_session(s, level, format, ...) \ 29289284Srpaulo fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \ 30289284Srpaulo (s)->id, (s)->data.fsts_id, \ 31289284Srpaulo MAC2STR((s)->data.old_peer_addr), \ 32289284Srpaulo MAC2STR((s)->data.new_peer_addr), \ 33289284Srpaulo ##__VA_ARGS__) 34289284Srpaulo 35289284Srpaulo#define fst_printf_siface(s, iface, level, format, ...) \ 36289284Srpaulo fst_printf_session((s), (level), "%s: " format, \ 37289284Srpaulo fst_iface_get_name(iface), ##__VA_ARGS__) 38289284Srpaulo 39289284Srpaulo#define fst_printf_sframe(s, is_old, level, format, ...) \ 40289284Srpaulo fst_printf_siface((s), \ 41289284Srpaulo (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \ 42289284Srpaulo (level), format, ##__VA_ARGS__) 43289284Srpaulo 44289284Srpaulo#define FST_LLT_MS_DEFAULT 50 45289284Srpaulo#define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL 46289284Srpaulo 47337817Scystatic const char * const fst_action_names[] = { 48289284Srpaulo [FST_ACTION_SETUP_REQUEST] = "Setup Request", 49289284Srpaulo [FST_ACTION_SETUP_RESPONSE] = "Setup Response", 50289284Srpaulo [FST_ACTION_TEAR_DOWN] = "Tear Down", 51289284Srpaulo [FST_ACTION_ACK_REQUEST] = "Ack Request", 52289284Srpaulo [FST_ACTION_ACK_RESPONSE] = "Ack Response", 53289284Srpaulo [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel", 54289284Srpaulo}; 55289284Srpaulo 56289284Srpaulostruct fst_session { 57289284Srpaulo struct { 58289284Srpaulo /* Session configuration that can be zeroed on reset */ 59289284Srpaulo u8 old_peer_addr[ETH_ALEN]; 60289284Srpaulo u8 new_peer_addr[ETH_ALEN]; 61289284Srpaulo struct fst_iface *new_iface; 62289284Srpaulo struct fst_iface *old_iface; 63289284Srpaulo u32 llt_ms; 64289284Srpaulo u8 pending_setup_req_dlgt; 65289284Srpaulo u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147 66289284Srpaulo * Session Transition element */ 67289284Srpaulo } data; 68289284Srpaulo /* Session object internal fields which won't be zeroed on reset */ 69289284Srpaulo struct dl_list global_sessions_lentry; 70289284Srpaulo u32 id; /* Session object ID used to identify 71289284Srpaulo * specific session object */ 72289284Srpaulo struct fst_group *group; 73289284Srpaulo enum fst_session_state state; 74289284Srpaulo Boolean stt_armed; 75289284Srpaulo}; 76289284Srpaulo 77289284Srpaulostatic struct dl_list global_sessions_list; 78289284Srpaulostatic u32 global_session_id = 0; 79289284Srpaulo 80289284Srpaulo#define foreach_fst_session(s) \ 81289284Srpaulo dl_list_for_each((s), &global_sessions_list, \ 82289284Srpaulo struct fst_session, global_sessions_lentry) 83289284Srpaulo 84289284Srpaulo#define foreach_fst_session_safe(s, temp) \ 85289284Srpaulo dl_list_for_each_safe((s), (temp), &global_sessions_list, \ 86289284Srpaulo struct fst_session, global_sessions_lentry) 87289284Srpaulo 88289284Srpaulo 89289284Srpaulostatic void fst_session_global_inc_id(void) 90289284Srpaulo{ 91289284Srpaulo global_session_id++; 92289284Srpaulo if (global_session_id == FST_INVALID_SESSION_ID) 93289284Srpaulo global_session_id++; 94289284Srpaulo} 95289284Srpaulo 96289284Srpaulo 97289284Srpauloint fst_session_global_init(void) 98289284Srpaulo{ 99289284Srpaulo dl_list_init(&global_sessions_list); 100289284Srpaulo return 0; 101289284Srpaulo} 102289284Srpaulo 103289284Srpaulo 104289284Srpaulovoid fst_session_global_deinit(void) 105289284Srpaulo{ 106289284Srpaulo WPA_ASSERT(dl_list_empty(&global_sessions_list)); 107289284Srpaulo} 108289284Srpaulo 109289284Srpaulo 110289284Srpaulostatic inline void fst_session_notify_ctrl(struct fst_session *s, 111289284Srpaulo enum fst_event_type event_type, 112289284Srpaulo union fst_event_extra *extra) 113289284Srpaulo{ 114289284Srpaulo foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra); 115289284Srpaulo} 116289284Srpaulo 117289284Srpaulo 118289284Srpaulostatic void fst_session_set_state(struct fst_session *s, 119289284Srpaulo enum fst_session_state state, 120289284Srpaulo union fst_session_state_switch_extra *extra) 121289284Srpaulo{ 122289284Srpaulo if (s->state != state) { 123289284Srpaulo union fst_event_extra evext = { 124289284Srpaulo .session_state = { 125289284Srpaulo .old_state = s->state, 126289284Srpaulo .new_state = state, 127289284Srpaulo }, 128289284Srpaulo }; 129289284Srpaulo 130289284Srpaulo if (extra) 131289284Srpaulo evext.session_state.extra = *extra; 132289284Srpaulo fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED, 133289284Srpaulo &evext); 134289284Srpaulo fst_printf_session(s, MSG_INFO, "State: %s => %s", 135289284Srpaulo fst_session_state_name(s->state), 136289284Srpaulo fst_session_state_name(state)); 137289284Srpaulo s->state = state; 138289284Srpaulo } 139289284Srpaulo} 140289284Srpaulo 141289284Srpaulo 142289284Srpaulostatic u32 fst_find_free_session_id(void) 143289284Srpaulo{ 144289284Srpaulo u32 i, id = FST_INVALID_SESSION_ID; 145289284Srpaulo struct fst_session *s; 146289284Srpaulo 147289284Srpaulo for (i = 0; i < (u32) -1; i++) { 148289284Srpaulo Boolean in_use = FALSE; 149289284Srpaulo 150289284Srpaulo foreach_fst_session(s) { 151289284Srpaulo if (s->id == global_session_id) { 152289284Srpaulo fst_session_global_inc_id(); 153289284Srpaulo in_use = TRUE; 154289284Srpaulo break; 155289284Srpaulo } 156289284Srpaulo } 157289284Srpaulo if (!in_use) { 158289284Srpaulo id = global_session_id; 159289284Srpaulo fst_session_global_inc_id(); 160289284Srpaulo break; 161289284Srpaulo } 162289284Srpaulo } 163289284Srpaulo 164289284Srpaulo return id; 165289284Srpaulo} 166289284Srpaulo 167289284Srpaulo 168289284Srpaulostatic void fst_session_timeout_handler(void *eloop_data, void *user_ctx) 169289284Srpaulo{ 170289284Srpaulo struct fst_session *s = user_ctx; 171289284Srpaulo union fst_session_state_switch_extra extra = { 172289284Srpaulo .to_initial = { 173289284Srpaulo .reason = REASON_STT, 174289284Srpaulo }, 175289284Srpaulo }; 176289284Srpaulo 177289284Srpaulo fst_printf_session(s, MSG_WARNING, "Session State Timeout"); 178289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra); 179289284Srpaulo} 180289284Srpaulo 181289284Srpaulo 182289284Srpaulostatic void fst_session_stt_arm(struct fst_session *s) 183289284Srpaulo{ 184337817Scy /* Action frames sometimes get delayed. Use relaxed timeout (2*) */ 185337817Scy eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), 186289284Srpaulo fst_session_timeout_handler, NULL, s); 187289284Srpaulo s->stt_armed = TRUE; 188289284Srpaulo} 189289284Srpaulo 190289284Srpaulo 191289284Srpaulostatic void fst_session_stt_disarm(struct fst_session *s) 192289284Srpaulo{ 193289284Srpaulo if (s->stt_armed) { 194289284Srpaulo eloop_cancel_timeout(fst_session_timeout_handler, NULL, s); 195289284Srpaulo s->stt_armed = FALSE; 196289284Srpaulo } 197289284Srpaulo} 198289284Srpaulo 199289284Srpaulo 200289284Srpaulostatic Boolean fst_session_is_in_transition(struct fst_session *s) 201289284Srpaulo{ 202289284Srpaulo /* See spec, 10.32.2.2 Transitioning between states */ 203289284Srpaulo return s->stt_armed; 204289284Srpaulo} 205289284Srpaulo 206289284Srpaulo 207289284Srpaulostatic int fst_session_is_in_progress(struct fst_session *s) 208289284Srpaulo{ 209289284Srpaulo return s->state != FST_SESSION_STATE_INITIAL; 210289284Srpaulo} 211289284Srpaulo 212289284Srpaulo 213289284Srpaulostatic int fst_session_is_ready_pending(struct fst_session *s) 214289284Srpaulo{ 215289284Srpaulo return s->state == FST_SESSION_STATE_SETUP_COMPLETION && 216289284Srpaulo fst_session_is_in_transition(s); 217289284Srpaulo} 218289284Srpaulo 219289284Srpaulo 220289284Srpaulostatic int fst_session_is_ready(struct fst_session *s) 221289284Srpaulo{ 222289284Srpaulo return s->state == FST_SESSION_STATE_SETUP_COMPLETION && 223289284Srpaulo !fst_session_is_in_transition(s); 224289284Srpaulo} 225289284Srpaulo 226289284Srpaulo 227289284Srpaulostatic int fst_session_is_switch_requested(struct fst_session *s) 228289284Srpaulo{ 229289284Srpaulo return s->state == FST_SESSION_STATE_TRANSITION_DONE && 230289284Srpaulo fst_session_is_in_transition(s); 231289284Srpaulo} 232289284Srpaulo 233289284Srpaulo 234289284Srpaulostatic struct fst_session * 235289284Srpaulofst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g) 236289284Srpaulo{ 237289284Srpaulo struct fst_session *s; 238289284Srpaulo 239289284Srpaulo foreach_fst_session(s) { 240289284Srpaulo if (s->group == g && 241289284Srpaulo (os_memcmp(s->data.old_peer_addr, peer_addr, 242289284Srpaulo ETH_ALEN) == 0 || 243289284Srpaulo os_memcmp(s->data.new_peer_addr, peer_addr, 244289284Srpaulo ETH_ALEN) == 0) && 245289284Srpaulo fst_session_is_in_progress(s)) 246289284Srpaulo return s; 247289284Srpaulo } 248289284Srpaulo 249289284Srpaulo return NULL; 250289284Srpaulo} 251289284Srpaulo 252289284Srpaulo 253289284Srpaulostatic void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason) 254289284Srpaulo{ 255289284Srpaulo union fst_session_state_switch_extra evext = { 256289284Srpaulo .to_initial = { 257289284Srpaulo .reason = reason, 258289284Srpaulo }, 259289284Srpaulo }; 260289284Srpaulo 261289284Srpaulo if (s->state == FST_SESSION_STATE_SETUP_COMPLETION || 262289284Srpaulo s->state == FST_SESSION_STATE_TRANSITION_DONE) 263289284Srpaulo fst_session_tear_down_setup(s); 264289284Srpaulo fst_session_stt_disarm(s); 265289284Srpaulo os_memset(&s->data, 0, sizeof(s->data)); 266289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 267289284Srpaulo} 268289284Srpaulo 269289284Srpaulo 270289284Srpaulostatic int fst_session_send_action(struct fst_session *s, Boolean old_iface, 271289284Srpaulo const void *payload, size_t size, 272289284Srpaulo const struct wpabuf *extra_buf) 273289284Srpaulo{ 274289284Srpaulo size_t len; 275289284Srpaulo int res; 276289284Srpaulo struct wpabuf *buf; 277289284Srpaulo u8 action; 278289284Srpaulo struct fst_iface *iface = 279289284Srpaulo old_iface ? s->data.old_iface : s->data.new_iface; 280289284Srpaulo 281289284Srpaulo WPA_ASSERT(payload != NULL); 282289284Srpaulo WPA_ASSERT(size != 0); 283289284Srpaulo 284289284Srpaulo action = *(const u8 *) payload; 285289284Srpaulo 286289284Srpaulo WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED); 287289284Srpaulo 288289284Srpaulo if (!iface) { 289289284Srpaulo fst_printf_session(s, MSG_ERROR, 290289284Srpaulo "no %s interface for FST Action '%s' sending", 291289284Srpaulo old_iface ? "old" : "new", 292289284Srpaulo fst_action_names[action]); 293289284Srpaulo return -1; 294289284Srpaulo } 295289284Srpaulo 296289284Srpaulo len = sizeof(u8) /* category */ + size; 297289284Srpaulo if (extra_buf) 298289284Srpaulo len += wpabuf_size(extra_buf); 299289284Srpaulo 300289284Srpaulo buf = wpabuf_alloc(len); 301289284Srpaulo if (!buf) { 302289284Srpaulo fst_printf_session(s, MSG_ERROR, 303289284Srpaulo "cannot allocate buffer of %zu bytes for FST Action '%s' sending", 304289284Srpaulo len, fst_action_names[action]); 305289284Srpaulo return -1; 306289284Srpaulo } 307289284Srpaulo 308289284Srpaulo wpabuf_put_u8(buf, WLAN_ACTION_FST); 309289284Srpaulo wpabuf_put_data(buf, payload, size); 310289284Srpaulo if (extra_buf) 311289284Srpaulo wpabuf_put_buf(buf, extra_buf); 312289284Srpaulo 313289284Srpaulo res = fst_iface_send_action(iface, 314289284Srpaulo old_iface ? s->data.old_peer_addr : 315289284Srpaulo s->data.new_peer_addr, buf); 316289284Srpaulo if (res < 0) 317289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 318289284Srpaulo "failed to send FST Action '%s'", 319289284Srpaulo fst_action_names[action]); 320289284Srpaulo else 321289284Srpaulo fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent", 322289284Srpaulo fst_action_names[action]); 323289284Srpaulo wpabuf_free(buf); 324289284Srpaulo 325289284Srpaulo return res; 326289284Srpaulo} 327289284Srpaulo 328289284Srpaulo 329289284Srpaulostatic int fst_session_send_tear_down(struct fst_session *s) 330289284Srpaulo{ 331289284Srpaulo struct fst_tear_down td; 332289284Srpaulo int res; 333289284Srpaulo 334289284Srpaulo if (!fst_session_is_in_progress(s)) { 335289284Srpaulo fst_printf_session(s, MSG_ERROR, "No FST setup to tear down"); 336289284Srpaulo return -1; 337289284Srpaulo } 338289284Srpaulo 339289284Srpaulo WPA_ASSERT(s->data.old_iface != NULL); 340289284Srpaulo WPA_ASSERT(s->data.new_iface != NULL); 341289284Srpaulo 342289284Srpaulo os_memset(&td, 0, sizeof(td)); 343289284Srpaulo 344289284Srpaulo td.action = FST_ACTION_TEAR_DOWN; 345289284Srpaulo td.fsts_id = host_to_le32(s->data.fsts_id); 346289284Srpaulo 347289284Srpaulo res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL); 348289284Srpaulo if (!res) 349289284Srpaulo fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent"); 350289284Srpaulo else 351289284Srpaulo fst_printf_sframe(s, TRUE, MSG_ERROR, 352289284Srpaulo "failed to send FST TearDown"); 353289284Srpaulo 354289284Srpaulo return res; 355289284Srpaulo} 356289284Srpaulo 357289284Srpaulo 358289284Srpaulostatic void fst_session_handle_setup_request(struct fst_iface *iface, 359289284Srpaulo const struct ieee80211_mgmt *mgmt, 360289284Srpaulo size_t frame_len) 361289284Srpaulo{ 362289284Srpaulo struct fst_session *s; 363289284Srpaulo const struct fst_setup_req *req; 364289284Srpaulo struct fst_iface *new_iface = NULL; 365289284Srpaulo struct fst_group *g; 366289284Srpaulo u8 new_iface_peer_addr[ETH_ALEN]; 367289284Srpaulo size_t plen; 368289284Srpaulo 369289284Srpaulo if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { 370289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 371289284Srpaulo "FST Request dropped: too short (%zu < %zu)", 372289284Srpaulo frame_len, 373289284Srpaulo IEEE80211_HDRLEN + 1 + sizeof(*req)); 374289284Srpaulo return; 375289284Srpaulo } 376289284Srpaulo plen = frame_len - IEEE80211_HDRLEN - 1; 377289284Srpaulo req = (const struct fst_setup_req *) 378289284Srpaulo (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 379289284Srpaulo if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || 380289284Srpaulo req->stie.length < 11) { 381289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 382289284Srpaulo "FST Request dropped: invalid STIE"); 383289284Srpaulo return; 384289284Srpaulo } 385289284Srpaulo 386289284Srpaulo if (req->stie.new_band_id == req->stie.old_band_id) { 387289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 388289284Srpaulo "FST Request dropped: new and old band IDs are the same"); 389289284Srpaulo return; 390289284Srpaulo } 391289284Srpaulo 392289284Srpaulo g = fst_iface_get_group(iface); 393289284Srpaulo 394289284Srpaulo if (plen > sizeof(*req)) { 395289284Srpaulo fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), 396289284Srpaulo plen - sizeof(*req)); 397289284Srpaulo fst_printf_iface(iface, MSG_INFO, 398289284Srpaulo "FST Request: MB IEs updated for " MACSTR, 399289284Srpaulo MAC2STR(mgmt->sa)); 400289284Srpaulo } 401289284Srpaulo 402337817Scy new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa, 403337817Scy req->stie.new_band_id, 404337817Scy new_iface_peer_addr); 405289284Srpaulo if (!new_iface) { 406289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 407289284Srpaulo "FST Request dropped: new iface not found"); 408289284Srpaulo return; 409289284Srpaulo } 410337817Scy fst_printf_iface(iface, MSG_INFO, 411337817Scy "FST Request: new iface (%s:" MACSTR ") found", 412337817Scy fst_iface_get_name(new_iface), 413337817Scy MAC2STR(new_iface_peer_addr)); 414289284Srpaulo 415289284Srpaulo s = fst_find_session_in_progress(mgmt->sa, g); 416289284Srpaulo if (s) { 417289284Srpaulo union fst_session_state_switch_extra evext = { 418289284Srpaulo .to_initial = { 419289284Srpaulo .reason = REASON_SETUP, 420289284Srpaulo }, 421289284Srpaulo }; 422289284Srpaulo 423289284Srpaulo /* 424289284Srpaulo * 10.32.2.2 Transitioning between states: 425289284Srpaulo * Upon receipt of an FST Setup Request frame, the responder 426289284Srpaulo * shall respond with an FST Setup Response frame unless it has 427289284Srpaulo * a pending FST Setup Request frame addressed to the initiator 428289284Srpaulo * and the responder has a numerically larger MAC address than 429289284Srpaulo * the initiator���s MAC address, in which case, the responder 430289284Srpaulo * shall delete the received FST Setup Request. 431289284Srpaulo */ 432337817Scy if (fst_session_is_ready_pending(s) && 433337817Scy /* waiting for Setup Response */ 434337817Scy os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { 435289284Srpaulo fst_printf_session(s, MSG_WARNING, 436289284Srpaulo "FST Request dropped due to MAC comparison (our MAC is " 437289284Srpaulo MACSTR ")", 438289284Srpaulo MAC2STR(mgmt->da)); 439289284Srpaulo return; 440289284Srpaulo } 441289284Srpaulo 442337817Scy /* 443337817Scy * State is SETUP_COMPLETION (either in transition or not) or 444337817Scy * TRANSITION_DONE (in transition). 445337817Scy * Setup Request arriving in this state could mean: 446337817Scy * 1. peer sent it before receiving our Setup Request (race 447337817Scy * condition) 448337817Scy * 2. peer didn't receive our Setup Response. Peer is retrying 449337817Scy * after STT timeout 450337817Scy * 3. peer's FST state machines are out of sync due to some 451337817Scy * other reason 452337817Scy * 453337817Scy * We will reset our session and create a new one instead. 454337817Scy */ 455289284Srpaulo 456337817Scy fst_printf_session(s, MSG_WARNING, 457337817Scy "resetting due to FST request"); 458289284Srpaulo 459289284Srpaulo /* 460289284Srpaulo * If FST Setup Request arrived with the same FSTS ID as one we 461337817Scy * initialized before, there's no need to tear down the session. 462289284Srpaulo * Moreover, as FSTS ID is the same, the other side will 463289284Srpaulo * associate this tear down with the session it initiated that 464289284Srpaulo * will break the sync. 465289284Srpaulo */ 466289284Srpaulo if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) 467289284Srpaulo fst_session_send_tear_down(s); 468289284Srpaulo else 469289284Srpaulo fst_printf_session(s, MSG_WARNING, 470289284Srpaulo "Skipping TearDown as the FST request has the same FSTS ID as initiated"); 471289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 472289284Srpaulo fst_session_stt_disarm(s); 473289284Srpaulo } 474289284Srpaulo 475289284Srpaulo s = fst_session_create(g); 476289284Srpaulo if (!s) { 477289284Srpaulo fst_printf(MSG_WARNING, 478289284Srpaulo "FST Request dropped: cannot create session for %s and %s", 479289284Srpaulo fst_iface_get_name(iface), 480289284Srpaulo fst_iface_get_name(new_iface)); 481289284Srpaulo return; 482289284Srpaulo } 483289284Srpaulo 484289284Srpaulo fst_session_set_iface(s, iface, TRUE); 485289284Srpaulo fst_session_set_peer_addr(s, mgmt->sa, TRUE); 486289284Srpaulo fst_session_set_iface(s, new_iface, FALSE); 487289284Srpaulo fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); 488289284Srpaulo fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); 489289284Srpaulo s->data.pending_setup_req_dlgt = req->dialog_token; 490289284Srpaulo s->data.fsts_id = le_to_host32(req->stie.fsts_id); 491289284Srpaulo 492289284Srpaulo fst_session_stt_arm(s); 493289284Srpaulo 494289284Srpaulo fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); 495289284Srpaulo 496289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); 497289284Srpaulo} 498289284Srpaulo 499289284Srpaulo 500289284Srpaulostatic void fst_session_handle_setup_response(struct fst_session *s, 501289284Srpaulo struct fst_iface *iface, 502289284Srpaulo const struct ieee80211_mgmt *mgmt, 503289284Srpaulo size_t frame_len) 504289284Srpaulo{ 505289284Srpaulo const struct fst_setup_res *res; 506289284Srpaulo size_t plen = frame_len - IEEE80211_HDRLEN - 1; 507289284Srpaulo enum hostapd_hw_mode hw_mode; 508289284Srpaulo u8 channel; 509289284Srpaulo union fst_session_state_switch_extra evext = { 510337817Scy .to_initial = { 511337817Scy .reject_code = 0, 512337817Scy }, 513289284Srpaulo }; 514289284Srpaulo 515289284Srpaulo if (iface != s->data.old_iface) { 516289284Srpaulo fst_printf_session(s, MSG_WARNING, 517289284Srpaulo "FST Response dropped: %s is not the old iface", 518289284Srpaulo fst_iface_get_name(iface)); 519289284Srpaulo return; 520289284Srpaulo } 521289284Srpaulo 522289284Srpaulo if (!fst_session_is_ready_pending(s)) { 523289284Srpaulo fst_printf_session(s, MSG_WARNING, 524289284Srpaulo "FST Response dropped due to wrong state: %s", 525289284Srpaulo fst_session_state_name(s->state)); 526289284Srpaulo return; 527289284Srpaulo } 528289284Srpaulo 529289284Srpaulo if (plen < sizeof(*res)) { 530289284Srpaulo fst_printf_session(s, MSG_WARNING, 531289284Srpaulo "Too short FST Response dropped"); 532289284Srpaulo return; 533289284Srpaulo } 534289284Srpaulo res = (const struct fst_setup_res *) 535289284Srpaulo (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 536289284Srpaulo if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION || 537289284Srpaulo res->stie.length < 11) { 538289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 539289284Srpaulo "FST Response dropped: invalid STIE"); 540289284Srpaulo return; 541289284Srpaulo } 542289284Srpaulo 543289284Srpaulo if (res->dialog_token != s->data.pending_setup_req_dlgt) { 544289284Srpaulo fst_printf_session(s, MSG_WARNING, 545289284Srpaulo "FST Response dropped due to wrong dialog token (%u != %u)", 546289284Srpaulo s->data.pending_setup_req_dlgt, 547289284Srpaulo res->dialog_token); 548289284Srpaulo return; 549289284Srpaulo } 550289284Srpaulo 551289284Srpaulo if (res->status_code == WLAN_STATUS_SUCCESS && 552289284Srpaulo le_to_host32(res->stie.fsts_id) != s->data.fsts_id) { 553289284Srpaulo fst_printf_session(s, MSG_WARNING, 554289284Srpaulo "FST Response dropped due to wrong FST Session ID (%u)", 555289284Srpaulo le_to_host32(res->stie.fsts_id)); 556289284Srpaulo return; 557289284Srpaulo } 558289284Srpaulo 559289284Srpaulo fst_session_stt_disarm(s); 560289284Srpaulo 561289284Srpaulo if (res->status_code != WLAN_STATUS_SUCCESS) { 562289284Srpaulo /* 563289284Srpaulo * 10.32.2.2 Transitioning between states 564289284Srpaulo * The initiator shall set the STT to the value of the 565289284Srpaulo * FSTSessionTimeOut field at ... and at each ACK frame sent in 566289284Srpaulo * response to a received FST Setup Response with the Status 567289284Srpaulo * Code field equal to PENDING_ADMITTING_FST_SESSION or 568289284Srpaulo * PENDING_GAP_IN_BA_WINDOW. 569289284Srpaulo */ 570289284Srpaulo evext.to_initial.reason = REASON_REJECT; 571289284Srpaulo evext.to_initial.reject_code = res->status_code; 572289284Srpaulo evext.to_initial.initiator = FST_INITIATOR_REMOTE; 573289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 574289284Srpaulo fst_printf_session(s, MSG_WARNING, 575289284Srpaulo "FST Setup rejected by remote side with status %u", 576289284Srpaulo res->status_code); 577289284Srpaulo return; 578289284Srpaulo } 579289284Srpaulo 580289284Srpaulo fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel); 581289284Srpaulo 582289284Srpaulo if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) { 583289284Srpaulo evext.to_initial.reason = REASON_ERROR_PARAMS; 584289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 585289284Srpaulo fst_printf_session(s, MSG_WARNING, 586289284Srpaulo "invalid FST Setup parameters"); 587289284Srpaulo fst_session_tear_down_setup(s); 588289284Srpaulo return; 589289284Srpaulo } 590289284Srpaulo 591289284Srpaulo fst_printf_session(s, MSG_INFO, 592289284Srpaulo "%s: FST Setup established for %s (llt=%u)", 593289284Srpaulo fst_iface_get_name(s->data.old_iface), 594289284Srpaulo fst_iface_get_name(s->data.new_iface), 595289284Srpaulo s->data.llt_ms); 596289284Srpaulo 597289284Srpaulo fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL); 598289284Srpaulo 599289284Srpaulo if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY) 600289284Srpaulo fst_session_initiate_switch(s); 601289284Srpaulo} 602289284Srpaulo 603289284Srpaulo 604289284Srpaulostatic void fst_session_handle_tear_down(struct fst_session *s, 605289284Srpaulo struct fst_iface *iface, 606289284Srpaulo const struct ieee80211_mgmt *mgmt, 607289284Srpaulo size_t frame_len) 608289284Srpaulo{ 609289284Srpaulo const struct fst_tear_down *td; 610289284Srpaulo size_t plen = frame_len - IEEE80211_HDRLEN - 1; 611289284Srpaulo union fst_session_state_switch_extra evext = { 612289284Srpaulo .to_initial = { 613289284Srpaulo .reason = REASON_TEARDOWN, 614289284Srpaulo .initiator = FST_INITIATOR_REMOTE, 615289284Srpaulo }, 616289284Srpaulo }; 617289284Srpaulo 618289284Srpaulo if (plen < sizeof(*td)) { 619289284Srpaulo fst_printf_session(s, MSG_WARNING, 620289284Srpaulo "Too short FST Tear Down dropped"); 621289284Srpaulo return; 622289284Srpaulo } 623289284Srpaulo td = (const struct fst_tear_down *) 624289284Srpaulo (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 625289284Srpaulo 626289284Srpaulo if (le_to_host32(td->fsts_id) != s->data.fsts_id) { 627289284Srpaulo fst_printf_siface(s, iface, MSG_WARNING, 628289284Srpaulo "tear down for wrong FST Setup ID (%u)", 629289284Srpaulo le_to_host32(td->fsts_id)); 630289284Srpaulo return; 631289284Srpaulo } 632289284Srpaulo 633289284Srpaulo fst_session_stt_disarm(s); 634289284Srpaulo 635289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 636289284Srpaulo} 637289284Srpaulo 638289284Srpaulo 639289284Srpaulostatic void fst_session_handle_ack_request(struct fst_session *s, 640289284Srpaulo struct fst_iface *iface, 641289284Srpaulo const struct ieee80211_mgmt *mgmt, 642289284Srpaulo size_t frame_len) 643289284Srpaulo{ 644289284Srpaulo const struct fst_ack_req *req; 645289284Srpaulo size_t plen = frame_len - IEEE80211_HDRLEN - 1; 646289284Srpaulo struct fst_ack_res res; 647289284Srpaulo union fst_session_state_switch_extra evext = { 648289284Srpaulo .to_initial = { 649289284Srpaulo .reason = REASON_SWITCH, 650289284Srpaulo .initiator = FST_INITIATOR_REMOTE, 651289284Srpaulo }, 652289284Srpaulo }; 653289284Srpaulo 654289284Srpaulo if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) { 655289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 656289284Srpaulo "cannot initiate switch due to wrong session state (%s)", 657289284Srpaulo fst_session_state_name(s->state)); 658289284Srpaulo return; 659289284Srpaulo } 660289284Srpaulo 661289284Srpaulo WPA_ASSERT(s->data.new_iface != NULL); 662289284Srpaulo 663289284Srpaulo if (iface != s->data.new_iface) { 664289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 665289284Srpaulo "Ack received on wrong interface"); 666289284Srpaulo return; 667289284Srpaulo } 668289284Srpaulo 669289284Srpaulo if (plen < sizeof(*req)) { 670289284Srpaulo fst_printf_session(s, MSG_WARNING, 671289284Srpaulo "Too short FST Ack Request dropped"); 672289284Srpaulo return; 673289284Srpaulo } 674289284Srpaulo req = (const struct fst_ack_req *) 675289284Srpaulo (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 676289284Srpaulo 677289284Srpaulo if (le_to_host32(req->fsts_id) != s->data.fsts_id) { 678289284Srpaulo fst_printf_siface(s, iface, MSG_WARNING, 679289284Srpaulo "Ack for wrong FST Setup ID (%u)", 680289284Srpaulo le_to_host32(req->fsts_id)); 681289284Srpaulo return; 682289284Srpaulo } 683289284Srpaulo 684289284Srpaulo os_memset(&res, 0, sizeof(res)); 685289284Srpaulo 686289284Srpaulo res.action = FST_ACTION_ACK_RESPONSE; 687289284Srpaulo res.dialog_token = req->dialog_token; 688289284Srpaulo res.fsts_id = req->fsts_id; 689289284Srpaulo 690289284Srpaulo if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) { 691289284Srpaulo fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent"); 692289284Srpaulo fst_session_stt_disarm(s); 693289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, 694289284Srpaulo NULL); 695289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, 696289284Srpaulo NULL); 697289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 698289284Srpaulo } 699289284Srpaulo} 700289284Srpaulo 701289284Srpaulo 702289284Srpaulostatic void 703289284Srpaulofst_session_handle_ack_response(struct fst_session *s, 704289284Srpaulo struct fst_iface *iface, 705289284Srpaulo const struct ieee80211_mgmt *mgmt, 706289284Srpaulo size_t frame_len) 707289284Srpaulo{ 708289284Srpaulo const struct fst_ack_res *res; 709289284Srpaulo size_t plen = frame_len - IEEE80211_HDRLEN - 1; 710289284Srpaulo union fst_session_state_switch_extra evext = { 711289284Srpaulo .to_initial = { 712289284Srpaulo .reason = REASON_SWITCH, 713289284Srpaulo .initiator = FST_INITIATOR_LOCAL, 714289284Srpaulo }, 715289284Srpaulo }; 716289284Srpaulo 717289284Srpaulo if (!fst_session_is_switch_requested(s)) { 718289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 719289284Srpaulo "Ack Response in inappropriate session state (%s)", 720289284Srpaulo fst_session_state_name(s->state)); 721289284Srpaulo return; 722289284Srpaulo } 723289284Srpaulo 724289284Srpaulo WPA_ASSERT(s->data.new_iface != NULL); 725289284Srpaulo 726289284Srpaulo if (iface != s->data.new_iface) { 727289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 728289284Srpaulo "Ack response received on wrong interface"); 729289284Srpaulo return; 730289284Srpaulo } 731289284Srpaulo 732289284Srpaulo if (plen < sizeof(*res)) { 733289284Srpaulo fst_printf_session(s, MSG_WARNING, 734289284Srpaulo "Too short FST Ack Response dropped"); 735289284Srpaulo return; 736289284Srpaulo } 737289284Srpaulo res = (const struct fst_ack_res *) 738289284Srpaulo (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); 739289284Srpaulo 740289284Srpaulo if (le_to_host32(res->fsts_id) != s->data.fsts_id) { 741289284Srpaulo fst_printf_siface(s, iface, MSG_ERROR, 742289284Srpaulo "Ack response for wrong FST Setup ID (%u)", 743289284Srpaulo le_to_host32(res->fsts_id)); 744289284Srpaulo return; 745289284Srpaulo } 746289284Srpaulo 747289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL); 748289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 749289284Srpaulo 750289284Srpaulo fst_session_stt_disarm(s); 751289284Srpaulo} 752289284Srpaulo 753289284Srpaulo 754289284Srpaulostruct fst_session * fst_session_create(struct fst_group *g) 755289284Srpaulo{ 756289284Srpaulo struct fst_session *s; 757289284Srpaulo u32 id; 758289284Srpaulo 759289284Srpaulo id = fst_find_free_session_id(); 760289284Srpaulo if (id == FST_INVALID_SESSION_ID) { 761289284Srpaulo fst_printf(MSG_ERROR, "Cannot assign new session ID"); 762289284Srpaulo return NULL; 763289284Srpaulo } 764289284Srpaulo 765289284Srpaulo s = os_zalloc(sizeof(*s)); 766289284Srpaulo if (!s) { 767289284Srpaulo fst_printf(MSG_ERROR, "Cannot allocate new session object"); 768289284Srpaulo return NULL; 769289284Srpaulo } 770289284Srpaulo 771289284Srpaulo s->id = id; 772289284Srpaulo s->group = g; 773289284Srpaulo s->state = FST_SESSION_STATE_INITIAL; 774289284Srpaulo 775289284Srpaulo s->data.llt_ms = FST_LLT_MS_DEFAULT; 776289284Srpaulo 777289284Srpaulo fst_printf(MSG_INFO, "Session %u created", s->id); 778289284Srpaulo 779289284Srpaulo dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry); 780289284Srpaulo 781289284Srpaulo foreach_fst_ctrl_call(on_session_added, s); 782289284Srpaulo 783289284Srpaulo return s; 784289284Srpaulo} 785289284Srpaulo 786289284Srpaulo 787289284Srpaulovoid fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, 788289284Srpaulo Boolean is_old) 789289284Srpaulo{ 790289284Srpaulo if (is_old) 791289284Srpaulo s->data.old_iface = iface; 792289284Srpaulo else 793289284Srpaulo s->data.new_iface = iface; 794289284Srpaulo 795289284Srpaulo} 796289284Srpaulo 797289284Srpaulo 798289284Srpaulovoid fst_session_set_llt(struct fst_session *s, u32 llt) 799289284Srpaulo{ 800289284Srpaulo s->data.llt_ms = llt; 801289284Srpaulo} 802289284Srpaulo 803289284Srpaulo 804289284Srpaulovoid fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, 805289284Srpaulo Boolean is_old) 806289284Srpaulo{ 807289284Srpaulo u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr; 808289284Srpaulo 809289284Srpaulo os_memcpy(a, addr, ETH_ALEN); 810289284Srpaulo} 811289284Srpaulo 812289284Srpaulo 813289284Srpauloint fst_session_initiate_setup(struct fst_session *s) 814289284Srpaulo{ 815289284Srpaulo struct fst_setup_req req; 816289284Srpaulo int res; 817289284Srpaulo u32 fsts_id; 818289284Srpaulo u8 dialog_token; 819289284Srpaulo struct fst_session *_s; 820289284Srpaulo 821289284Srpaulo if (fst_session_is_in_progress(s)) { 822289284Srpaulo fst_printf_session(s, MSG_ERROR, "Session in progress"); 823289284Srpaulo return -EINVAL; 824289284Srpaulo } 825289284Srpaulo 826289284Srpaulo if (is_zero_ether_addr(s->data.old_peer_addr)) { 827289284Srpaulo fst_printf_session(s, MSG_ERROR, "No old peer MAC address"); 828289284Srpaulo return -EINVAL; 829289284Srpaulo } 830289284Srpaulo 831289284Srpaulo if (is_zero_ether_addr(s->data.new_peer_addr)) { 832289284Srpaulo fst_printf_session(s, MSG_ERROR, "No new peer MAC address"); 833289284Srpaulo return -EINVAL; 834289284Srpaulo } 835289284Srpaulo 836289284Srpaulo if (!s->data.old_iface) { 837289284Srpaulo fst_printf_session(s, MSG_ERROR, "No old interface defined"); 838289284Srpaulo return -EINVAL; 839289284Srpaulo } 840289284Srpaulo 841289284Srpaulo if (!s->data.new_iface) { 842289284Srpaulo fst_printf_session(s, MSG_ERROR, "No new interface defined"); 843289284Srpaulo return -EINVAL; 844289284Srpaulo } 845289284Srpaulo 846289284Srpaulo if (s->data.new_iface == s->data.old_iface) { 847289284Srpaulo fst_printf_session(s, MSG_ERROR, 848289284Srpaulo "Same interface set as old and new"); 849289284Srpaulo return -EINVAL; 850289284Srpaulo } 851289284Srpaulo 852337817Scy if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr, 853337817Scy FALSE)) { 854289284Srpaulo fst_printf_session(s, MSG_ERROR, 855289284Srpaulo "The preset old peer address is not connected"); 856289284Srpaulo return -EINVAL; 857289284Srpaulo } 858289284Srpaulo 859337817Scy if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr, 860337817Scy FALSE)) { 861289284Srpaulo fst_printf_session(s, MSG_ERROR, 862289284Srpaulo "The preset new peer address is not connected"); 863289284Srpaulo return -EINVAL; 864289284Srpaulo } 865289284Srpaulo 866289284Srpaulo _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group); 867289284Srpaulo if (_s) { 868289284Srpaulo fst_printf_session(s, MSG_ERROR, 869289284Srpaulo "There is another session in progress (old): %u", 870289284Srpaulo _s->id); 871289284Srpaulo return -EINVAL; 872289284Srpaulo } 873289284Srpaulo 874289284Srpaulo _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group); 875289284Srpaulo if (_s) { 876289284Srpaulo fst_printf_session(s, MSG_ERROR, 877289284Srpaulo "There is another session in progress (new): %u", 878289284Srpaulo _s->id); 879289284Srpaulo return -EINVAL; 880289284Srpaulo } 881289284Srpaulo 882289284Srpaulo dialog_token = fst_group_assign_dialog_token(s->group); 883289284Srpaulo fsts_id = fst_group_assign_fsts_id(s->group); 884289284Srpaulo 885289284Srpaulo os_memset(&req, 0, sizeof(req)); 886289284Srpaulo 887289284Srpaulo fst_printf_siface(s, s->data.old_iface, MSG_INFO, 888289284Srpaulo "initiating FST setup for %s (llt=%u ms)", 889289284Srpaulo fst_iface_get_name(s->data.new_iface), s->data.llt_ms); 890289284Srpaulo 891289284Srpaulo req.action = FST_ACTION_SETUP_REQUEST; 892289284Srpaulo req.dialog_token = dialog_token; 893289284Srpaulo req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms)); 894289284Srpaulo /* 8.4.2.147 Session Transition element */ 895289284Srpaulo req.stie.element_id = WLAN_EID_SESSION_TRANSITION; 896289284Srpaulo req.stie.length = sizeof(req.stie) - 2; 897289284Srpaulo req.stie.fsts_id = host_to_le32(fsts_id); 898289284Srpaulo req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 899289284Srpaulo 900289284Srpaulo req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface); 901289284Srpaulo req.stie.new_band_op = 1; 902289284Srpaulo req.stie.new_band_setup = 0; 903289284Srpaulo 904289284Srpaulo req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface); 905289284Srpaulo req.stie.old_band_op = 1; 906289284Srpaulo req.stie.old_band_setup = 0; 907289284Srpaulo 908289284Srpaulo res = fst_session_send_action(s, TRUE, &req, sizeof(req), 909289284Srpaulo fst_iface_get_mbie(s->data.old_iface)); 910289284Srpaulo if (!res) { 911289284Srpaulo s->data.fsts_id = fsts_id; 912289284Srpaulo s->data.pending_setup_req_dlgt = dialog_token; 913289284Srpaulo fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent"); 914289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, 915289284Srpaulo NULL); 916289284Srpaulo 917289284Srpaulo fst_session_stt_arm(s); 918289284Srpaulo } 919289284Srpaulo 920289284Srpaulo return res; 921289284Srpaulo} 922289284Srpaulo 923289284Srpaulo 924289284Srpauloint fst_session_respond(struct fst_session *s, u8 status_code) 925289284Srpaulo{ 926289284Srpaulo struct fst_setup_res res; 927289284Srpaulo enum hostapd_hw_mode hw_mode; 928289284Srpaulo u8 channel; 929289284Srpaulo 930289284Srpaulo if (!fst_session_is_ready_pending(s)) { 931289284Srpaulo fst_printf_session(s, MSG_ERROR, "incorrect state: %s", 932289284Srpaulo fst_session_state_name(s->state)); 933289284Srpaulo return -EINVAL; 934289284Srpaulo } 935289284Srpaulo 936289284Srpaulo if (is_zero_ether_addr(s->data.old_peer_addr)) { 937289284Srpaulo fst_printf_session(s, MSG_ERROR, "No peer MAC address"); 938289284Srpaulo return -EINVAL; 939289284Srpaulo } 940289284Srpaulo 941289284Srpaulo if (!s->data.old_iface) { 942289284Srpaulo fst_printf_session(s, MSG_ERROR, "No old interface defined"); 943289284Srpaulo return -EINVAL; 944289284Srpaulo } 945289284Srpaulo 946289284Srpaulo if (!s->data.new_iface) { 947289284Srpaulo fst_printf_session(s, MSG_ERROR, "No new interface defined"); 948289284Srpaulo return -EINVAL; 949289284Srpaulo } 950289284Srpaulo 951289284Srpaulo if (s->data.new_iface == s->data.old_iface) { 952289284Srpaulo fst_printf_session(s, MSG_ERROR, 953289284Srpaulo "Same interface set as old and new"); 954289284Srpaulo return -EINVAL; 955289284Srpaulo } 956289284Srpaulo 957337817Scy if (!fst_iface_is_connected(s->data.old_iface, 958337817Scy s->data.old_peer_addr, FALSE)) { 959289284Srpaulo fst_printf_session(s, MSG_ERROR, 960289284Srpaulo "The preset peer address is not in the peer list"); 961289284Srpaulo return -EINVAL; 962289284Srpaulo } 963289284Srpaulo 964289284Srpaulo fst_session_stt_disarm(s); 965289284Srpaulo 966289284Srpaulo os_memset(&res, 0, sizeof(res)); 967289284Srpaulo 968289284Srpaulo res.action = FST_ACTION_SETUP_RESPONSE; 969289284Srpaulo res.dialog_token = s->data.pending_setup_req_dlgt; 970289284Srpaulo res.status_code = status_code; 971289284Srpaulo 972289284Srpaulo res.stie.element_id = WLAN_EID_SESSION_TRANSITION; 973289284Srpaulo res.stie.length = sizeof(res.stie) - 2; 974289284Srpaulo 975289284Srpaulo if (status_code == WLAN_STATUS_SUCCESS) { 976337817Scy res.stie.fsts_id = host_to_le32(s->data.fsts_id); 977289284Srpaulo res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 978289284Srpaulo 979289284Srpaulo fst_iface_get_channel_info(s->data.new_iface, &hw_mode, 980289284Srpaulo &channel); 981289284Srpaulo res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 982289284Srpaulo res.stie.new_band_op = 1; 983289284Srpaulo res.stie.new_band_setup = 0; 984289284Srpaulo 985289284Srpaulo fst_iface_get_channel_info(s->data.old_iface, &hw_mode, 986289284Srpaulo &channel); 987289284Srpaulo res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 988289284Srpaulo res.stie.old_band_op = 1; 989289284Srpaulo res.stie.old_band_setup = 0; 990289284Srpaulo 991289284Srpaulo fst_printf_session(s, MSG_INFO, 992289284Srpaulo "%s: FST Setup Request accepted for %s (llt=%u)", 993289284Srpaulo fst_iface_get_name(s->data.old_iface), 994289284Srpaulo fst_iface_get_name(s->data.new_iface), 995289284Srpaulo s->data.llt_ms); 996289284Srpaulo } else { 997289284Srpaulo fst_printf_session(s, MSG_WARNING, 998289284Srpaulo "%s: FST Setup Request rejected with code %d", 999289284Srpaulo fst_iface_get_name(s->data.old_iface), 1000289284Srpaulo status_code); 1001289284Srpaulo } 1002289284Srpaulo 1003289284Srpaulo if (fst_session_send_action(s, TRUE, &res, sizeof(res), 1004289284Srpaulo fst_iface_get_mbie(s->data.old_iface))) { 1005289284Srpaulo fst_printf_sframe(s, TRUE, MSG_ERROR, 1006289284Srpaulo "cannot send FST Setup Response with code %d", 1007289284Srpaulo status_code); 1008289284Srpaulo return -EINVAL; 1009289284Srpaulo } 1010289284Srpaulo 1011289284Srpaulo fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent"); 1012289284Srpaulo 1013289284Srpaulo if (status_code != WLAN_STATUS_SUCCESS) { 1014289284Srpaulo union fst_session_state_switch_extra evext = { 1015289284Srpaulo .to_initial = { 1016289284Srpaulo .reason = REASON_REJECT, 1017289284Srpaulo .reject_code = status_code, 1018289284Srpaulo .initiator = FST_INITIATOR_LOCAL, 1019289284Srpaulo }, 1020289284Srpaulo }; 1021289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 1022289284Srpaulo } 1023289284Srpaulo 1024289284Srpaulo return 0; 1025289284Srpaulo} 1026289284Srpaulo 1027289284Srpaulo 1028289284Srpauloint fst_session_initiate_switch(struct fst_session *s) 1029289284Srpaulo{ 1030289284Srpaulo struct fst_ack_req req; 1031289284Srpaulo int res; 1032289284Srpaulo u8 dialog_token; 1033289284Srpaulo 1034289284Srpaulo if (!fst_session_is_ready(s)) { 1035289284Srpaulo fst_printf_session(s, MSG_ERROR, 1036289284Srpaulo "cannot initiate switch due to wrong setup state (%d)", 1037289284Srpaulo s->state); 1038289284Srpaulo return -1; 1039289284Srpaulo } 1040289284Srpaulo 1041289284Srpaulo dialog_token = fst_group_assign_dialog_token(s->group); 1042289284Srpaulo 1043289284Srpaulo WPA_ASSERT(s->data.new_iface != NULL); 1044289284Srpaulo WPA_ASSERT(s->data.old_iface != NULL); 1045289284Srpaulo 1046289284Srpaulo fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s", 1047289284Srpaulo fst_iface_get_name(s->data.old_iface), 1048289284Srpaulo fst_iface_get_name(s->data.new_iface)); 1049289284Srpaulo 1050289284Srpaulo os_memset(&req, 0, sizeof(req)); 1051289284Srpaulo 1052289284Srpaulo req.action = FST_ACTION_ACK_REQUEST; 1053289284Srpaulo req.dialog_token = dialog_token; 1054289284Srpaulo req.fsts_id = host_to_le32(s->data.fsts_id); 1055289284Srpaulo 1056289284Srpaulo res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL); 1057289284Srpaulo if (!res) { 1058289284Srpaulo fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent"); 1059289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, 1060289284Srpaulo NULL); 1061289284Srpaulo fst_session_stt_arm(s); 1062289284Srpaulo } else { 1063289284Srpaulo fst_printf_sframe(s, FALSE, MSG_ERROR, 1064289284Srpaulo "Cannot send FST Ack Request"); 1065289284Srpaulo } 1066289284Srpaulo 1067289284Srpaulo return res; 1068289284Srpaulo} 1069289284Srpaulo 1070289284Srpaulo 1071289284Srpaulovoid fst_session_handle_action(struct fst_session *s, 1072289284Srpaulo struct fst_iface *iface, 1073289284Srpaulo const struct ieee80211_mgmt *mgmt, 1074289284Srpaulo size_t frame_len) 1075289284Srpaulo{ 1076289284Srpaulo switch (mgmt->u.action.u.fst_action.action) { 1077289284Srpaulo case FST_ACTION_SETUP_REQUEST: 1078289284Srpaulo WPA_ASSERT(0); 1079289284Srpaulo break; 1080289284Srpaulo case FST_ACTION_SETUP_RESPONSE: 1081289284Srpaulo fst_session_handle_setup_response(s, iface, mgmt, frame_len); 1082289284Srpaulo break; 1083289284Srpaulo case FST_ACTION_TEAR_DOWN: 1084289284Srpaulo fst_session_handle_tear_down(s, iface, mgmt, frame_len); 1085289284Srpaulo break; 1086289284Srpaulo case FST_ACTION_ACK_REQUEST: 1087289284Srpaulo fst_session_handle_ack_request(s, iface, mgmt, frame_len); 1088289284Srpaulo break; 1089289284Srpaulo case FST_ACTION_ACK_RESPONSE: 1090289284Srpaulo fst_session_handle_ack_response(s, iface, mgmt, frame_len); 1091289284Srpaulo break; 1092289284Srpaulo case FST_ACTION_ON_CHANNEL_TUNNEL: 1093289284Srpaulo default: 1094289284Srpaulo fst_printf_sframe(s, FALSE, MSG_ERROR, 1095289284Srpaulo "Unsupported FST Action frame"); 1096289284Srpaulo break; 1097289284Srpaulo } 1098289284Srpaulo} 1099289284Srpaulo 1100289284Srpaulo 1101289284Srpauloint fst_session_tear_down_setup(struct fst_session *s) 1102289284Srpaulo{ 1103289284Srpaulo int res; 1104289284Srpaulo union fst_session_state_switch_extra evext = { 1105289284Srpaulo .to_initial = { 1106289284Srpaulo .reason = REASON_TEARDOWN, 1107289284Srpaulo .initiator = FST_INITIATOR_LOCAL, 1108289284Srpaulo }, 1109289284Srpaulo }; 1110289284Srpaulo 1111289284Srpaulo res = fst_session_send_tear_down(s); 1112289284Srpaulo 1113289284Srpaulo fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); 1114289284Srpaulo 1115289284Srpaulo return res; 1116289284Srpaulo} 1117289284Srpaulo 1118289284Srpaulo 1119289284Srpaulovoid fst_session_reset(struct fst_session *s) 1120289284Srpaulo{ 1121289284Srpaulo fst_session_reset_ex(s, REASON_RESET); 1122289284Srpaulo} 1123289284Srpaulo 1124289284Srpaulo 1125289284Srpaulovoid fst_session_delete(struct fst_session *s) 1126289284Srpaulo{ 1127289284Srpaulo fst_printf(MSG_INFO, "Session %u deleted", s->id); 1128289284Srpaulo dl_list_del(&s->global_sessions_lentry); 1129289284Srpaulo foreach_fst_ctrl_call(on_session_removed, s); 1130289284Srpaulo os_free(s); 1131289284Srpaulo} 1132289284Srpaulo 1133289284Srpaulo 1134289284Srpaulostruct fst_group * fst_session_get_group(struct fst_session *s) 1135289284Srpaulo{ 1136289284Srpaulo return s->group; 1137289284Srpaulo} 1138289284Srpaulo 1139289284Srpaulo 1140289284Srpaulostruct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old) 1141289284Srpaulo{ 1142289284Srpaulo return is_old ? s->data.old_iface : s->data.new_iface; 1143289284Srpaulo} 1144289284Srpaulo 1145289284Srpaulo 1146289284Srpaulou32 fst_session_get_id(struct fst_session *s) 1147289284Srpaulo{ 1148289284Srpaulo return s->id; 1149289284Srpaulo} 1150289284Srpaulo 1151289284Srpaulo 1152289284Srpauloconst u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old) 1153289284Srpaulo{ 1154289284Srpaulo return is_old ? s->data.old_peer_addr : s->data.new_peer_addr; 1155289284Srpaulo} 1156289284Srpaulo 1157289284Srpaulo 1158289284Srpaulou32 fst_session_get_llt(struct fst_session *s) 1159289284Srpaulo{ 1160289284Srpaulo return s->data.llt_ms; 1161289284Srpaulo} 1162289284Srpaulo 1163289284Srpaulo 1164289284Srpauloenum fst_session_state fst_session_get_state(struct fst_session *s) 1165289284Srpaulo{ 1166289284Srpaulo return s->state; 1167289284Srpaulo} 1168289284Srpaulo 1169289284Srpaulo 1170289284Srpaulostruct fst_session * fst_session_get_by_id(u32 id) 1171289284Srpaulo{ 1172289284Srpaulo struct fst_session *s; 1173289284Srpaulo 1174289284Srpaulo foreach_fst_session(s) { 1175289284Srpaulo if (id == s->id) 1176289284Srpaulo return s; 1177289284Srpaulo } 1178289284Srpaulo 1179289284Srpaulo return NULL; 1180289284Srpaulo} 1181289284Srpaulo 1182289284Srpaulo 1183289284Srpaulovoid fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx) 1184289284Srpaulo{ 1185289284Srpaulo struct fst_session *s; 1186289284Srpaulo 1187289284Srpaulo foreach_fst_session(s) { 1188289284Srpaulo if (!g || s->group == g) 1189289284Srpaulo clb(s->group, s, ctx); 1190289284Srpaulo } 1191289284Srpaulo} 1192289284Srpaulo 1193289284Srpaulo 1194289284Srpaulovoid fst_session_on_action_rx(struct fst_iface *iface, 1195289284Srpaulo const struct ieee80211_mgmt *mgmt, 1196289284Srpaulo size_t len) 1197289284Srpaulo{ 1198289284Srpaulo struct fst_session *s; 1199289284Srpaulo 1200289284Srpaulo if (len < IEEE80211_HDRLEN + 2 || 1201289284Srpaulo mgmt->u.action.category != WLAN_ACTION_FST) { 1202289284Srpaulo fst_printf_iface(iface, MSG_ERROR, 1203289284Srpaulo "invalid Action frame received"); 1204289284Srpaulo return; 1205289284Srpaulo } 1206289284Srpaulo 1207289284Srpaulo if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) { 1208289284Srpaulo fst_printf_iface(iface, MSG_DEBUG, 1209289284Srpaulo "FST Action '%s' received!", 1210289284Srpaulo fst_action_names[mgmt->u.action.u.fst_action.action]); 1211289284Srpaulo } else { 1212289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 1213289284Srpaulo "unknown FST Action (%u) received!", 1214289284Srpaulo mgmt->u.action.u.fst_action.action); 1215289284Srpaulo return; 1216289284Srpaulo } 1217289284Srpaulo 1218289284Srpaulo if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) { 1219289284Srpaulo fst_session_handle_setup_request(iface, mgmt, len); 1220289284Srpaulo return; 1221289284Srpaulo } 1222289284Srpaulo 1223289284Srpaulo s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface)); 1224289284Srpaulo if (s) { 1225289284Srpaulo fst_session_handle_action(s, iface, mgmt, len); 1226289284Srpaulo } else { 1227289284Srpaulo fst_printf_iface(iface, MSG_WARNING, 1228289284Srpaulo "FST Action '%s' dropped: no session in progress found", 1229289284Srpaulo fst_action_names[mgmt->u.action.u.fst_action.action]); 1230289284Srpaulo } 1231289284Srpaulo} 1232289284Srpaulo 1233289284Srpaulo 1234289284Srpauloint fst_session_set_str_ifname(struct fst_session *s, const char *ifname, 1235289284Srpaulo Boolean is_old) 1236289284Srpaulo{ 1237289284Srpaulo struct fst_group *g = fst_session_get_group(s); 1238289284Srpaulo struct fst_iface *i; 1239289284Srpaulo 1240289284Srpaulo i = fst_group_get_iface_by_name(g, ifname); 1241289284Srpaulo if (!i) { 1242289284Srpaulo fst_printf_session(s, MSG_WARNING, 1243289284Srpaulo "Cannot set iface %s: no such iface within group '%s'", 1244289284Srpaulo ifname, fst_group_get_id(g)); 1245289284Srpaulo return -1; 1246289284Srpaulo } 1247289284Srpaulo 1248289284Srpaulo fst_session_set_iface(s, i, is_old); 1249289284Srpaulo 1250289284Srpaulo return 0; 1251289284Srpaulo} 1252289284Srpaulo 1253289284Srpaulo 1254289284Srpauloint fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, 1255289284Srpaulo Boolean is_old) 1256289284Srpaulo{ 1257289284Srpaulo u8 peer_addr[ETH_ALEN]; 1258289284Srpaulo int res = fst_read_peer_addr(mac, peer_addr); 1259289284Srpaulo 1260289284Srpaulo if (res) 1261289284Srpaulo return res; 1262289284Srpaulo 1263289284Srpaulo fst_session_set_peer_addr(s, peer_addr, is_old); 1264289284Srpaulo 1265289284Srpaulo return 0; 1266289284Srpaulo} 1267289284Srpaulo 1268289284Srpaulo 1269289284Srpauloint fst_session_set_str_llt(struct fst_session *s, const char *llt_str) 1270289284Srpaulo{ 1271289284Srpaulo char *endp; 1272289284Srpaulo long int llt = strtol(llt_str, &endp, 0); 1273289284Srpaulo 1274289284Srpaulo if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) { 1275289284Srpaulo fst_printf_session(s, MSG_WARNING, 1276289284Srpaulo "Cannot set llt %s: Invalid llt value (1..%u expected)", 1277289284Srpaulo llt_str, FST_MAX_LLT_MS); 1278289284Srpaulo return -1; 1279289284Srpaulo } 1280289284Srpaulo fst_session_set_llt(s, (u32) llt); 1281289284Srpaulo 1282289284Srpaulo return 0; 1283289284Srpaulo} 1284289284Srpaulo 1285289284Srpaulo 1286289284Srpaulovoid fst_session_global_on_iface_detached(struct fst_iface *iface) 1287289284Srpaulo{ 1288289284Srpaulo struct fst_session *s; 1289289284Srpaulo 1290289284Srpaulo foreach_fst_session(s) { 1291289284Srpaulo if (fst_session_is_in_progress(s) && 1292289284Srpaulo (s->data.new_iface == iface || 1293289284Srpaulo s->data.old_iface == iface)) 1294289284Srpaulo fst_session_reset_ex(s, REASON_DETACH_IFACE); 1295289284Srpaulo } 1296289284Srpaulo} 1297289284Srpaulo 1298289284Srpaulo 1299289284Srpaulostruct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) 1300289284Srpaulo{ 1301289284Srpaulo struct fst_session *s; 1302289284Srpaulo 1303289284Srpaulo foreach_fst_session(s) { 1304289284Srpaulo if (s->group == g) 1305289284Srpaulo return s; 1306289284Srpaulo } 1307289284Srpaulo 1308289284Srpaulo return NULL; 1309289284Srpaulo} 1310289284Srpaulo 1311289284Srpaulo 1312289284Srpaulo#ifdef CONFIG_FST_TEST 1313289284Srpaulo 1314289284Srpaulostatic int get_group_fill_session(struct fst_group **g, struct fst_session *s) 1315289284Srpaulo{ 1316289284Srpaulo const u8 *old_addr, *new_addr; 1317289284Srpaulo struct fst_get_peer_ctx *ctx; 1318289284Srpaulo 1319289284Srpaulo os_memset(s, 0, sizeof(*s)); 1320289284Srpaulo foreach_fst_group(*g) { 1321289284Srpaulo s->data.new_iface = fst_group_first_iface(*g); 1322289284Srpaulo if (s->data.new_iface) 1323289284Srpaulo break; 1324289284Srpaulo } 1325289284Srpaulo if (!s->data.new_iface) 1326289284Srpaulo return -EINVAL; 1327289284Srpaulo 1328289284Srpaulo s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, 1329289284Srpaulo struct fst_iface, group_lentry); 1330289284Srpaulo if (!s->data.old_iface) 1331289284Srpaulo return -EINVAL; 1332289284Srpaulo 1333289284Srpaulo old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); 1334289284Srpaulo if (!old_addr) 1335289284Srpaulo return -EINVAL; 1336289284Srpaulo 1337289284Srpaulo new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); 1338289284Srpaulo if (!new_addr) 1339289284Srpaulo return -EINVAL; 1340289284Srpaulo 1341289284Srpaulo os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); 1342289284Srpaulo os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); 1343289284Srpaulo 1344289284Srpaulo return 0; 1345289284Srpaulo} 1346289284Srpaulo 1347289284Srpaulo 1348289284Srpaulo#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 1349289284Srpaulo 1350289284Srpauloint fst_test_req_send_fst_request(const char *params) 1351289284Srpaulo{ 1352289284Srpaulo int fsts_id; 1353289284Srpaulo Boolean is_valid; 1354289284Srpaulo char *endp; 1355289284Srpaulo struct fst_setup_req req; 1356289284Srpaulo struct fst_session s; 1357289284Srpaulo struct fst_group *g; 1358289284Srpaulo enum hostapd_hw_mode hw_mode; 1359289284Srpaulo u8 channel; 1360289284Srpaulo char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1361289284Srpaulo 1362289284Srpaulo if (params[0] != ' ') 1363289284Srpaulo return -EINVAL; 1364289284Srpaulo params++; 1365289284Srpaulo fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1366289284Srpaulo if (!is_valid) 1367289284Srpaulo return -EINVAL; 1368289284Srpaulo 1369289284Srpaulo if (get_group_fill_session(&g, &s)) 1370289284Srpaulo return -EINVAL; 1371289284Srpaulo 1372289284Srpaulo req.action = FST_ACTION_SETUP_REQUEST; 1373289284Srpaulo req.dialog_token = g->dialog_token; 1374289284Srpaulo req.llt = host_to_le32(FST_LLT_MS_DEFAULT); 1375289284Srpaulo /* 8.4.2.147 Session Transition element */ 1376289284Srpaulo req.stie.element_id = WLAN_EID_SESSION_TRANSITION; 1377289284Srpaulo req.stie.length = sizeof(req.stie) - 2; 1378289284Srpaulo req.stie.fsts_id = host_to_le32(fsts_id); 1379289284Srpaulo req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 1380289284Srpaulo 1381289284Srpaulo fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); 1382289284Srpaulo req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 1383289284Srpaulo req.stie.new_band_op = 1; 1384289284Srpaulo req.stie.new_band_setup = 0; 1385289284Srpaulo 1386289284Srpaulo fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); 1387289284Srpaulo req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 1388289284Srpaulo req.stie.old_band_op = 1; 1389289284Srpaulo req.stie.old_band_setup = 0; 1390289284Srpaulo 1391289284Srpaulo if (!fst_read_next_text_param(endp, additional_param, 1392289284Srpaulo sizeof(additional_param), &endp)) { 1393289284Srpaulo if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) 1394289284Srpaulo req.stie.new_band_id = req.stie.old_band_id; 1395289284Srpaulo } 1396289284Srpaulo 1397289284Srpaulo return fst_session_send_action(&s, TRUE, &req, sizeof(req), 1398289284Srpaulo s.data.old_iface->mb_ie); 1399289284Srpaulo} 1400289284Srpaulo 1401289284Srpaulo 1402289284Srpauloint fst_test_req_send_fst_response(const char *params) 1403289284Srpaulo{ 1404289284Srpaulo int fsts_id; 1405289284Srpaulo Boolean is_valid; 1406289284Srpaulo char *endp; 1407289284Srpaulo struct fst_setup_res res; 1408289284Srpaulo struct fst_session s; 1409289284Srpaulo struct fst_group *g; 1410289284Srpaulo enum hostapd_hw_mode hw_mode; 1411289284Srpaulo u8 status_code; 1412289284Srpaulo u8 channel; 1413289284Srpaulo char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1414289284Srpaulo struct fst_session *_s; 1415289284Srpaulo 1416289284Srpaulo if (params[0] != ' ') 1417289284Srpaulo return -EINVAL; 1418289284Srpaulo params++; 1419289284Srpaulo fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1420289284Srpaulo if (!is_valid) 1421289284Srpaulo return -EINVAL; 1422289284Srpaulo 1423289284Srpaulo if (get_group_fill_session(&g, &s)) 1424289284Srpaulo return -EINVAL; 1425289284Srpaulo 1426289284Srpaulo status_code = WLAN_STATUS_SUCCESS; 1427289284Srpaulo if (!fst_read_next_text_param(endp, response, sizeof(response), 1428289284Srpaulo &endp)) { 1429289284Srpaulo if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) 1430289284Srpaulo status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; 1431289284Srpaulo } 1432289284Srpaulo 1433289284Srpaulo os_memset(&res, 0, sizeof(res)); 1434289284Srpaulo 1435289284Srpaulo res.action = FST_ACTION_SETUP_RESPONSE; 1436289284Srpaulo /* 1437289284Srpaulo * If some session has just received an FST Setup Request, then 1438289284Srpaulo * use the correct dialog token copied from this request. 1439289284Srpaulo */ 1440289284Srpaulo _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), 1441289284Srpaulo g); 1442289284Srpaulo res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? 1443289284Srpaulo _s->data.pending_setup_req_dlgt : g->dialog_token; 1444289284Srpaulo res.status_code = status_code; 1445289284Srpaulo 1446289284Srpaulo res.stie.element_id = WLAN_EID_SESSION_TRANSITION; 1447289284Srpaulo res.stie.length = sizeof(res.stie) - 2; 1448289284Srpaulo 1449289284Srpaulo if (res.status_code == WLAN_STATUS_SUCCESS) { 1450337817Scy res.stie.fsts_id = host_to_le32(fsts_id); 1451289284Srpaulo res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); 1452289284Srpaulo 1453289284Srpaulo fst_iface_get_channel_info(s.data.new_iface, &hw_mode, 1454289284Srpaulo &channel); 1455289284Srpaulo res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); 1456289284Srpaulo res.stie.new_band_op = 1; 1457289284Srpaulo res.stie.new_band_setup = 0; 1458289284Srpaulo 1459289284Srpaulo fst_iface_get_channel_info(s.data.old_iface, &hw_mode, 1460289284Srpaulo &channel); 1461289284Srpaulo res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); 1462289284Srpaulo res.stie.old_band_op = 1; 1463289284Srpaulo res.stie.old_band_setup = 0; 1464289284Srpaulo } 1465289284Srpaulo 1466289284Srpaulo if (!fst_read_next_text_param(endp, response, sizeof(response), 1467289284Srpaulo &endp)) { 1468289284Srpaulo if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) 1469289284Srpaulo res.stie.new_band_id = res.stie.old_band_id; 1470289284Srpaulo } 1471289284Srpaulo 1472289284Srpaulo return fst_session_send_action(&s, TRUE, &res, sizeof(res), 1473289284Srpaulo s.data.old_iface->mb_ie); 1474289284Srpaulo} 1475289284Srpaulo 1476289284Srpaulo 1477289284Srpauloint fst_test_req_send_ack_request(const char *params) 1478289284Srpaulo{ 1479289284Srpaulo int fsts_id; 1480289284Srpaulo Boolean is_valid; 1481289284Srpaulo char *endp; 1482289284Srpaulo struct fst_ack_req req; 1483289284Srpaulo struct fst_session s; 1484289284Srpaulo struct fst_group *g; 1485289284Srpaulo 1486289284Srpaulo if (params[0] != ' ') 1487289284Srpaulo return -EINVAL; 1488289284Srpaulo params++; 1489289284Srpaulo fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1490289284Srpaulo if (!is_valid) 1491289284Srpaulo return -EINVAL; 1492289284Srpaulo 1493289284Srpaulo if (get_group_fill_session(&g, &s)) 1494289284Srpaulo return -EINVAL; 1495289284Srpaulo 1496289284Srpaulo os_memset(&req, 0, sizeof(req)); 1497289284Srpaulo req.action = FST_ACTION_ACK_REQUEST; 1498289284Srpaulo req.dialog_token = g->dialog_token; 1499337817Scy req.fsts_id = host_to_le32(fsts_id); 1500289284Srpaulo 1501289284Srpaulo return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); 1502289284Srpaulo} 1503289284Srpaulo 1504289284Srpaulo 1505289284Srpauloint fst_test_req_send_ack_response(const char *params) 1506289284Srpaulo{ 1507289284Srpaulo int fsts_id; 1508289284Srpaulo Boolean is_valid; 1509289284Srpaulo char *endp; 1510289284Srpaulo struct fst_ack_res res; 1511289284Srpaulo struct fst_session s; 1512289284Srpaulo struct fst_group *g; 1513289284Srpaulo 1514289284Srpaulo if (params[0] != ' ') 1515289284Srpaulo return -EINVAL; 1516289284Srpaulo params++; 1517289284Srpaulo fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1518289284Srpaulo if (!is_valid) 1519289284Srpaulo return -EINVAL; 1520289284Srpaulo 1521289284Srpaulo if (get_group_fill_session(&g, &s)) 1522289284Srpaulo return -EINVAL; 1523289284Srpaulo 1524289284Srpaulo os_memset(&res, 0, sizeof(res)); 1525289284Srpaulo res.action = FST_ACTION_ACK_RESPONSE; 1526289284Srpaulo res.dialog_token = g->dialog_token; 1527337817Scy res.fsts_id = host_to_le32(fsts_id); 1528289284Srpaulo 1529289284Srpaulo return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); 1530289284Srpaulo} 1531289284Srpaulo 1532289284Srpaulo 1533289284Srpauloint fst_test_req_send_tear_down(const char *params) 1534289284Srpaulo{ 1535289284Srpaulo int fsts_id; 1536289284Srpaulo Boolean is_valid; 1537289284Srpaulo char *endp; 1538289284Srpaulo struct fst_tear_down td; 1539289284Srpaulo struct fst_session s; 1540289284Srpaulo struct fst_group *g; 1541289284Srpaulo 1542289284Srpaulo if (params[0] != ' ') 1543289284Srpaulo return -EINVAL; 1544289284Srpaulo params++; 1545289284Srpaulo fsts_id = fst_read_next_int_param(params, &is_valid, &endp); 1546289284Srpaulo if (!is_valid) 1547289284Srpaulo return -EINVAL; 1548289284Srpaulo 1549289284Srpaulo if (get_group_fill_session(&g, &s)) 1550289284Srpaulo return -EINVAL; 1551289284Srpaulo 1552289284Srpaulo os_memset(&td, 0, sizeof(td)); 1553289284Srpaulo td.action = FST_ACTION_TEAR_DOWN; 1554337817Scy td.fsts_id = host_to_le32(fsts_id); 1555289284Srpaulo 1556289284Srpaulo return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); 1557289284Srpaulo} 1558289284Srpaulo 1559289284Srpaulo 1560289284Srpaulou32 fst_test_req_get_fsts_id(const char *params) 1561289284Srpaulo{ 1562289284Srpaulo int sid; 1563289284Srpaulo Boolean is_valid; 1564289284Srpaulo char *endp; 1565289284Srpaulo struct fst_session *s; 1566289284Srpaulo 1567289284Srpaulo if (params[0] != ' ') 1568289284Srpaulo return FST_FSTS_ID_NOT_FOUND; 1569289284Srpaulo params++; 1570289284Srpaulo sid = fst_read_next_int_param(params, &is_valid, &endp); 1571289284Srpaulo if (!is_valid) 1572289284Srpaulo return FST_FSTS_ID_NOT_FOUND; 1573289284Srpaulo 1574289284Srpaulo s = fst_session_get_by_id(sid); 1575289284Srpaulo if (!s) 1576289284Srpaulo return FST_FSTS_ID_NOT_FOUND; 1577289284Srpaulo 1578289284Srpaulo return s->data.fsts_id; 1579289284Srpaulo} 1580289284Srpaulo 1581289284Srpaulo 1582289284Srpauloint fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) 1583289284Srpaulo{ 1584289284Srpaulo char *endp; 1585289284Srpaulo char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; 1586289284Srpaulo struct fst_group *g; 1587289284Srpaulo struct fst_iface *iface; 1588289284Srpaulo 1589289284Srpaulo if (request[0] != ' ') 1590289284Srpaulo return -EINVAL; 1591289284Srpaulo request++; 1592289284Srpaulo if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || 1593289284Srpaulo !*ifname) 1594289284Srpaulo goto problem; 1595289284Srpaulo g = dl_list_first(&fst_global_groups_list, struct fst_group, 1596289284Srpaulo global_groups_lentry); 1597289284Srpaulo if (!g) 1598289284Srpaulo goto problem; 1599289284Srpaulo iface = fst_group_get_iface_by_name(g, ifname); 1600289284Srpaulo if (!iface || !iface->mb_ie) 1601289284Srpaulo goto problem; 1602289284Srpaulo return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie), 1603289284Srpaulo wpabuf_len(iface->mb_ie)); 1604289284Srpaulo 1605289284Srpauloproblem: 1606289284Srpaulo return os_snprintf(buf, buflen, "FAIL\n"); 1607289284Srpaulo} 1608289284Srpaulo 1609289284Srpaulo#endif /* CONFIG_FST_TEST */ 1610