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