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