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