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
47static 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
56struct 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
77static struct dl_list global_sessions_list;
78static 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
89static 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
97int fst_session_global_init(void)
98{
99	dl_list_init(&global_sessions_list);
100	return 0;
101}
102
103
104void fst_session_global_deinit(void)
105{
106	WPA_ASSERT(dl_list_empty(&global_sessions_list));
107}
108
109
110static 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
118static 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
142static 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
168static 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
182static 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
191static 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
200static 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
207static int fst_session_is_in_progress(struct fst_session *s)
208{
209	return s->state != FST_SESSION_STATE_INITIAL;
210}
211
212
213static 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
220static 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
227static 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
234static struct fst_session *
235fst_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
253static 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
270static 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
329static 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
358static 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 initiator’s 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
500static 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
604static 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
639static 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
702static void
703fst_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
754struct 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
787void 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
798void fst_session_set_llt(struct fst_session *s, u32 llt)
799{
800	s->data.llt_ms = llt;
801}
802
803
804void 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
813int 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
924int 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
1028int 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
1071void 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
1101int 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
1119void fst_session_reset(struct fst_session *s)
1120{
1121	fst_session_reset_ex(s, REASON_RESET);
1122}
1123
1124
1125void 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
1134struct fst_group * fst_session_get_group(struct fst_session *s)
1135{
1136	return s->group;
1137}
1138
1139
1140struct 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
1146u32 fst_session_get_id(struct fst_session *s)
1147{
1148	return s->id;
1149}
1150
1151
1152const 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
1158u32 fst_session_get_llt(struct fst_session *s)
1159{
1160	return s->data.llt_ms;
1161}
1162
1163
1164enum fst_session_state fst_session_get_state(struct fst_session *s)
1165{
1166	return s->state;
1167}
1168
1169
1170struct 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
1183void 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
1194void 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
1234int 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
1254int 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
1269int 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
1286void 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
1299struct 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
1314static 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
1350int 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
1402int 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
1477int 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
1505int 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
1533int 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
1560u32 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
1582int 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
1605problem:
1606	return os_snprintf(buf, buflen, "FAIL\n");
1607}
1608
1609#endif /* CONFIG_FST_TEST */
1610