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