fst_session.c revision 337817
1289284Srpaulo/*
2289284Srpaulo * FST module - FST Session implementation
3289284Srpaulo * Copyright (c) 2014, Qualcomm Atheros, Inc.
4289284Srpaulo *
5289284Srpaulo * This software may be distributed under the terms of the BSD license.
6289284Srpaulo * See README for more details.
7289284Srpaulo */
8289284Srpaulo
9289284Srpaulo#include "utils/includes.h"
10289284Srpaulo
11289284Srpaulo#include "utils/common.h"
12289284Srpaulo#include "utils/eloop.h"
13289284Srpaulo#include "common/defs.h"
14289284Srpaulo#include "fst/fst_internal.h"
15289284Srpaulo#include "fst/fst_defs.h"
16289284Srpaulo#include "fst/fst_ctrl_iface.h"
17289284Srpaulo#ifdef CONFIG_FST_TEST
18289284Srpaulo#include "fst/fst_ctrl_defs.h"
19289284Srpaulo#endif /* CONFIG_FST_TEST */
20289284Srpaulo
21289284Srpaulo#define US_80211_TU 1024
22289284Srpaulo
23289284Srpaulo#define US_TO_TU(m) ((m) * / US_80211_TU)
24289284Srpaulo#define TU_TO_US(m) ((m) * US_80211_TU)
25289284Srpaulo
26289284Srpaulo#define FST_LLT_SWITCH_IMMEDIATELY 0
27289284Srpaulo
28289284Srpaulo#define fst_printf_session(s, level, format, ...) \
29289284Srpaulo	fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \
30289284Srpaulo		   (s)->id, (s)->data.fsts_id, \
31289284Srpaulo		   MAC2STR((s)->data.old_peer_addr), \
32289284Srpaulo		   MAC2STR((s)->data.new_peer_addr), \
33289284Srpaulo		   ##__VA_ARGS__)
34289284Srpaulo
35289284Srpaulo#define fst_printf_siface(s, iface, level, format, ...) \
36289284Srpaulo	fst_printf_session((s), (level), "%s: " format, \
37289284Srpaulo			   fst_iface_get_name(iface), ##__VA_ARGS__)
38289284Srpaulo
39289284Srpaulo#define fst_printf_sframe(s, is_old, level, format, ...) \
40289284Srpaulo	fst_printf_siface((s), \
41289284Srpaulo		(is_old) ? (s)->data.old_iface : (s)->data.new_iface, \
42289284Srpaulo		(level), format, ##__VA_ARGS__)
43289284Srpaulo
44289284Srpaulo#define FST_LLT_MS_DEFAULT 50
45289284Srpaulo#define FST_ACTION_MAX_SUPPORTED   FST_ACTION_ON_CHANNEL_TUNNEL
46289284Srpaulo
47337817Scystatic const char * const fst_action_names[] = {
48289284Srpaulo	[FST_ACTION_SETUP_REQUEST]     = "Setup Request",
49289284Srpaulo	[FST_ACTION_SETUP_RESPONSE]    = "Setup Response",
50289284Srpaulo	[FST_ACTION_TEAR_DOWN]         = "Tear Down",
51289284Srpaulo	[FST_ACTION_ACK_REQUEST]       = "Ack Request",
52289284Srpaulo	[FST_ACTION_ACK_RESPONSE]      = "Ack Response",
53289284Srpaulo	[FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel",
54289284Srpaulo};
55289284Srpaulo
56289284Srpaulostruct fst_session {
57289284Srpaulo	struct {
58289284Srpaulo		/* Session configuration that can be zeroed on reset */
59289284Srpaulo		u8 old_peer_addr[ETH_ALEN];
60289284Srpaulo		u8 new_peer_addr[ETH_ALEN];
61289284Srpaulo		struct fst_iface *new_iface;
62289284Srpaulo		struct fst_iface *old_iface;
63289284Srpaulo		u32 llt_ms;
64289284Srpaulo		u8 pending_setup_req_dlgt;
65289284Srpaulo		u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147
66289284Srpaulo			      * Session Transition element */
67289284Srpaulo	} data;
68289284Srpaulo	/* Session object internal fields which won't be zeroed on reset */
69289284Srpaulo	struct dl_list global_sessions_lentry;
70289284Srpaulo	u32 id; /* Session object ID used to identify
71289284Srpaulo		 * specific session object */
72289284Srpaulo	struct fst_group *group;
73289284Srpaulo	enum fst_session_state state;
74289284Srpaulo	Boolean stt_armed;
75289284Srpaulo};
76289284Srpaulo
77289284Srpaulostatic struct dl_list global_sessions_list;
78289284Srpaulostatic u32 global_session_id = 0;
79289284Srpaulo
80289284Srpaulo#define foreach_fst_session(s) \
81289284Srpaulo	dl_list_for_each((s), &global_sessions_list, \
82289284Srpaulo			 struct fst_session, global_sessions_lentry)
83289284Srpaulo
84289284Srpaulo#define foreach_fst_session_safe(s, temp) \
85289284Srpaulo	dl_list_for_each_safe((s), (temp), &global_sessions_list, \
86289284Srpaulo			      struct fst_session, global_sessions_lentry)
87289284Srpaulo
88289284Srpaulo
89289284Srpaulostatic void fst_session_global_inc_id(void)
90289284Srpaulo{
91289284Srpaulo	global_session_id++;
92289284Srpaulo	if (global_session_id == FST_INVALID_SESSION_ID)
93289284Srpaulo		global_session_id++;
94289284Srpaulo}
95289284Srpaulo
96289284Srpaulo
97289284Srpauloint fst_session_global_init(void)
98289284Srpaulo{
99289284Srpaulo	dl_list_init(&global_sessions_list);
100289284Srpaulo	return 0;
101289284Srpaulo}
102289284Srpaulo
103289284Srpaulo
104289284Srpaulovoid fst_session_global_deinit(void)
105289284Srpaulo{
106289284Srpaulo	WPA_ASSERT(dl_list_empty(&global_sessions_list));
107289284Srpaulo}
108289284Srpaulo
109289284Srpaulo
110289284Srpaulostatic inline void fst_session_notify_ctrl(struct fst_session *s,
111289284Srpaulo					   enum fst_event_type event_type,
112289284Srpaulo					   union fst_event_extra *extra)
113289284Srpaulo{
114289284Srpaulo	foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
115289284Srpaulo}
116289284Srpaulo
117289284Srpaulo
118289284Srpaulostatic void fst_session_set_state(struct fst_session *s,
119289284Srpaulo				  enum fst_session_state state,
120289284Srpaulo				  union fst_session_state_switch_extra *extra)
121289284Srpaulo{
122289284Srpaulo	if (s->state != state) {
123289284Srpaulo		union fst_event_extra evext = {
124289284Srpaulo			.session_state = {
125289284Srpaulo				.old_state = s->state,
126289284Srpaulo				.new_state = state,
127289284Srpaulo			},
128289284Srpaulo		};
129289284Srpaulo
130289284Srpaulo		if (extra)
131289284Srpaulo			evext.session_state.extra = *extra;
132289284Srpaulo		fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
133289284Srpaulo					&evext);
134289284Srpaulo		fst_printf_session(s, MSG_INFO, "State: %s => %s",
135289284Srpaulo				   fst_session_state_name(s->state),
136289284Srpaulo				   fst_session_state_name(state));
137289284Srpaulo		s->state = state;
138289284Srpaulo	}
139289284Srpaulo}
140289284Srpaulo
141289284Srpaulo
142289284Srpaulostatic u32 fst_find_free_session_id(void)
143289284Srpaulo{
144289284Srpaulo	u32 i, id = FST_INVALID_SESSION_ID;
145289284Srpaulo	struct fst_session *s;
146289284Srpaulo
147289284Srpaulo	for (i = 0; i < (u32) -1; i++) {
148289284Srpaulo		Boolean in_use = FALSE;
149289284Srpaulo
150289284Srpaulo		foreach_fst_session(s) {
151289284Srpaulo			if (s->id == global_session_id) {
152289284Srpaulo				fst_session_global_inc_id();
153289284Srpaulo				in_use = TRUE;
154289284Srpaulo				break;
155289284Srpaulo			}
156289284Srpaulo		}
157289284Srpaulo		if (!in_use) {
158289284Srpaulo			id = global_session_id;
159289284Srpaulo			fst_session_global_inc_id();
160289284Srpaulo			break;
161289284Srpaulo		}
162289284Srpaulo	}
163289284Srpaulo
164289284Srpaulo	return id;
165289284Srpaulo}
166289284Srpaulo
167289284Srpaulo
168289284Srpaulostatic void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
169289284Srpaulo{
170289284Srpaulo	struct fst_session *s = user_ctx;
171289284Srpaulo	union fst_session_state_switch_extra extra = {
172289284Srpaulo		.to_initial = {
173289284Srpaulo			.reason = REASON_STT,
174289284Srpaulo		},
175289284Srpaulo	};
176289284Srpaulo
177289284Srpaulo	fst_printf_session(s, MSG_WARNING, "Session State Timeout");
178289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
179289284Srpaulo}
180289284Srpaulo
181289284Srpaulo
182289284Srpaulostatic void fst_session_stt_arm(struct fst_session *s)
183289284Srpaulo{
184337817Scy	/* Action frames sometimes get delayed. Use relaxed timeout (2*) */
185337817Scy	eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
186289284Srpaulo			       fst_session_timeout_handler, NULL, s);
187289284Srpaulo	s->stt_armed = TRUE;
188289284Srpaulo}
189289284Srpaulo
190289284Srpaulo
191289284Srpaulostatic void fst_session_stt_disarm(struct fst_session *s)
192289284Srpaulo{
193289284Srpaulo	if (s->stt_armed) {
194289284Srpaulo		eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
195289284Srpaulo		s->stt_armed = FALSE;
196289284Srpaulo	}
197289284Srpaulo}
198289284Srpaulo
199289284Srpaulo
200289284Srpaulostatic Boolean fst_session_is_in_transition(struct fst_session *s)
201289284Srpaulo{
202289284Srpaulo	/* See spec, 10.32.2.2  Transitioning between states */
203289284Srpaulo	return s->stt_armed;
204289284Srpaulo}
205289284Srpaulo
206289284Srpaulo
207289284Srpaulostatic int fst_session_is_in_progress(struct fst_session *s)
208289284Srpaulo{
209289284Srpaulo	return s->state != FST_SESSION_STATE_INITIAL;
210289284Srpaulo}
211289284Srpaulo
212289284Srpaulo
213289284Srpaulostatic int fst_session_is_ready_pending(struct fst_session *s)
214289284Srpaulo{
215289284Srpaulo	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
216289284Srpaulo		fst_session_is_in_transition(s);
217289284Srpaulo}
218289284Srpaulo
219289284Srpaulo
220289284Srpaulostatic int fst_session_is_ready(struct fst_session *s)
221289284Srpaulo{
222289284Srpaulo	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
223289284Srpaulo		!fst_session_is_in_transition(s);
224289284Srpaulo}
225289284Srpaulo
226289284Srpaulo
227289284Srpaulostatic int fst_session_is_switch_requested(struct fst_session *s)
228289284Srpaulo{
229289284Srpaulo	return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
230289284Srpaulo		fst_session_is_in_transition(s);
231289284Srpaulo}
232289284Srpaulo
233289284Srpaulo
234289284Srpaulostatic struct fst_session *
235289284Srpaulofst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
236289284Srpaulo{
237289284Srpaulo	struct fst_session *s;
238289284Srpaulo
239289284Srpaulo	foreach_fst_session(s) {
240289284Srpaulo		if (s->group == g &&
241289284Srpaulo		    (os_memcmp(s->data.old_peer_addr, peer_addr,
242289284Srpaulo			       ETH_ALEN) == 0 ||
243289284Srpaulo		     os_memcmp(s->data.new_peer_addr, peer_addr,
244289284Srpaulo			       ETH_ALEN) == 0) &&
245289284Srpaulo		    fst_session_is_in_progress(s))
246289284Srpaulo			return s;
247289284Srpaulo	}
248289284Srpaulo
249289284Srpaulo	return NULL;
250289284Srpaulo}
251289284Srpaulo
252289284Srpaulo
253289284Srpaulostatic void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
254289284Srpaulo{
255289284Srpaulo	union fst_session_state_switch_extra evext = {
256289284Srpaulo		.to_initial = {
257289284Srpaulo			.reason = reason,
258289284Srpaulo		},
259289284Srpaulo	};
260289284Srpaulo
261289284Srpaulo	if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
262289284Srpaulo	    s->state == FST_SESSION_STATE_TRANSITION_DONE)
263289284Srpaulo		fst_session_tear_down_setup(s);
264289284Srpaulo	fst_session_stt_disarm(s);
265289284Srpaulo	os_memset(&s->data, 0, sizeof(s->data));
266289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
267289284Srpaulo}
268289284Srpaulo
269289284Srpaulo
270289284Srpaulostatic int fst_session_send_action(struct fst_session *s, Boolean old_iface,
271289284Srpaulo				   const void *payload, size_t size,
272289284Srpaulo				   const struct wpabuf *extra_buf)
273289284Srpaulo{
274289284Srpaulo	size_t len;
275289284Srpaulo	int res;
276289284Srpaulo	struct wpabuf *buf;
277289284Srpaulo	u8 action;
278289284Srpaulo	struct fst_iface *iface =
279289284Srpaulo		old_iface ? s->data.old_iface : s->data.new_iface;
280289284Srpaulo
281289284Srpaulo	WPA_ASSERT(payload != NULL);
282289284Srpaulo	WPA_ASSERT(size != 0);
283289284Srpaulo
284289284Srpaulo	action = *(const u8 *) payload;
285289284Srpaulo
286289284Srpaulo	WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
287289284Srpaulo
288289284Srpaulo	if (!iface) {
289289284Srpaulo		fst_printf_session(s, MSG_ERROR,
290289284Srpaulo				   "no %s interface for FST Action '%s' sending",
291289284Srpaulo				   old_iface ? "old" : "new",
292289284Srpaulo				   fst_action_names[action]);
293289284Srpaulo		return -1;
294289284Srpaulo	}
295289284Srpaulo
296289284Srpaulo	len = sizeof(u8) /* category */ + size;
297289284Srpaulo	if (extra_buf)
298289284Srpaulo		len += wpabuf_size(extra_buf);
299289284Srpaulo
300289284Srpaulo	buf = wpabuf_alloc(len);
301289284Srpaulo	if (!buf) {
302289284Srpaulo		fst_printf_session(s, MSG_ERROR,
303289284Srpaulo				   "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
304289284Srpaulo				   len, fst_action_names[action]);
305289284Srpaulo		return -1;
306289284Srpaulo	}
307289284Srpaulo
308289284Srpaulo	wpabuf_put_u8(buf, WLAN_ACTION_FST);
309289284Srpaulo	wpabuf_put_data(buf, payload, size);
310289284Srpaulo	if (extra_buf)
311289284Srpaulo		wpabuf_put_buf(buf, extra_buf);
312289284Srpaulo
313289284Srpaulo	res = fst_iface_send_action(iface,
314289284Srpaulo				    old_iface ? s->data.old_peer_addr :
315289284Srpaulo				    s->data.new_peer_addr, buf);
316289284Srpaulo	if (res < 0)
317289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
318289284Srpaulo				  "failed to send FST Action '%s'",
319289284Srpaulo				  fst_action_names[action]);
320289284Srpaulo	else
321289284Srpaulo		fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
322289284Srpaulo				  fst_action_names[action]);
323289284Srpaulo	wpabuf_free(buf);
324289284Srpaulo
325289284Srpaulo	return res;
326289284Srpaulo}
327289284Srpaulo
328289284Srpaulo
329289284Srpaulostatic int fst_session_send_tear_down(struct fst_session *s)
330289284Srpaulo{
331289284Srpaulo	struct fst_tear_down td;
332289284Srpaulo	int res;
333289284Srpaulo
334289284Srpaulo	if (!fst_session_is_in_progress(s)) {
335289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
336289284Srpaulo		return -1;
337289284Srpaulo	}
338289284Srpaulo
339289284Srpaulo	WPA_ASSERT(s->data.old_iface != NULL);
340289284Srpaulo	WPA_ASSERT(s->data.new_iface != NULL);
341289284Srpaulo
342289284Srpaulo	os_memset(&td, 0, sizeof(td));
343289284Srpaulo
344289284Srpaulo	td.action = FST_ACTION_TEAR_DOWN;
345289284Srpaulo	td.fsts_id = host_to_le32(s->data.fsts_id);
346289284Srpaulo
347289284Srpaulo	res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
348289284Srpaulo	if (!res)
349289284Srpaulo		fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
350289284Srpaulo	else
351289284Srpaulo		fst_printf_sframe(s, TRUE, MSG_ERROR,
352289284Srpaulo				  "failed to send FST TearDown");
353289284Srpaulo
354289284Srpaulo	return res;
355289284Srpaulo}
356289284Srpaulo
357289284Srpaulo
358289284Srpaulostatic void fst_session_handle_setup_request(struct fst_iface *iface,
359289284Srpaulo					     const struct ieee80211_mgmt *mgmt,
360289284Srpaulo					     size_t frame_len)
361289284Srpaulo{
362289284Srpaulo	struct fst_session *s;
363289284Srpaulo	const struct fst_setup_req *req;
364289284Srpaulo	struct fst_iface *new_iface = NULL;
365289284Srpaulo	struct fst_group *g;
366289284Srpaulo	u8 new_iface_peer_addr[ETH_ALEN];
367289284Srpaulo	size_t plen;
368289284Srpaulo
369289284Srpaulo	if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req))  {
370289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
371289284Srpaulo				 "FST Request dropped: too short (%zu < %zu)",
372289284Srpaulo				 frame_len,
373289284Srpaulo				 IEEE80211_HDRLEN + 1 + sizeof(*req));
374289284Srpaulo		return;
375289284Srpaulo	}
376289284Srpaulo	plen = frame_len - IEEE80211_HDRLEN - 1;
377289284Srpaulo	req = (const struct fst_setup_req *)
378289284Srpaulo		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
379289284Srpaulo	if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
380289284Srpaulo	    req->stie.length < 11) {
381289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
382289284Srpaulo				 "FST Request dropped: invalid STIE");
383289284Srpaulo		return;
384289284Srpaulo	}
385289284Srpaulo
386289284Srpaulo	if (req->stie.new_band_id == req->stie.old_band_id) {
387289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
388289284Srpaulo				 "FST Request dropped: new and old band IDs are the same");
389289284Srpaulo		return;
390289284Srpaulo	}
391289284Srpaulo
392289284Srpaulo	g = fst_iface_get_group(iface);
393289284Srpaulo
394289284Srpaulo	if (plen > sizeof(*req)) {
395289284Srpaulo		fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
396289284Srpaulo				       plen - sizeof(*req));
397289284Srpaulo		fst_printf_iface(iface, MSG_INFO,
398289284Srpaulo				 "FST Request: MB IEs updated for " MACSTR,
399289284Srpaulo				 MAC2STR(mgmt->sa));
400289284Srpaulo	}
401289284Srpaulo
402337817Scy	new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa,
403337817Scy							req->stie.new_band_id,
404337817Scy							new_iface_peer_addr);
405289284Srpaulo	if (!new_iface) {
406289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
407289284Srpaulo				 "FST Request dropped: new iface not found");
408289284Srpaulo		return;
409289284Srpaulo	}
410337817Scy	fst_printf_iface(iface, MSG_INFO,
411337817Scy			 "FST Request: new iface (%s:" MACSTR ") found",
412337817Scy			 fst_iface_get_name(new_iface),
413337817Scy			 MAC2STR(new_iface_peer_addr));
414289284Srpaulo
415289284Srpaulo	s = fst_find_session_in_progress(mgmt->sa, g);
416289284Srpaulo	if (s) {
417289284Srpaulo		union fst_session_state_switch_extra evext = {
418289284Srpaulo			.to_initial = {
419289284Srpaulo				.reason = REASON_SETUP,
420289284Srpaulo			},
421289284Srpaulo		};
422289284Srpaulo
423289284Srpaulo		/*
424289284Srpaulo		 * 10.32.2.2  Transitioning between states:
425289284Srpaulo		 * Upon receipt of an FST Setup Request frame, the responder
426289284Srpaulo		 * shall respond with an FST Setup Response frame unless it has
427289284Srpaulo		 * a pending FST Setup Request frame addressed to the initiator
428289284Srpaulo		 * and the responder has a numerically larger MAC address than
429289284Srpaulo		 * the initiator���s MAC address, in which case, the responder
430289284Srpaulo		 * shall delete the received FST Setup Request.
431289284Srpaulo		 */
432337817Scy		if (fst_session_is_ready_pending(s) &&
433337817Scy		    /* waiting for Setup Response */
434337817Scy		    os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
435289284Srpaulo			fst_printf_session(s, MSG_WARNING,
436289284Srpaulo					   "FST Request dropped due to MAC comparison (our MAC is "
437289284Srpaulo					   MACSTR ")",
438289284Srpaulo					   MAC2STR(mgmt->da));
439289284Srpaulo			return;
440289284Srpaulo		}
441289284Srpaulo
442337817Scy		/*
443337817Scy		 * State is SETUP_COMPLETION (either in transition or not) or
444337817Scy		 * TRANSITION_DONE (in transition).
445337817Scy		 * Setup Request arriving in this state could mean:
446337817Scy		 * 1. peer sent it before receiving our Setup Request (race
447337817Scy		 *    condition)
448337817Scy		 * 2. peer didn't receive our Setup Response. Peer is retrying
449337817Scy		 *    after STT timeout
450337817Scy		 * 3. peer's FST state machines are out of sync due to some
451337817Scy		 *    other reason
452337817Scy		 *
453337817Scy		 * We will reset our session and create a new one instead.
454337817Scy		 */
455289284Srpaulo
456337817Scy		fst_printf_session(s, MSG_WARNING,
457337817Scy			"resetting due to FST request");
458289284Srpaulo
459289284Srpaulo		/*
460289284Srpaulo		 * If FST Setup Request arrived with the same FSTS ID as one we
461337817Scy		 * initialized before, there's no need to tear down the session.
462289284Srpaulo		 * Moreover, as FSTS ID is the same, the other side will
463289284Srpaulo		 * associate this tear down with the session it initiated that
464289284Srpaulo		 * will break the sync.
465289284Srpaulo		 */
466289284Srpaulo		if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
467289284Srpaulo			fst_session_send_tear_down(s);
468289284Srpaulo		else
469289284Srpaulo			fst_printf_session(s, MSG_WARNING,
470289284Srpaulo					   "Skipping TearDown as the FST request has the same FSTS ID as initiated");
471289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
472289284Srpaulo		fst_session_stt_disarm(s);
473289284Srpaulo	}
474289284Srpaulo
475289284Srpaulo	s = fst_session_create(g);
476289284Srpaulo	if (!s) {
477289284Srpaulo		fst_printf(MSG_WARNING,
478289284Srpaulo			   "FST Request dropped: cannot create session for %s and %s",
479289284Srpaulo			   fst_iface_get_name(iface),
480289284Srpaulo			   fst_iface_get_name(new_iface));
481289284Srpaulo		return;
482289284Srpaulo	}
483289284Srpaulo
484289284Srpaulo	fst_session_set_iface(s, iface, TRUE);
485289284Srpaulo	fst_session_set_peer_addr(s, mgmt->sa, TRUE);
486289284Srpaulo	fst_session_set_iface(s, new_iface, FALSE);
487289284Srpaulo	fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
488289284Srpaulo	fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
489289284Srpaulo	s->data.pending_setup_req_dlgt = req->dialog_token;
490289284Srpaulo	s->data.fsts_id = le_to_host32(req->stie.fsts_id);
491289284Srpaulo
492289284Srpaulo	fst_session_stt_arm(s);
493289284Srpaulo
494289284Srpaulo	fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
495289284Srpaulo
496289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
497289284Srpaulo}
498289284Srpaulo
499289284Srpaulo
500289284Srpaulostatic void fst_session_handle_setup_response(struct fst_session *s,
501289284Srpaulo					      struct fst_iface *iface,
502289284Srpaulo					      const struct ieee80211_mgmt *mgmt,
503289284Srpaulo					      size_t frame_len)
504289284Srpaulo{
505289284Srpaulo	const struct fst_setup_res *res;
506289284Srpaulo	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
507289284Srpaulo	enum hostapd_hw_mode hw_mode;
508289284Srpaulo	u8 channel;
509289284Srpaulo	union fst_session_state_switch_extra evext = {
510337817Scy		.to_initial = {
511337817Scy			.reject_code = 0,
512337817Scy		},
513289284Srpaulo	};
514289284Srpaulo
515289284Srpaulo	if (iface != s->data.old_iface) {
516289284Srpaulo		fst_printf_session(s, MSG_WARNING,
517289284Srpaulo				   "FST Response dropped: %s is not the old iface",
518289284Srpaulo				   fst_iface_get_name(iface));
519289284Srpaulo		return;
520289284Srpaulo	}
521289284Srpaulo
522289284Srpaulo	if (!fst_session_is_ready_pending(s)) {
523289284Srpaulo		fst_printf_session(s, MSG_WARNING,
524289284Srpaulo				   "FST Response dropped due to wrong state: %s",
525289284Srpaulo				   fst_session_state_name(s->state));
526289284Srpaulo		return;
527289284Srpaulo	}
528289284Srpaulo
529289284Srpaulo	if (plen < sizeof(*res)) {
530289284Srpaulo		fst_printf_session(s, MSG_WARNING,
531289284Srpaulo				   "Too short FST Response dropped");
532289284Srpaulo		return;
533289284Srpaulo	}
534289284Srpaulo	res = (const struct fst_setup_res *)
535289284Srpaulo		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
536289284Srpaulo	if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
537289284Srpaulo	    res->stie.length < 11) {
538289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
539289284Srpaulo				 "FST Response dropped: invalid STIE");
540289284Srpaulo		return;
541289284Srpaulo	}
542289284Srpaulo
543289284Srpaulo	if (res->dialog_token != s->data.pending_setup_req_dlgt)  {
544289284Srpaulo		fst_printf_session(s, MSG_WARNING,
545289284Srpaulo				   "FST Response dropped due to wrong dialog token (%u != %u)",
546289284Srpaulo				   s->data.pending_setup_req_dlgt,
547289284Srpaulo				   res->dialog_token);
548289284Srpaulo		return;
549289284Srpaulo	}
550289284Srpaulo
551289284Srpaulo	if (res->status_code == WLAN_STATUS_SUCCESS &&
552289284Srpaulo	    le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
553289284Srpaulo		fst_printf_session(s, MSG_WARNING,
554289284Srpaulo				   "FST Response dropped due to wrong FST Session ID (%u)",
555289284Srpaulo				   le_to_host32(res->stie.fsts_id));
556289284Srpaulo		return;
557289284Srpaulo	}
558289284Srpaulo
559289284Srpaulo	fst_session_stt_disarm(s);
560289284Srpaulo
561289284Srpaulo	if (res->status_code != WLAN_STATUS_SUCCESS) {
562289284Srpaulo		/*
563289284Srpaulo		 * 10.32.2.2  Transitioning between states
564289284Srpaulo		 * The initiator shall set the STT to the value of the
565289284Srpaulo		 * FSTSessionTimeOut field at ... and at each ACK frame sent in
566289284Srpaulo		 * response to a received FST Setup Response with the Status
567289284Srpaulo		 * Code field equal to PENDING_ADMITTING_FST_SESSION or
568289284Srpaulo		 * PENDING_GAP_IN_BA_WINDOW.
569289284Srpaulo		 */
570289284Srpaulo		evext.to_initial.reason = REASON_REJECT;
571289284Srpaulo		evext.to_initial.reject_code = res->status_code;
572289284Srpaulo		evext.to_initial.initiator = FST_INITIATOR_REMOTE;
573289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
574289284Srpaulo		fst_printf_session(s, MSG_WARNING,
575289284Srpaulo				   "FST Setup rejected by remote side with status %u",
576289284Srpaulo				   res->status_code);
577289284Srpaulo		return;
578289284Srpaulo	}
579289284Srpaulo
580289284Srpaulo	fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
581289284Srpaulo
582289284Srpaulo	if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
583289284Srpaulo		evext.to_initial.reason = REASON_ERROR_PARAMS;
584289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
585289284Srpaulo		fst_printf_session(s, MSG_WARNING,
586289284Srpaulo				   "invalid FST Setup parameters");
587289284Srpaulo		fst_session_tear_down_setup(s);
588289284Srpaulo		return;
589289284Srpaulo	}
590289284Srpaulo
591289284Srpaulo	fst_printf_session(s, MSG_INFO,
592289284Srpaulo			   "%s: FST Setup established for %s (llt=%u)",
593289284Srpaulo			   fst_iface_get_name(s->data.old_iface),
594289284Srpaulo			   fst_iface_get_name(s->data.new_iface),
595289284Srpaulo			   s->data.llt_ms);
596289284Srpaulo
597289284Srpaulo	fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
598289284Srpaulo
599289284Srpaulo	if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
600289284Srpaulo		fst_session_initiate_switch(s);
601289284Srpaulo}
602289284Srpaulo
603289284Srpaulo
604289284Srpaulostatic void fst_session_handle_tear_down(struct fst_session *s,
605289284Srpaulo					 struct fst_iface *iface,
606289284Srpaulo					 const struct ieee80211_mgmt *mgmt,
607289284Srpaulo					 size_t frame_len)
608289284Srpaulo{
609289284Srpaulo	const struct fst_tear_down *td;
610289284Srpaulo	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
611289284Srpaulo	union fst_session_state_switch_extra evext = {
612289284Srpaulo		.to_initial = {
613289284Srpaulo			.reason = REASON_TEARDOWN,
614289284Srpaulo			.initiator = FST_INITIATOR_REMOTE,
615289284Srpaulo		},
616289284Srpaulo	};
617289284Srpaulo
618289284Srpaulo	if (plen < sizeof(*td)) {
619289284Srpaulo		fst_printf_session(s, MSG_WARNING,
620289284Srpaulo				   "Too short FST Tear Down dropped");
621289284Srpaulo		return;
622289284Srpaulo	}
623289284Srpaulo	td = (const struct fst_tear_down *)
624289284Srpaulo		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
625289284Srpaulo
626289284Srpaulo	if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
627289284Srpaulo		fst_printf_siface(s, iface, MSG_WARNING,
628289284Srpaulo				  "tear down for wrong FST Setup ID (%u)",
629289284Srpaulo				  le_to_host32(td->fsts_id));
630289284Srpaulo		return;
631289284Srpaulo	}
632289284Srpaulo
633289284Srpaulo	fst_session_stt_disarm(s);
634289284Srpaulo
635289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
636289284Srpaulo}
637289284Srpaulo
638289284Srpaulo
639289284Srpaulostatic void fst_session_handle_ack_request(struct fst_session *s,
640289284Srpaulo					   struct fst_iface *iface,
641289284Srpaulo					   const struct ieee80211_mgmt *mgmt,
642289284Srpaulo					   size_t frame_len)
643289284Srpaulo{
644289284Srpaulo	const struct fst_ack_req *req;
645289284Srpaulo	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
646289284Srpaulo	struct fst_ack_res res;
647289284Srpaulo	union fst_session_state_switch_extra evext = {
648289284Srpaulo		.to_initial = {
649289284Srpaulo			.reason = REASON_SWITCH,
650289284Srpaulo			.initiator = FST_INITIATOR_REMOTE,
651289284Srpaulo		},
652289284Srpaulo	};
653289284Srpaulo
654289284Srpaulo	if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
655289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
656289284Srpaulo				  "cannot initiate switch due to wrong session state (%s)",
657289284Srpaulo				  fst_session_state_name(s->state));
658289284Srpaulo		return;
659289284Srpaulo	}
660289284Srpaulo
661289284Srpaulo	WPA_ASSERT(s->data.new_iface != NULL);
662289284Srpaulo
663289284Srpaulo	if (iface != s->data.new_iface) {
664289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
665289284Srpaulo				  "Ack received on wrong interface");
666289284Srpaulo		return;
667289284Srpaulo	}
668289284Srpaulo
669289284Srpaulo	if (plen < sizeof(*req)) {
670289284Srpaulo		fst_printf_session(s, MSG_WARNING,
671289284Srpaulo				   "Too short FST Ack Request dropped");
672289284Srpaulo		return;
673289284Srpaulo	}
674289284Srpaulo	req = (const struct fst_ack_req *)
675289284Srpaulo		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
676289284Srpaulo
677289284Srpaulo	if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
678289284Srpaulo		fst_printf_siface(s, iface, MSG_WARNING,
679289284Srpaulo				  "Ack for wrong FST Setup ID (%u)",
680289284Srpaulo				  le_to_host32(req->fsts_id));
681289284Srpaulo		return;
682289284Srpaulo	}
683289284Srpaulo
684289284Srpaulo	os_memset(&res, 0, sizeof(res));
685289284Srpaulo
686289284Srpaulo	res.action = FST_ACTION_ACK_RESPONSE;
687289284Srpaulo	res.dialog_token = req->dialog_token;
688289284Srpaulo	res.fsts_id = req->fsts_id;
689289284Srpaulo
690289284Srpaulo	if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
691289284Srpaulo		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
692289284Srpaulo		fst_session_stt_disarm(s);
693289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
694289284Srpaulo				      NULL);
695289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
696289284Srpaulo				      NULL);
697289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
698289284Srpaulo	}
699289284Srpaulo}
700289284Srpaulo
701289284Srpaulo
702289284Srpaulostatic void
703289284Srpaulofst_session_handle_ack_response(struct fst_session *s,
704289284Srpaulo				struct fst_iface *iface,
705289284Srpaulo				const struct ieee80211_mgmt *mgmt,
706289284Srpaulo				size_t frame_len)
707289284Srpaulo{
708289284Srpaulo	const struct fst_ack_res *res;
709289284Srpaulo	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
710289284Srpaulo	union fst_session_state_switch_extra evext = {
711289284Srpaulo		.to_initial = {
712289284Srpaulo			.reason = REASON_SWITCH,
713289284Srpaulo			.initiator = FST_INITIATOR_LOCAL,
714289284Srpaulo		},
715289284Srpaulo	};
716289284Srpaulo
717289284Srpaulo	if (!fst_session_is_switch_requested(s)) {
718289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
719289284Srpaulo				  "Ack Response in inappropriate session state (%s)",
720289284Srpaulo				  fst_session_state_name(s->state));
721289284Srpaulo		return;
722289284Srpaulo	}
723289284Srpaulo
724289284Srpaulo	WPA_ASSERT(s->data.new_iface != NULL);
725289284Srpaulo
726289284Srpaulo	if (iface != s->data.new_iface) {
727289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
728289284Srpaulo				  "Ack response received on wrong interface");
729289284Srpaulo		return;
730289284Srpaulo	}
731289284Srpaulo
732289284Srpaulo	if (plen < sizeof(*res)) {
733289284Srpaulo		fst_printf_session(s, MSG_WARNING,
734289284Srpaulo				   "Too short FST Ack Response dropped");
735289284Srpaulo		return;
736289284Srpaulo	}
737289284Srpaulo	res = (const struct fst_ack_res *)
738289284Srpaulo		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
739289284Srpaulo
740289284Srpaulo	if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
741289284Srpaulo		fst_printf_siface(s, iface, MSG_ERROR,
742289284Srpaulo				  "Ack response for wrong FST Setup ID (%u)",
743289284Srpaulo				  le_to_host32(res->fsts_id));
744289284Srpaulo		return;
745289284Srpaulo	}
746289284Srpaulo
747289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
748289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
749289284Srpaulo
750289284Srpaulo	fst_session_stt_disarm(s);
751289284Srpaulo}
752289284Srpaulo
753289284Srpaulo
754289284Srpaulostruct fst_session * fst_session_create(struct fst_group *g)
755289284Srpaulo{
756289284Srpaulo	struct fst_session *s;
757289284Srpaulo	u32 id;
758289284Srpaulo
759289284Srpaulo	WPA_ASSERT(!is_zero_ether_addr(own_addr));
760289284Srpaulo
761289284Srpaulo	id = fst_find_free_session_id();
762289284Srpaulo	if (id == FST_INVALID_SESSION_ID) {
763289284Srpaulo		fst_printf(MSG_ERROR, "Cannot assign new session ID");
764289284Srpaulo		return NULL;
765289284Srpaulo	}
766289284Srpaulo
767289284Srpaulo	s = os_zalloc(sizeof(*s));
768289284Srpaulo	if (!s) {
769289284Srpaulo		fst_printf(MSG_ERROR, "Cannot allocate new session object");
770289284Srpaulo		return NULL;
771289284Srpaulo	}
772289284Srpaulo
773289284Srpaulo	s->id = id;
774289284Srpaulo	s->group = g;
775289284Srpaulo	s->state = FST_SESSION_STATE_INITIAL;
776289284Srpaulo
777289284Srpaulo	s->data.llt_ms = FST_LLT_MS_DEFAULT;
778289284Srpaulo
779289284Srpaulo	fst_printf(MSG_INFO, "Session %u created", s->id);
780289284Srpaulo
781289284Srpaulo	dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
782289284Srpaulo
783289284Srpaulo	foreach_fst_ctrl_call(on_session_added, s);
784289284Srpaulo
785289284Srpaulo	return s;
786289284Srpaulo}
787289284Srpaulo
788289284Srpaulo
789289284Srpaulovoid fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
790289284Srpaulo			   Boolean is_old)
791289284Srpaulo{
792289284Srpaulo	if (is_old)
793289284Srpaulo		s->data.old_iface = iface;
794289284Srpaulo	else
795289284Srpaulo		s->data.new_iface = iface;
796289284Srpaulo
797289284Srpaulo}
798289284Srpaulo
799289284Srpaulo
800289284Srpaulovoid fst_session_set_llt(struct fst_session *s, u32 llt)
801289284Srpaulo{
802289284Srpaulo	s->data.llt_ms = llt;
803289284Srpaulo}
804289284Srpaulo
805289284Srpaulo
806289284Srpaulovoid fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
807289284Srpaulo			       Boolean is_old)
808289284Srpaulo{
809289284Srpaulo	u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
810289284Srpaulo
811289284Srpaulo	os_memcpy(a, addr, ETH_ALEN);
812289284Srpaulo}
813289284Srpaulo
814289284Srpaulo
815289284Srpauloint fst_session_initiate_setup(struct fst_session *s)
816289284Srpaulo{
817289284Srpaulo	struct fst_setup_req req;
818289284Srpaulo	int res;
819289284Srpaulo	u32 fsts_id;
820289284Srpaulo	u8 dialog_token;
821289284Srpaulo	struct fst_session *_s;
822289284Srpaulo
823289284Srpaulo	if (fst_session_is_in_progress(s)) {
824289284Srpaulo		fst_printf_session(s, MSG_ERROR, "Session in progress");
825289284Srpaulo		return -EINVAL;
826289284Srpaulo	}
827289284Srpaulo
828289284Srpaulo	if (is_zero_ether_addr(s->data.old_peer_addr)) {
829289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
830289284Srpaulo		return -EINVAL;
831289284Srpaulo	}
832289284Srpaulo
833289284Srpaulo	if (is_zero_ether_addr(s->data.new_peer_addr)) {
834289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
835289284Srpaulo		return -EINVAL;
836289284Srpaulo	}
837289284Srpaulo
838289284Srpaulo	if (!s->data.old_iface) {
839289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No old interface defined");
840289284Srpaulo		return -EINVAL;
841289284Srpaulo	}
842289284Srpaulo
843289284Srpaulo	if (!s->data.new_iface) {
844289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No new interface defined");
845289284Srpaulo		return -EINVAL;
846289284Srpaulo	}
847289284Srpaulo
848289284Srpaulo	if (s->data.new_iface == s->data.old_iface) {
849289284Srpaulo		fst_printf_session(s, MSG_ERROR,
850289284Srpaulo				   "Same interface set as old and new");
851289284Srpaulo		return -EINVAL;
852289284Srpaulo	}
853289284Srpaulo
854337817Scy	if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
855337817Scy				    FALSE)) {
856289284Srpaulo		fst_printf_session(s, MSG_ERROR,
857289284Srpaulo				   "The preset old peer address is not connected");
858289284Srpaulo		return -EINVAL;
859289284Srpaulo	}
860289284Srpaulo
861337817Scy	if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
862337817Scy				    FALSE)) {
863289284Srpaulo		fst_printf_session(s, MSG_ERROR,
864289284Srpaulo				   "The preset new peer address is not connected");
865289284Srpaulo		return -EINVAL;
866289284Srpaulo	}
867289284Srpaulo
868289284Srpaulo	_s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
869289284Srpaulo	if (_s) {
870289284Srpaulo		fst_printf_session(s, MSG_ERROR,
871289284Srpaulo				   "There is another session in progress (old): %u",
872289284Srpaulo				   _s->id);
873289284Srpaulo		return -EINVAL;
874289284Srpaulo	}
875289284Srpaulo
876289284Srpaulo	_s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
877289284Srpaulo	if (_s) {
878289284Srpaulo		fst_printf_session(s, MSG_ERROR,
879289284Srpaulo				   "There is another session in progress (new): %u",
880289284Srpaulo				   _s->id);
881289284Srpaulo		return -EINVAL;
882289284Srpaulo	}
883289284Srpaulo
884289284Srpaulo	dialog_token = fst_group_assign_dialog_token(s->group);
885289284Srpaulo	fsts_id = fst_group_assign_fsts_id(s->group);
886289284Srpaulo
887289284Srpaulo	os_memset(&req, 0, sizeof(req));
888289284Srpaulo
889289284Srpaulo	fst_printf_siface(s, s->data.old_iface, MSG_INFO,
890289284Srpaulo		"initiating FST setup for %s (llt=%u ms)",
891289284Srpaulo		fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
892289284Srpaulo
893289284Srpaulo	req.action = FST_ACTION_SETUP_REQUEST;
894289284Srpaulo	req.dialog_token = dialog_token;
895289284Srpaulo	req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
896289284Srpaulo	/* 8.4.2.147 Session Transition element */
897289284Srpaulo	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
898289284Srpaulo	req.stie.length = sizeof(req.stie) - 2;
899289284Srpaulo	req.stie.fsts_id = host_to_le32(fsts_id);
900289284Srpaulo	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
901289284Srpaulo
902289284Srpaulo	req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
903289284Srpaulo	req.stie.new_band_op = 1;
904289284Srpaulo	req.stie.new_band_setup = 0;
905289284Srpaulo
906289284Srpaulo	req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
907289284Srpaulo	req.stie.old_band_op = 1;
908289284Srpaulo	req.stie.old_band_setup = 0;
909289284Srpaulo
910289284Srpaulo	res = fst_session_send_action(s, TRUE, &req, sizeof(req),
911289284Srpaulo				      fst_iface_get_mbie(s->data.old_iface));
912289284Srpaulo	if (!res) {
913289284Srpaulo		s->data.fsts_id = fsts_id;
914289284Srpaulo		s->data.pending_setup_req_dlgt = dialog_token;
915289284Srpaulo		fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
916289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
917289284Srpaulo				      NULL);
918289284Srpaulo
919289284Srpaulo		fst_session_stt_arm(s);
920289284Srpaulo	}
921289284Srpaulo
922289284Srpaulo	return res;
923289284Srpaulo}
924289284Srpaulo
925289284Srpaulo
926289284Srpauloint fst_session_respond(struct fst_session *s, u8 status_code)
927289284Srpaulo{
928289284Srpaulo	struct fst_setup_res res;
929289284Srpaulo	enum hostapd_hw_mode hw_mode;
930289284Srpaulo	u8 channel;
931289284Srpaulo
932289284Srpaulo	if (!fst_session_is_ready_pending(s)) {
933289284Srpaulo		fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
934289284Srpaulo				   fst_session_state_name(s->state));
935289284Srpaulo		return -EINVAL;
936289284Srpaulo	}
937289284Srpaulo
938289284Srpaulo	if (is_zero_ether_addr(s->data.old_peer_addr)) {
939289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No peer MAC address");
940289284Srpaulo		return -EINVAL;
941289284Srpaulo	}
942289284Srpaulo
943289284Srpaulo	if (!s->data.old_iface) {
944289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No old interface defined");
945289284Srpaulo		return -EINVAL;
946289284Srpaulo	}
947289284Srpaulo
948289284Srpaulo	if (!s->data.new_iface) {
949289284Srpaulo		fst_printf_session(s, MSG_ERROR, "No new interface defined");
950289284Srpaulo		return -EINVAL;
951289284Srpaulo	}
952289284Srpaulo
953289284Srpaulo	if (s->data.new_iface == s->data.old_iface) {
954289284Srpaulo		fst_printf_session(s, MSG_ERROR,
955289284Srpaulo				   "Same interface set as old and new");
956289284Srpaulo		return -EINVAL;
957289284Srpaulo	}
958289284Srpaulo
959337817Scy	if (!fst_iface_is_connected(s->data.old_iface,
960337817Scy				    s->data.old_peer_addr, FALSE)) {
961289284Srpaulo		fst_printf_session(s, MSG_ERROR,
962289284Srpaulo				   "The preset peer address is not in the peer list");
963289284Srpaulo		return -EINVAL;
964289284Srpaulo	}
965289284Srpaulo
966289284Srpaulo	fst_session_stt_disarm(s);
967289284Srpaulo
968289284Srpaulo	os_memset(&res, 0, sizeof(res));
969289284Srpaulo
970289284Srpaulo	res.action = FST_ACTION_SETUP_RESPONSE;
971289284Srpaulo	res.dialog_token = s->data.pending_setup_req_dlgt;
972289284Srpaulo	res.status_code = status_code;
973289284Srpaulo
974289284Srpaulo	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
975289284Srpaulo	res.stie.length = sizeof(res.stie) - 2;
976289284Srpaulo
977289284Srpaulo	if (status_code == WLAN_STATUS_SUCCESS) {
978337817Scy		res.stie.fsts_id = host_to_le32(s->data.fsts_id);
979289284Srpaulo		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
980289284Srpaulo
981289284Srpaulo		fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
982289284Srpaulo					   &channel);
983289284Srpaulo		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
984289284Srpaulo		res.stie.new_band_op = 1;
985289284Srpaulo		res.stie.new_band_setup = 0;
986289284Srpaulo
987289284Srpaulo		fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
988289284Srpaulo					   &channel);
989289284Srpaulo		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
990289284Srpaulo		res.stie.old_band_op = 1;
991289284Srpaulo		res.stie.old_band_setup = 0;
992289284Srpaulo
993289284Srpaulo		fst_printf_session(s, MSG_INFO,
994289284Srpaulo				   "%s: FST Setup Request accepted for %s (llt=%u)",
995289284Srpaulo				   fst_iface_get_name(s->data.old_iface),
996289284Srpaulo				   fst_iface_get_name(s->data.new_iface),
997289284Srpaulo				   s->data.llt_ms);
998289284Srpaulo	} else {
999289284Srpaulo		fst_printf_session(s, MSG_WARNING,
1000289284Srpaulo				   "%s: FST Setup Request rejected with code %d",
1001289284Srpaulo				   fst_iface_get_name(s->data.old_iface),
1002289284Srpaulo				   status_code);
1003289284Srpaulo	}
1004289284Srpaulo
1005289284Srpaulo	if (fst_session_send_action(s, TRUE, &res, sizeof(res),
1006289284Srpaulo				    fst_iface_get_mbie(s->data.old_iface))) {
1007289284Srpaulo		fst_printf_sframe(s, TRUE, MSG_ERROR,
1008289284Srpaulo				  "cannot send FST Setup Response with code %d",
1009289284Srpaulo				  status_code);
1010289284Srpaulo		return -EINVAL;
1011289284Srpaulo	}
1012289284Srpaulo
1013289284Srpaulo	fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
1014289284Srpaulo
1015289284Srpaulo	if (status_code != WLAN_STATUS_SUCCESS) {
1016289284Srpaulo		union fst_session_state_switch_extra evext = {
1017289284Srpaulo			.to_initial = {
1018289284Srpaulo				.reason = REASON_REJECT,
1019289284Srpaulo				.reject_code = status_code,
1020289284Srpaulo				.initiator = FST_INITIATOR_LOCAL,
1021289284Srpaulo			},
1022289284Srpaulo		};
1023289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1024289284Srpaulo	}
1025289284Srpaulo
1026289284Srpaulo	return 0;
1027289284Srpaulo}
1028289284Srpaulo
1029289284Srpaulo
1030289284Srpauloint fst_session_initiate_switch(struct fst_session *s)
1031289284Srpaulo{
1032289284Srpaulo	struct fst_ack_req req;
1033289284Srpaulo	int res;
1034289284Srpaulo	u8 dialog_token;
1035289284Srpaulo
1036289284Srpaulo	if (!fst_session_is_ready(s)) {
1037289284Srpaulo		fst_printf_session(s, MSG_ERROR,
1038289284Srpaulo				   "cannot initiate switch due to wrong setup state (%d)",
1039289284Srpaulo				   s->state);
1040289284Srpaulo		return -1;
1041289284Srpaulo	}
1042289284Srpaulo
1043289284Srpaulo	dialog_token = fst_group_assign_dialog_token(s->group);
1044289284Srpaulo
1045289284Srpaulo	WPA_ASSERT(s->data.new_iface != NULL);
1046289284Srpaulo	WPA_ASSERT(s->data.old_iface != NULL);
1047289284Srpaulo
1048289284Srpaulo	fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
1049289284Srpaulo			   fst_iface_get_name(s->data.old_iface),
1050289284Srpaulo			   fst_iface_get_name(s->data.new_iface));
1051289284Srpaulo
1052289284Srpaulo	os_memset(&req, 0, sizeof(req));
1053289284Srpaulo
1054289284Srpaulo	req.action = FST_ACTION_ACK_REQUEST;
1055289284Srpaulo	req.dialog_token = dialog_token;
1056289284Srpaulo	req.fsts_id = host_to_le32(s->data.fsts_id);
1057289284Srpaulo
1058289284Srpaulo	res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
1059289284Srpaulo	if (!res) {
1060289284Srpaulo		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
1061289284Srpaulo		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
1062289284Srpaulo				      NULL);
1063289284Srpaulo		fst_session_stt_arm(s);
1064289284Srpaulo	} else {
1065289284Srpaulo		fst_printf_sframe(s, FALSE, MSG_ERROR,
1066289284Srpaulo				  "Cannot send FST Ack Request");
1067289284Srpaulo	}
1068289284Srpaulo
1069289284Srpaulo	return res;
1070289284Srpaulo}
1071289284Srpaulo
1072289284Srpaulo
1073289284Srpaulovoid fst_session_handle_action(struct fst_session *s,
1074289284Srpaulo			       struct fst_iface *iface,
1075289284Srpaulo			       const struct ieee80211_mgmt *mgmt,
1076289284Srpaulo			       size_t frame_len)
1077289284Srpaulo{
1078289284Srpaulo	switch (mgmt->u.action.u.fst_action.action) {
1079289284Srpaulo	case FST_ACTION_SETUP_REQUEST:
1080289284Srpaulo		WPA_ASSERT(0);
1081289284Srpaulo		break;
1082289284Srpaulo	case FST_ACTION_SETUP_RESPONSE:
1083289284Srpaulo		fst_session_handle_setup_response(s, iface, mgmt, frame_len);
1084289284Srpaulo		break;
1085289284Srpaulo	case FST_ACTION_TEAR_DOWN:
1086289284Srpaulo		fst_session_handle_tear_down(s, iface, mgmt, frame_len);
1087289284Srpaulo		break;
1088289284Srpaulo	case FST_ACTION_ACK_REQUEST:
1089289284Srpaulo		fst_session_handle_ack_request(s, iface, mgmt, frame_len);
1090289284Srpaulo		break;
1091289284Srpaulo	case FST_ACTION_ACK_RESPONSE:
1092289284Srpaulo		fst_session_handle_ack_response(s, iface, mgmt, frame_len);
1093289284Srpaulo		break;
1094289284Srpaulo	case FST_ACTION_ON_CHANNEL_TUNNEL:
1095289284Srpaulo	default:
1096289284Srpaulo		fst_printf_sframe(s, FALSE, MSG_ERROR,
1097289284Srpaulo				  "Unsupported FST Action frame");
1098289284Srpaulo		break;
1099289284Srpaulo	}
1100289284Srpaulo}
1101289284Srpaulo
1102289284Srpaulo
1103289284Srpauloint fst_session_tear_down_setup(struct fst_session *s)
1104289284Srpaulo{
1105289284Srpaulo	int res;
1106289284Srpaulo	union fst_session_state_switch_extra evext = {
1107289284Srpaulo		.to_initial = {
1108289284Srpaulo			.reason = REASON_TEARDOWN,
1109289284Srpaulo			.initiator = FST_INITIATOR_LOCAL,
1110289284Srpaulo		},
1111289284Srpaulo	};
1112289284Srpaulo
1113289284Srpaulo	res = fst_session_send_tear_down(s);
1114289284Srpaulo
1115289284Srpaulo	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1116289284Srpaulo
1117289284Srpaulo	return res;
1118289284Srpaulo}
1119289284Srpaulo
1120289284Srpaulo
1121289284Srpaulovoid fst_session_reset(struct fst_session *s)
1122289284Srpaulo{
1123289284Srpaulo	fst_session_reset_ex(s, REASON_RESET);
1124289284Srpaulo}
1125289284Srpaulo
1126289284Srpaulo
1127289284Srpaulovoid fst_session_delete(struct fst_session *s)
1128289284Srpaulo{
1129289284Srpaulo	fst_printf(MSG_INFO, "Session %u deleted", s->id);
1130289284Srpaulo	dl_list_del(&s->global_sessions_lentry);
1131289284Srpaulo	foreach_fst_ctrl_call(on_session_removed, s);
1132289284Srpaulo	os_free(s);
1133289284Srpaulo}
1134289284Srpaulo
1135289284Srpaulo
1136289284Srpaulostruct fst_group * fst_session_get_group(struct fst_session *s)
1137289284Srpaulo{
1138289284Srpaulo	return s->group;
1139289284Srpaulo}
1140289284Srpaulo
1141289284Srpaulo
1142289284Srpaulostruct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
1143289284Srpaulo{
1144289284Srpaulo	return is_old ? s->data.old_iface : s->data.new_iface;
1145289284Srpaulo}
1146289284Srpaulo
1147289284Srpaulo
1148289284Srpaulou32 fst_session_get_id(struct fst_session *s)
1149289284Srpaulo{
1150289284Srpaulo	return s->id;
1151289284Srpaulo}
1152289284Srpaulo
1153289284Srpaulo
1154289284Srpauloconst u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
1155289284Srpaulo{
1156289284Srpaulo	return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
1157289284Srpaulo}
1158289284Srpaulo
1159289284Srpaulo
1160289284Srpaulou32 fst_session_get_llt(struct fst_session *s)
1161289284Srpaulo{
1162289284Srpaulo	return s->data.llt_ms;
1163289284Srpaulo}
1164289284Srpaulo
1165289284Srpaulo
1166289284Srpauloenum fst_session_state fst_session_get_state(struct fst_session *s)
1167289284Srpaulo{
1168289284Srpaulo	return s->state;
1169289284Srpaulo}
1170289284Srpaulo
1171289284Srpaulo
1172289284Srpaulostruct fst_session * fst_session_get_by_id(u32 id)
1173289284Srpaulo{
1174289284Srpaulo	struct fst_session *s;
1175289284Srpaulo
1176289284Srpaulo	foreach_fst_session(s) {
1177289284Srpaulo		if (id == s->id)
1178289284Srpaulo			return s;
1179289284Srpaulo	}
1180289284Srpaulo
1181289284Srpaulo	return NULL;
1182289284Srpaulo}
1183289284Srpaulo
1184289284Srpaulo
1185289284Srpaulovoid fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
1186289284Srpaulo{
1187289284Srpaulo	struct fst_session *s;
1188289284Srpaulo
1189289284Srpaulo	foreach_fst_session(s) {
1190289284Srpaulo		if (!g || s->group == g)
1191289284Srpaulo			clb(s->group, s, ctx);
1192289284Srpaulo	}
1193289284Srpaulo}
1194289284Srpaulo
1195289284Srpaulo
1196289284Srpaulovoid fst_session_on_action_rx(struct fst_iface *iface,
1197289284Srpaulo			      const struct ieee80211_mgmt *mgmt,
1198289284Srpaulo			      size_t len)
1199289284Srpaulo{
1200289284Srpaulo	struct fst_session *s;
1201289284Srpaulo
1202289284Srpaulo	if (len < IEEE80211_HDRLEN + 2 ||
1203289284Srpaulo	    mgmt->u.action.category != WLAN_ACTION_FST) {
1204289284Srpaulo		fst_printf_iface(iface, MSG_ERROR,
1205289284Srpaulo				 "invalid Action frame received");
1206289284Srpaulo		return;
1207289284Srpaulo	}
1208289284Srpaulo
1209289284Srpaulo	if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
1210289284Srpaulo		fst_printf_iface(iface, MSG_DEBUG,
1211289284Srpaulo				 "FST Action '%s' received!",
1212289284Srpaulo				 fst_action_names[mgmt->u.action.u.fst_action.action]);
1213289284Srpaulo	} else {
1214289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
1215289284Srpaulo				 "unknown FST Action (%u) received!",
1216289284Srpaulo				 mgmt->u.action.u.fst_action.action);
1217289284Srpaulo		return;
1218289284Srpaulo	}
1219289284Srpaulo
1220289284Srpaulo	if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
1221289284Srpaulo		fst_session_handle_setup_request(iface, mgmt, len);
1222289284Srpaulo		return;
1223289284Srpaulo	}
1224289284Srpaulo
1225289284Srpaulo	s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
1226289284Srpaulo	if (s) {
1227289284Srpaulo		fst_session_handle_action(s, iface, mgmt, len);
1228289284Srpaulo	} else {
1229289284Srpaulo		fst_printf_iface(iface, MSG_WARNING,
1230289284Srpaulo				 "FST Action '%s' dropped: no session in progress found",
1231289284Srpaulo				 fst_action_names[mgmt->u.action.u.fst_action.action]);
1232289284Srpaulo	}
1233289284Srpaulo}
1234289284Srpaulo
1235289284Srpaulo
1236289284Srpauloint fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
1237289284Srpaulo			       Boolean is_old)
1238289284Srpaulo{
1239289284Srpaulo	struct fst_group *g = fst_session_get_group(s);
1240289284Srpaulo	struct fst_iface *i;
1241289284Srpaulo
1242289284Srpaulo	i = fst_group_get_iface_by_name(g, ifname);
1243289284Srpaulo	if (!i) {
1244289284Srpaulo		fst_printf_session(s, MSG_WARNING,
1245289284Srpaulo				   "Cannot set iface %s: no such iface within group '%s'",
1246289284Srpaulo				   ifname, fst_group_get_id(g));
1247289284Srpaulo		return -1;
1248289284Srpaulo	}
1249289284Srpaulo
1250289284Srpaulo	fst_session_set_iface(s, i, is_old);
1251289284Srpaulo
1252289284Srpaulo	return 0;
1253289284Srpaulo}
1254289284Srpaulo
1255289284Srpaulo
1256289284Srpauloint fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
1257289284Srpaulo				  Boolean is_old)
1258289284Srpaulo{
1259289284Srpaulo	u8 peer_addr[ETH_ALEN];
1260289284Srpaulo	int res = fst_read_peer_addr(mac, peer_addr);
1261289284Srpaulo
1262289284Srpaulo	if (res)
1263289284Srpaulo		return res;
1264289284Srpaulo
1265289284Srpaulo	fst_session_set_peer_addr(s, peer_addr, is_old);
1266289284Srpaulo
1267289284Srpaulo	return 0;
1268289284Srpaulo}
1269289284Srpaulo
1270289284Srpaulo
1271289284Srpauloint fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
1272289284Srpaulo{
1273289284Srpaulo	char *endp;
1274289284Srpaulo	long int llt = strtol(llt_str, &endp, 0);
1275289284Srpaulo
1276289284Srpaulo	if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
1277289284Srpaulo		fst_printf_session(s, MSG_WARNING,
1278289284Srpaulo				   "Cannot set llt %s: Invalid llt value (1..%u expected)",
1279289284Srpaulo				   llt_str, FST_MAX_LLT_MS);
1280289284Srpaulo		return -1;
1281289284Srpaulo	}
1282289284Srpaulo	fst_session_set_llt(s, (u32) llt);
1283289284Srpaulo
1284289284Srpaulo	return 0;
1285289284Srpaulo}
1286289284Srpaulo
1287289284Srpaulo
1288289284Srpaulovoid fst_session_global_on_iface_detached(struct fst_iface *iface)
1289289284Srpaulo{
1290289284Srpaulo	struct fst_session *s;
1291289284Srpaulo
1292289284Srpaulo	foreach_fst_session(s) {
1293289284Srpaulo		if (fst_session_is_in_progress(s) &&
1294289284Srpaulo		    (s->data.new_iface == iface ||
1295289284Srpaulo		     s->data.old_iface == iface))
1296289284Srpaulo			fst_session_reset_ex(s, REASON_DETACH_IFACE);
1297289284Srpaulo	}
1298289284Srpaulo}
1299289284Srpaulo
1300289284Srpaulo
1301289284Srpaulostruct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
1302289284Srpaulo{
1303289284Srpaulo	struct fst_session *s;
1304289284Srpaulo
1305289284Srpaulo	foreach_fst_session(s) {
1306289284Srpaulo		if (s->group == g)
1307289284Srpaulo			return s;
1308289284Srpaulo	}
1309289284Srpaulo
1310289284Srpaulo	return NULL;
1311289284Srpaulo}
1312289284Srpaulo
1313289284Srpaulo
1314289284Srpaulo#ifdef CONFIG_FST_TEST
1315289284Srpaulo
1316289284Srpaulostatic int get_group_fill_session(struct fst_group **g, struct fst_session *s)
1317289284Srpaulo{
1318289284Srpaulo	const u8 *old_addr, *new_addr;
1319289284Srpaulo	struct fst_get_peer_ctx *ctx;
1320289284Srpaulo
1321289284Srpaulo	os_memset(s, 0, sizeof(*s));
1322289284Srpaulo	foreach_fst_group(*g) {
1323289284Srpaulo		s->data.new_iface = fst_group_first_iface(*g);
1324289284Srpaulo		if (s->data.new_iface)
1325289284Srpaulo			break;
1326289284Srpaulo	}
1327289284Srpaulo	if (!s->data.new_iface)
1328289284Srpaulo		return -EINVAL;
1329289284Srpaulo
1330289284Srpaulo	s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
1331289284Srpaulo					  struct fst_iface, group_lentry);
1332289284Srpaulo	if (!s->data.old_iface)
1333289284Srpaulo		return -EINVAL;
1334289284Srpaulo
1335289284Srpaulo	old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
1336289284Srpaulo	if (!old_addr)
1337289284Srpaulo		return -EINVAL;
1338289284Srpaulo
1339289284Srpaulo	new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
1340289284Srpaulo	if (!new_addr)
1341289284Srpaulo		return -EINVAL;
1342289284Srpaulo
1343289284Srpaulo	os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
1344289284Srpaulo	os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
1345289284Srpaulo
1346289284Srpaulo	return 0;
1347289284Srpaulo}
1348289284Srpaulo
1349289284Srpaulo
1350289284Srpaulo#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
1351289284Srpaulo
1352289284Srpauloint fst_test_req_send_fst_request(const char *params)
1353289284Srpaulo{
1354289284Srpaulo	int fsts_id;
1355289284Srpaulo	Boolean is_valid;
1356289284Srpaulo	char *endp;
1357289284Srpaulo	struct fst_setup_req req;
1358289284Srpaulo	struct fst_session s;
1359289284Srpaulo	struct fst_group *g;
1360289284Srpaulo	enum hostapd_hw_mode hw_mode;
1361289284Srpaulo	u8 channel;
1362289284Srpaulo	char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1363289284Srpaulo
1364289284Srpaulo	if (params[0] != ' ')
1365289284Srpaulo		return -EINVAL;
1366289284Srpaulo	params++;
1367289284Srpaulo	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1368289284Srpaulo	if (!is_valid)
1369289284Srpaulo		return -EINVAL;
1370289284Srpaulo
1371289284Srpaulo	if (get_group_fill_session(&g, &s))
1372289284Srpaulo		return -EINVAL;
1373289284Srpaulo
1374289284Srpaulo	req.action = FST_ACTION_SETUP_REQUEST;
1375289284Srpaulo	req.dialog_token = g->dialog_token;
1376289284Srpaulo	req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
1377289284Srpaulo	/* 8.4.2.147 Session Transition element */
1378289284Srpaulo	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1379289284Srpaulo	req.stie.length = sizeof(req.stie) - 2;
1380289284Srpaulo	req.stie.fsts_id = host_to_le32(fsts_id);
1381289284Srpaulo	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1382289284Srpaulo
1383289284Srpaulo	fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
1384289284Srpaulo	req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1385289284Srpaulo	req.stie.new_band_op = 1;
1386289284Srpaulo	req.stie.new_band_setup = 0;
1387289284Srpaulo
1388289284Srpaulo	fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
1389289284Srpaulo	req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1390289284Srpaulo	req.stie.old_band_op = 1;
1391289284Srpaulo	req.stie.old_band_setup = 0;
1392289284Srpaulo
1393289284Srpaulo	if (!fst_read_next_text_param(endp, additional_param,
1394289284Srpaulo				       sizeof(additional_param), &endp)) {
1395289284Srpaulo		if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
1396289284Srpaulo			req.stie.new_band_id = req.stie.old_band_id;
1397289284Srpaulo	}
1398289284Srpaulo
1399289284Srpaulo	return fst_session_send_action(&s, TRUE, &req, sizeof(req),
1400289284Srpaulo				       s.data.old_iface->mb_ie);
1401289284Srpaulo}
1402289284Srpaulo
1403289284Srpaulo
1404289284Srpauloint fst_test_req_send_fst_response(const char *params)
1405289284Srpaulo{
1406289284Srpaulo	int fsts_id;
1407289284Srpaulo	Boolean is_valid;
1408289284Srpaulo	char *endp;
1409289284Srpaulo	struct fst_setup_res res;
1410289284Srpaulo	struct fst_session s;
1411289284Srpaulo	struct fst_group *g;
1412289284Srpaulo	enum hostapd_hw_mode hw_mode;
1413289284Srpaulo	u8 status_code;
1414289284Srpaulo	u8 channel;
1415289284Srpaulo	char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1416289284Srpaulo	struct fst_session *_s;
1417289284Srpaulo
1418289284Srpaulo	if (params[0] != ' ')
1419289284Srpaulo		return -EINVAL;
1420289284Srpaulo	params++;
1421289284Srpaulo	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1422289284Srpaulo	if (!is_valid)
1423289284Srpaulo		return -EINVAL;
1424289284Srpaulo
1425289284Srpaulo	if (get_group_fill_session(&g, &s))
1426289284Srpaulo		return -EINVAL;
1427289284Srpaulo
1428289284Srpaulo	status_code = WLAN_STATUS_SUCCESS;
1429289284Srpaulo	if (!fst_read_next_text_param(endp, response, sizeof(response),
1430289284Srpaulo				      &endp)) {
1431289284Srpaulo		if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
1432289284Srpaulo			status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
1433289284Srpaulo	}
1434289284Srpaulo
1435289284Srpaulo	os_memset(&res, 0, sizeof(res));
1436289284Srpaulo
1437289284Srpaulo	res.action = FST_ACTION_SETUP_RESPONSE;
1438289284Srpaulo	/*
1439289284Srpaulo	 * If some session has just received an FST Setup Request, then
1440289284Srpaulo	 * use the correct dialog token copied from this request.
1441289284Srpaulo	 */
1442289284Srpaulo	_s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
1443289284Srpaulo					  g);
1444289284Srpaulo	res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
1445289284Srpaulo		_s->data.pending_setup_req_dlgt : g->dialog_token;
1446289284Srpaulo	res.status_code  = status_code;
1447289284Srpaulo
1448289284Srpaulo	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1449289284Srpaulo	res.stie.length = sizeof(res.stie) - 2;
1450289284Srpaulo
1451289284Srpaulo	if (res.status_code == WLAN_STATUS_SUCCESS) {
1452337817Scy		res.stie.fsts_id = host_to_le32(fsts_id);
1453289284Srpaulo		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1454289284Srpaulo
1455289284Srpaulo		fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
1456289284Srpaulo					    &channel);
1457289284Srpaulo		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1458289284Srpaulo		res.stie.new_band_op = 1;
1459289284Srpaulo		res.stie.new_band_setup = 0;
1460289284Srpaulo
1461289284Srpaulo		fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
1462289284Srpaulo					   &channel);
1463289284Srpaulo		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1464289284Srpaulo		res.stie.old_band_op = 1;
1465289284Srpaulo		res.stie.old_band_setup = 0;
1466289284Srpaulo	}
1467289284Srpaulo
1468289284Srpaulo	if (!fst_read_next_text_param(endp, response, sizeof(response),
1469289284Srpaulo				      &endp)) {
1470289284Srpaulo		if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
1471289284Srpaulo			res.stie.new_band_id = res.stie.old_band_id;
1472289284Srpaulo	}
1473289284Srpaulo
1474289284Srpaulo	return fst_session_send_action(&s, TRUE, &res, sizeof(res),
1475289284Srpaulo				       s.data.old_iface->mb_ie);
1476289284Srpaulo}
1477289284Srpaulo
1478289284Srpaulo
1479289284Srpauloint fst_test_req_send_ack_request(const char *params)
1480289284Srpaulo{
1481289284Srpaulo	int fsts_id;
1482289284Srpaulo	Boolean is_valid;
1483289284Srpaulo	char *endp;
1484289284Srpaulo	struct fst_ack_req req;
1485289284Srpaulo	struct fst_session s;
1486289284Srpaulo	struct fst_group *g;
1487289284Srpaulo
1488289284Srpaulo	if (params[0] != ' ')
1489289284Srpaulo		return -EINVAL;
1490289284Srpaulo	params++;
1491289284Srpaulo	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1492289284Srpaulo	if (!is_valid)
1493289284Srpaulo		return -EINVAL;
1494289284Srpaulo
1495289284Srpaulo	if (get_group_fill_session(&g, &s))
1496289284Srpaulo		return -EINVAL;
1497289284Srpaulo
1498289284Srpaulo	os_memset(&req, 0, sizeof(req));
1499289284Srpaulo	req.action = FST_ACTION_ACK_REQUEST;
1500289284Srpaulo	req.dialog_token = g->dialog_token;
1501337817Scy	req.fsts_id = host_to_le32(fsts_id);
1502289284Srpaulo
1503289284Srpaulo	return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
1504289284Srpaulo}
1505289284Srpaulo
1506289284Srpaulo
1507289284Srpauloint fst_test_req_send_ack_response(const char *params)
1508289284Srpaulo{
1509289284Srpaulo	int fsts_id;
1510289284Srpaulo	Boolean is_valid;
1511289284Srpaulo	char *endp;
1512289284Srpaulo	struct fst_ack_res res;
1513289284Srpaulo	struct fst_session s;
1514289284Srpaulo	struct fst_group *g;
1515289284Srpaulo
1516289284Srpaulo	if (params[0] != ' ')
1517289284Srpaulo		return -EINVAL;
1518289284Srpaulo	params++;
1519289284Srpaulo	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1520289284Srpaulo	if (!is_valid)
1521289284Srpaulo		return -EINVAL;
1522289284Srpaulo
1523289284Srpaulo	if (get_group_fill_session(&g, &s))
1524289284Srpaulo		return -EINVAL;
1525289284Srpaulo
1526289284Srpaulo	os_memset(&res, 0, sizeof(res));
1527289284Srpaulo	res.action = FST_ACTION_ACK_RESPONSE;
1528289284Srpaulo	res.dialog_token = g->dialog_token;
1529337817Scy	res.fsts_id = host_to_le32(fsts_id);
1530289284Srpaulo
1531289284Srpaulo	return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
1532289284Srpaulo}
1533289284Srpaulo
1534289284Srpaulo
1535289284Srpauloint fst_test_req_send_tear_down(const char *params)
1536289284Srpaulo{
1537289284Srpaulo	int fsts_id;
1538289284Srpaulo	Boolean is_valid;
1539289284Srpaulo	char *endp;
1540289284Srpaulo	struct fst_tear_down td;
1541289284Srpaulo	struct fst_session s;
1542289284Srpaulo	struct fst_group *g;
1543289284Srpaulo
1544289284Srpaulo	if (params[0] != ' ')
1545289284Srpaulo		return -EINVAL;
1546289284Srpaulo	params++;
1547289284Srpaulo	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1548289284Srpaulo	if (!is_valid)
1549289284Srpaulo		return -EINVAL;
1550289284Srpaulo
1551289284Srpaulo	if (get_group_fill_session(&g, &s))
1552289284Srpaulo		return -EINVAL;
1553289284Srpaulo
1554289284Srpaulo	os_memset(&td, 0, sizeof(td));
1555289284Srpaulo	td.action = FST_ACTION_TEAR_DOWN;
1556337817Scy	td.fsts_id = host_to_le32(fsts_id);
1557289284Srpaulo
1558289284Srpaulo	return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
1559289284Srpaulo}
1560289284Srpaulo
1561289284Srpaulo
1562289284Srpaulou32 fst_test_req_get_fsts_id(const char *params)
1563289284Srpaulo{
1564289284Srpaulo	int sid;
1565289284Srpaulo	Boolean is_valid;
1566289284Srpaulo	char *endp;
1567289284Srpaulo	struct fst_session *s;
1568289284Srpaulo
1569289284Srpaulo	if (params[0] != ' ')
1570289284Srpaulo		return FST_FSTS_ID_NOT_FOUND;
1571289284Srpaulo	params++;
1572289284Srpaulo	sid = fst_read_next_int_param(params, &is_valid, &endp);
1573289284Srpaulo	if (!is_valid)
1574289284Srpaulo		return FST_FSTS_ID_NOT_FOUND;
1575289284Srpaulo
1576289284Srpaulo	s = fst_session_get_by_id(sid);
1577289284Srpaulo	if (!s)
1578289284Srpaulo		return FST_FSTS_ID_NOT_FOUND;
1579289284Srpaulo
1580289284Srpaulo	return s->data.fsts_id;
1581289284Srpaulo}
1582289284Srpaulo
1583289284Srpaulo
1584289284Srpauloint fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
1585289284Srpaulo{
1586289284Srpaulo	char *endp;
1587289284Srpaulo	char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1588289284Srpaulo	struct fst_group *g;
1589289284Srpaulo	struct fst_iface *iface;
1590289284Srpaulo
1591289284Srpaulo	if (request[0] != ' ')
1592289284Srpaulo		return -EINVAL;
1593289284Srpaulo	request++;
1594289284Srpaulo	if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
1595289284Srpaulo	    !*ifname)
1596289284Srpaulo		goto problem;
1597289284Srpaulo	g = dl_list_first(&fst_global_groups_list, struct fst_group,
1598289284Srpaulo			  global_groups_lentry);
1599289284Srpaulo	if (!g)
1600289284Srpaulo		goto problem;
1601289284Srpaulo	iface = fst_group_get_iface_by_name(g, ifname);
1602289284Srpaulo	if (!iface || !iface->mb_ie)
1603289284Srpaulo		goto problem;
1604289284Srpaulo	return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
1605289284Srpaulo				wpabuf_len(iface->mb_ie));
1606289284Srpaulo
1607289284Srpauloproblem:
1608289284Srpaulo	return os_snprintf(buf, buflen, "FAIL\n");
1609289284Srpaulo}
1610289284Srpaulo
1611289284Srpaulo#endif /* CONFIG_FST_TEST */
1612