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