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