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