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