xref: /freebsd/contrib/wpa/src/fst/fst_session.c (revision 78b9f0095b4af3aca6c931b2c7b009ddb8a05125)
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 	Boolean 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 		Boolean in_use = FALSE;
149 
150 		foreach_fst_session(s) {
151 			if (s->id == global_session_id) {
152 				fst_session_global_inc_id();
153 				in_use = TRUE;
154 				break;
155 			}
156 		}
157 		if (!in_use) {
158 			id = global_session_id;
159 			fst_session_global_inc_id();
160 			break;
161 		}
162 	}
163 
164 	return id;
165 }
166 
167 
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 Boolean fst_session_is_in_transition(struct fst_session *s)
201 {
202 	/* See spec, 10.32.2.2  Transitioning between states */
203 	return s->stt_armed;
204 }
205 
206 
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, Boolean old_iface,
271 				   const void *payload, size_t size,
272 				   const struct wpabuf *extra_buf)
273 {
274 	size_t len;
275 	int res;
276 	struct wpabuf *buf;
277 	u8 action;
278 	struct fst_iface *iface =
279 		old_iface ? s->data.old_iface : s->data.new_iface;
280 
281 	WPA_ASSERT(payload != NULL);
282 	WPA_ASSERT(size != 0);
283 
284 	action = *(const u8 *) payload;
285 
286 	WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
287 
288 	if (!iface) {
289 		fst_printf_session(s, MSG_ERROR,
290 				   "no %s interface for FST Action '%s' sending",
291 				   old_iface ? "old" : "new",
292 				   fst_action_names[action]);
293 		return -1;
294 	}
295 
296 	len = sizeof(u8) /* category */ + size;
297 	if (extra_buf)
298 		len += wpabuf_size(extra_buf);
299 
300 	buf = wpabuf_alloc(len);
301 	if (!buf) {
302 		fst_printf_session(s, MSG_ERROR,
303 				   "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
304 				   len, fst_action_names[action]);
305 		return -1;
306 	}
307 
308 	wpabuf_put_u8(buf, WLAN_ACTION_FST);
309 	wpabuf_put_data(buf, payload, size);
310 	if (extra_buf)
311 		wpabuf_put_buf(buf, extra_buf);
312 
313 	res = fst_iface_send_action(iface,
314 				    old_iface ? s->data.old_peer_addr :
315 				    s->data.new_peer_addr, buf);
316 	if (res < 0)
317 		fst_printf_siface(s, iface, MSG_ERROR,
318 				  "failed to send FST Action '%s'",
319 				  fst_action_names[action]);
320 	else
321 		fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
322 				  fst_action_names[action]);
323 	wpabuf_free(buf);
324 
325 	return res;
326 }
327 
328 
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 	WPA_ASSERT(!is_zero_ether_addr(own_addr));
760 
761 	id = fst_find_free_session_id();
762 	if (id == FST_INVALID_SESSION_ID) {
763 		fst_printf(MSG_ERROR, "Cannot assign new session ID");
764 		return NULL;
765 	}
766 
767 	s = os_zalloc(sizeof(*s));
768 	if (!s) {
769 		fst_printf(MSG_ERROR, "Cannot allocate new session object");
770 		return NULL;
771 	}
772 
773 	s->id = id;
774 	s->group = g;
775 	s->state = FST_SESSION_STATE_INITIAL;
776 
777 	s->data.llt_ms = FST_LLT_MS_DEFAULT;
778 
779 	fst_printf(MSG_INFO, "Session %u created", s->id);
780 
781 	dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
782 
783 	foreach_fst_ctrl_call(on_session_added, s);
784 
785 	return s;
786 }
787 
788 
789 void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
790 			   Boolean is_old)
791 {
792 	if (is_old)
793 		s->data.old_iface = iface;
794 	else
795 		s->data.new_iface = iface;
796 
797 }
798 
799 
800 void fst_session_set_llt(struct fst_session *s, u32 llt)
801 {
802 	s->data.llt_ms = llt;
803 }
804 
805 
806 void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
807 			       Boolean is_old)
808 {
809 	u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
810 
811 	os_memcpy(a, addr, ETH_ALEN);
812 }
813 
814 
815 int fst_session_initiate_setup(struct fst_session *s)
816 {
817 	struct fst_setup_req req;
818 	int res;
819 	u32 fsts_id;
820 	u8 dialog_token;
821 	struct fst_session *_s;
822 
823 	if (fst_session_is_in_progress(s)) {
824 		fst_printf_session(s, MSG_ERROR, "Session in progress");
825 		return -EINVAL;
826 	}
827 
828 	if (is_zero_ether_addr(s->data.old_peer_addr)) {
829 		fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
830 		return -EINVAL;
831 	}
832 
833 	if (is_zero_ether_addr(s->data.new_peer_addr)) {
834 		fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
835 		return -EINVAL;
836 	}
837 
838 	if (!s->data.old_iface) {
839 		fst_printf_session(s, MSG_ERROR, "No old interface defined");
840 		return -EINVAL;
841 	}
842 
843 	if (!s->data.new_iface) {
844 		fst_printf_session(s, MSG_ERROR, "No new interface defined");
845 		return -EINVAL;
846 	}
847 
848 	if (s->data.new_iface == s->data.old_iface) {
849 		fst_printf_session(s, MSG_ERROR,
850 				   "Same interface set as old and new");
851 		return -EINVAL;
852 	}
853 
854 	if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
855 				    FALSE)) {
856 		fst_printf_session(s, MSG_ERROR,
857 				   "The preset old peer address is not connected");
858 		return -EINVAL;
859 	}
860 
861 	if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
862 				    FALSE)) {
863 		fst_printf_session(s, MSG_ERROR,
864 				   "The preset new peer address is not connected");
865 		return -EINVAL;
866 	}
867 
868 	_s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
869 	if (_s) {
870 		fst_printf_session(s, MSG_ERROR,
871 				   "There is another session in progress (old): %u",
872 				   _s->id);
873 		return -EINVAL;
874 	}
875 
876 	_s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
877 	if (_s) {
878 		fst_printf_session(s, MSG_ERROR,
879 				   "There is another session in progress (new): %u",
880 				   _s->id);
881 		return -EINVAL;
882 	}
883 
884 	dialog_token = fst_group_assign_dialog_token(s->group);
885 	fsts_id = fst_group_assign_fsts_id(s->group);
886 
887 	os_memset(&req, 0, sizeof(req));
888 
889 	fst_printf_siface(s, s->data.old_iface, MSG_INFO,
890 		"initiating FST setup for %s (llt=%u ms)",
891 		fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
892 
893 	req.action = FST_ACTION_SETUP_REQUEST;
894 	req.dialog_token = dialog_token;
895 	req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
896 	/* 8.4.2.147 Session Transition element */
897 	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
898 	req.stie.length = sizeof(req.stie) - 2;
899 	req.stie.fsts_id = host_to_le32(fsts_id);
900 	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
901 
902 	req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
903 	req.stie.new_band_op = 1;
904 	req.stie.new_band_setup = 0;
905 
906 	req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
907 	req.stie.old_band_op = 1;
908 	req.stie.old_band_setup = 0;
909 
910 	res = fst_session_send_action(s, TRUE, &req, sizeof(req),
911 				      fst_iface_get_mbie(s->data.old_iface));
912 	if (!res) {
913 		s->data.fsts_id = fsts_id;
914 		s->data.pending_setup_req_dlgt = dialog_token;
915 		fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
916 		fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
917 				      NULL);
918 
919 		fst_session_stt_arm(s);
920 	}
921 
922 	return res;
923 }
924 
925 
926 int fst_session_respond(struct fst_session *s, u8 status_code)
927 {
928 	struct fst_setup_res res;
929 	enum hostapd_hw_mode hw_mode;
930 	u8 channel;
931 
932 	if (!fst_session_is_ready_pending(s)) {
933 		fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
934 				   fst_session_state_name(s->state));
935 		return -EINVAL;
936 	}
937 
938 	if (is_zero_ether_addr(s->data.old_peer_addr)) {
939 		fst_printf_session(s, MSG_ERROR, "No peer MAC address");
940 		return -EINVAL;
941 	}
942 
943 	if (!s->data.old_iface) {
944 		fst_printf_session(s, MSG_ERROR, "No old interface defined");
945 		return -EINVAL;
946 	}
947 
948 	if (!s->data.new_iface) {
949 		fst_printf_session(s, MSG_ERROR, "No new interface defined");
950 		return -EINVAL;
951 	}
952 
953 	if (s->data.new_iface == s->data.old_iface) {
954 		fst_printf_session(s, MSG_ERROR,
955 				   "Same interface set as old and new");
956 		return -EINVAL;
957 	}
958 
959 	if (!fst_iface_is_connected(s->data.old_iface,
960 				    s->data.old_peer_addr, FALSE)) {
961 		fst_printf_session(s, MSG_ERROR,
962 				   "The preset peer address is not in the peer list");
963 		return -EINVAL;
964 	}
965 
966 	fst_session_stt_disarm(s);
967 
968 	os_memset(&res, 0, sizeof(res));
969 
970 	res.action = FST_ACTION_SETUP_RESPONSE;
971 	res.dialog_token = s->data.pending_setup_req_dlgt;
972 	res.status_code = status_code;
973 
974 	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
975 	res.stie.length = sizeof(res.stie) - 2;
976 
977 	if (status_code == WLAN_STATUS_SUCCESS) {
978 		res.stie.fsts_id = host_to_le32(s->data.fsts_id);
979 		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
980 
981 		fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
982 					   &channel);
983 		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
984 		res.stie.new_band_op = 1;
985 		res.stie.new_band_setup = 0;
986 
987 		fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
988 					   &channel);
989 		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
990 		res.stie.old_band_op = 1;
991 		res.stie.old_band_setup = 0;
992 
993 		fst_printf_session(s, MSG_INFO,
994 				   "%s: FST Setup Request accepted for %s (llt=%u)",
995 				   fst_iface_get_name(s->data.old_iface),
996 				   fst_iface_get_name(s->data.new_iface),
997 				   s->data.llt_ms);
998 	} else {
999 		fst_printf_session(s, MSG_WARNING,
1000 				   "%s: FST Setup Request rejected with code %d",
1001 				   fst_iface_get_name(s->data.old_iface),
1002 				   status_code);
1003 	}
1004 
1005 	if (fst_session_send_action(s, TRUE, &res, sizeof(res),
1006 				    fst_iface_get_mbie(s->data.old_iface))) {
1007 		fst_printf_sframe(s, TRUE, MSG_ERROR,
1008 				  "cannot send FST Setup Response with code %d",
1009 				  status_code);
1010 		return -EINVAL;
1011 	}
1012 
1013 	fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
1014 
1015 	if (status_code != WLAN_STATUS_SUCCESS) {
1016 		union fst_session_state_switch_extra evext = {
1017 			.to_initial = {
1018 				.reason = REASON_REJECT,
1019 				.reject_code = status_code,
1020 				.initiator = FST_INITIATOR_LOCAL,
1021 			},
1022 		};
1023 		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1024 	}
1025 
1026 	return 0;
1027 }
1028 
1029 
1030 int fst_session_initiate_switch(struct fst_session *s)
1031 {
1032 	struct fst_ack_req req;
1033 	int res;
1034 	u8 dialog_token;
1035 
1036 	if (!fst_session_is_ready(s)) {
1037 		fst_printf_session(s, MSG_ERROR,
1038 				   "cannot initiate switch due to wrong setup state (%d)",
1039 				   s->state);
1040 		return -1;
1041 	}
1042 
1043 	dialog_token = fst_group_assign_dialog_token(s->group);
1044 
1045 	WPA_ASSERT(s->data.new_iface != NULL);
1046 	WPA_ASSERT(s->data.old_iface != NULL);
1047 
1048 	fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
1049 			   fst_iface_get_name(s->data.old_iface),
1050 			   fst_iface_get_name(s->data.new_iface));
1051 
1052 	os_memset(&req, 0, sizeof(req));
1053 
1054 	req.action = FST_ACTION_ACK_REQUEST;
1055 	req.dialog_token = dialog_token;
1056 	req.fsts_id = host_to_le32(s->data.fsts_id);
1057 
1058 	res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
1059 	if (!res) {
1060 		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
1061 		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
1062 				      NULL);
1063 		fst_session_stt_arm(s);
1064 	} else {
1065 		fst_printf_sframe(s, FALSE, MSG_ERROR,
1066 				  "Cannot send FST Ack Request");
1067 	}
1068 
1069 	return res;
1070 }
1071 
1072 
1073 void fst_session_handle_action(struct fst_session *s,
1074 			       struct fst_iface *iface,
1075 			       const struct ieee80211_mgmt *mgmt,
1076 			       size_t frame_len)
1077 {
1078 	switch (mgmt->u.action.u.fst_action.action) {
1079 	case FST_ACTION_SETUP_REQUEST:
1080 		WPA_ASSERT(0);
1081 		break;
1082 	case FST_ACTION_SETUP_RESPONSE:
1083 		fst_session_handle_setup_response(s, iface, mgmt, frame_len);
1084 		break;
1085 	case FST_ACTION_TEAR_DOWN:
1086 		fst_session_handle_tear_down(s, iface, mgmt, frame_len);
1087 		break;
1088 	case FST_ACTION_ACK_REQUEST:
1089 		fst_session_handle_ack_request(s, iface, mgmt, frame_len);
1090 		break;
1091 	case FST_ACTION_ACK_RESPONSE:
1092 		fst_session_handle_ack_response(s, iface, mgmt, frame_len);
1093 		break;
1094 	case FST_ACTION_ON_CHANNEL_TUNNEL:
1095 	default:
1096 		fst_printf_sframe(s, FALSE, MSG_ERROR,
1097 				  "Unsupported FST Action frame");
1098 		break;
1099 	}
1100 }
1101 
1102 
1103 int fst_session_tear_down_setup(struct fst_session *s)
1104 {
1105 	int res;
1106 	union fst_session_state_switch_extra evext = {
1107 		.to_initial = {
1108 			.reason = REASON_TEARDOWN,
1109 			.initiator = FST_INITIATOR_LOCAL,
1110 		},
1111 	};
1112 
1113 	res = fst_session_send_tear_down(s);
1114 
1115 	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1116 
1117 	return res;
1118 }
1119 
1120 
1121 void fst_session_reset(struct fst_session *s)
1122 {
1123 	fst_session_reset_ex(s, REASON_RESET);
1124 }
1125 
1126 
1127 void fst_session_delete(struct fst_session *s)
1128 {
1129 	fst_printf(MSG_INFO, "Session %u deleted", s->id);
1130 	dl_list_del(&s->global_sessions_lentry);
1131 	foreach_fst_ctrl_call(on_session_removed, s);
1132 	os_free(s);
1133 }
1134 
1135 
1136 struct fst_group * fst_session_get_group(struct fst_session *s)
1137 {
1138 	return s->group;
1139 }
1140 
1141 
1142 struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
1143 {
1144 	return is_old ? s->data.old_iface : s->data.new_iface;
1145 }
1146 
1147 
1148 u32 fst_session_get_id(struct fst_session *s)
1149 {
1150 	return s->id;
1151 }
1152 
1153 
1154 const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
1155 {
1156 	return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
1157 }
1158 
1159 
1160 u32 fst_session_get_llt(struct fst_session *s)
1161 {
1162 	return s->data.llt_ms;
1163 }
1164 
1165 
1166 enum fst_session_state fst_session_get_state(struct fst_session *s)
1167 {
1168 	return s->state;
1169 }
1170 
1171 
1172 struct fst_session * fst_session_get_by_id(u32 id)
1173 {
1174 	struct fst_session *s;
1175 
1176 	foreach_fst_session(s) {
1177 		if (id == s->id)
1178 			return s;
1179 	}
1180 
1181 	return NULL;
1182 }
1183 
1184 
1185 void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
1186 {
1187 	struct fst_session *s;
1188 
1189 	foreach_fst_session(s) {
1190 		if (!g || s->group == g)
1191 			clb(s->group, s, ctx);
1192 	}
1193 }
1194 
1195 
1196 void fst_session_on_action_rx(struct fst_iface *iface,
1197 			      const struct ieee80211_mgmt *mgmt,
1198 			      size_t len)
1199 {
1200 	struct fst_session *s;
1201 
1202 	if (len < IEEE80211_HDRLEN + 2 ||
1203 	    mgmt->u.action.category != WLAN_ACTION_FST) {
1204 		fst_printf_iface(iface, MSG_ERROR,
1205 				 "invalid Action frame received");
1206 		return;
1207 	}
1208 
1209 	if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
1210 		fst_printf_iface(iface, MSG_DEBUG,
1211 				 "FST Action '%s' received!",
1212 				 fst_action_names[mgmt->u.action.u.fst_action.action]);
1213 	} else {
1214 		fst_printf_iface(iface, MSG_WARNING,
1215 				 "unknown FST Action (%u) received!",
1216 				 mgmt->u.action.u.fst_action.action);
1217 		return;
1218 	}
1219 
1220 	if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
1221 		fst_session_handle_setup_request(iface, mgmt, len);
1222 		return;
1223 	}
1224 
1225 	s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
1226 	if (s) {
1227 		fst_session_handle_action(s, iface, mgmt, len);
1228 	} else {
1229 		fst_printf_iface(iface, MSG_WARNING,
1230 				 "FST Action '%s' dropped: no session in progress found",
1231 				 fst_action_names[mgmt->u.action.u.fst_action.action]);
1232 	}
1233 }
1234 
1235 
1236 int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
1237 			       Boolean is_old)
1238 {
1239 	struct fst_group *g = fst_session_get_group(s);
1240 	struct fst_iface *i;
1241 
1242 	i = fst_group_get_iface_by_name(g, ifname);
1243 	if (!i) {
1244 		fst_printf_session(s, MSG_WARNING,
1245 				   "Cannot set iface %s: no such iface within group '%s'",
1246 				   ifname, fst_group_get_id(g));
1247 		return -1;
1248 	}
1249 
1250 	fst_session_set_iface(s, i, is_old);
1251 
1252 	return 0;
1253 }
1254 
1255 
1256 int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
1257 				  Boolean is_old)
1258 {
1259 	u8 peer_addr[ETH_ALEN];
1260 	int res = fst_read_peer_addr(mac, peer_addr);
1261 
1262 	if (res)
1263 		return res;
1264 
1265 	fst_session_set_peer_addr(s, peer_addr, is_old);
1266 
1267 	return 0;
1268 }
1269 
1270 
1271 int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
1272 {
1273 	char *endp;
1274 	long int llt = strtol(llt_str, &endp, 0);
1275 
1276 	if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
1277 		fst_printf_session(s, MSG_WARNING,
1278 				   "Cannot set llt %s: Invalid llt value (1..%u expected)",
1279 				   llt_str, FST_MAX_LLT_MS);
1280 		return -1;
1281 	}
1282 	fst_session_set_llt(s, (u32) llt);
1283 
1284 	return 0;
1285 }
1286 
1287 
1288 void fst_session_global_on_iface_detached(struct fst_iface *iface)
1289 {
1290 	struct fst_session *s;
1291 
1292 	foreach_fst_session(s) {
1293 		if (fst_session_is_in_progress(s) &&
1294 		    (s->data.new_iface == iface ||
1295 		     s->data.old_iface == iface))
1296 			fst_session_reset_ex(s, REASON_DETACH_IFACE);
1297 	}
1298 }
1299 
1300 
1301 struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
1302 {
1303 	struct fst_session *s;
1304 
1305 	foreach_fst_session(s) {
1306 		if (s->group == g)
1307 			return s;
1308 	}
1309 
1310 	return NULL;
1311 }
1312 
1313 
1314 #ifdef CONFIG_FST_TEST
1315 
1316 static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
1317 {
1318 	const u8 *old_addr, *new_addr;
1319 	struct fst_get_peer_ctx *ctx;
1320 
1321 	os_memset(s, 0, sizeof(*s));
1322 	foreach_fst_group(*g) {
1323 		s->data.new_iface = fst_group_first_iface(*g);
1324 		if (s->data.new_iface)
1325 			break;
1326 	}
1327 	if (!s->data.new_iface)
1328 		return -EINVAL;
1329 
1330 	s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
1331 					  struct fst_iface, group_lentry);
1332 	if (!s->data.old_iface)
1333 		return -EINVAL;
1334 
1335 	old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
1336 	if (!old_addr)
1337 		return -EINVAL;
1338 
1339 	new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
1340 	if (!new_addr)
1341 		return -EINVAL;
1342 
1343 	os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
1344 	os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
1345 
1346 	return 0;
1347 }
1348 
1349 
1350 #define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
1351 
1352 int fst_test_req_send_fst_request(const char *params)
1353 {
1354 	int fsts_id;
1355 	Boolean is_valid;
1356 	char *endp;
1357 	struct fst_setup_req req;
1358 	struct fst_session s;
1359 	struct fst_group *g;
1360 	enum hostapd_hw_mode hw_mode;
1361 	u8 channel;
1362 	char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1363 
1364 	if (params[0] != ' ')
1365 		return -EINVAL;
1366 	params++;
1367 	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1368 	if (!is_valid)
1369 		return -EINVAL;
1370 
1371 	if (get_group_fill_session(&g, &s))
1372 		return -EINVAL;
1373 
1374 	req.action = FST_ACTION_SETUP_REQUEST;
1375 	req.dialog_token = g->dialog_token;
1376 	req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
1377 	/* 8.4.2.147 Session Transition element */
1378 	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1379 	req.stie.length = sizeof(req.stie) - 2;
1380 	req.stie.fsts_id = host_to_le32(fsts_id);
1381 	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1382 
1383 	fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
1384 	req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1385 	req.stie.new_band_op = 1;
1386 	req.stie.new_band_setup = 0;
1387 
1388 	fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
1389 	req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1390 	req.stie.old_band_op = 1;
1391 	req.stie.old_band_setup = 0;
1392 
1393 	if (!fst_read_next_text_param(endp, additional_param,
1394 				       sizeof(additional_param), &endp)) {
1395 		if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
1396 			req.stie.new_band_id = req.stie.old_band_id;
1397 	}
1398 
1399 	return fst_session_send_action(&s, TRUE, &req, sizeof(req),
1400 				       s.data.old_iface->mb_ie);
1401 }
1402 
1403 
1404 int fst_test_req_send_fst_response(const char *params)
1405 {
1406 	int fsts_id;
1407 	Boolean is_valid;
1408 	char *endp;
1409 	struct fst_setup_res res;
1410 	struct fst_session s;
1411 	struct fst_group *g;
1412 	enum hostapd_hw_mode hw_mode;
1413 	u8 status_code;
1414 	u8 channel;
1415 	char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1416 	struct fst_session *_s;
1417 
1418 	if (params[0] != ' ')
1419 		return -EINVAL;
1420 	params++;
1421 	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1422 	if (!is_valid)
1423 		return -EINVAL;
1424 
1425 	if (get_group_fill_session(&g, &s))
1426 		return -EINVAL;
1427 
1428 	status_code = WLAN_STATUS_SUCCESS;
1429 	if (!fst_read_next_text_param(endp, response, sizeof(response),
1430 				      &endp)) {
1431 		if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
1432 			status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
1433 	}
1434 
1435 	os_memset(&res, 0, sizeof(res));
1436 
1437 	res.action = FST_ACTION_SETUP_RESPONSE;
1438 	/*
1439 	 * If some session has just received an FST Setup Request, then
1440 	 * use the correct dialog token copied from this request.
1441 	 */
1442 	_s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
1443 					  g);
1444 	res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
1445 		_s->data.pending_setup_req_dlgt : g->dialog_token;
1446 	res.status_code  = status_code;
1447 
1448 	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1449 	res.stie.length = sizeof(res.stie) - 2;
1450 
1451 	if (res.status_code == WLAN_STATUS_SUCCESS) {
1452 		res.stie.fsts_id = host_to_le32(fsts_id);
1453 		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1454 
1455 		fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
1456 					    &channel);
1457 		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1458 		res.stie.new_band_op = 1;
1459 		res.stie.new_band_setup = 0;
1460 
1461 		fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
1462 					   &channel);
1463 		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1464 		res.stie.old_band_op = 1;
1465 		res.stie.old_band_setup = 0;
1466 	}
1467 
1468 	if (!fst_read_next_text_param(endp, response, sizeof(response),
1469 				      &endp)) {
1470 		if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
1471 			res.stie.new_band_id = res.stie.old_band_id;
1472 	}
1473 
1474 	return fst_session_send_action(&s, TRUE, &res, sizeof(res),
1475 				       s.data.old_iface->mb_ie);
1476 }
1477 
1478 
1479 int fst_test_req_send_ack_request(const char *params)
1480 {
1481 	int fsts_id;
1482 	Boolean is_valid;
1483 	char *endp;
1484 	struct fst_ack_req req;
1485 	struct fst_session s;
1486 	struct fst_group *g;
1487 
1488 	if (params[0] != ' ')
1489 		return -EINVAL;
1490 	params++;
1491 	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1492 	if (!is_valid)
1493 		return -EINVAL;
1494 
1495 	if (get_group_fill_session(&g, &s))
1496 		return -EINVAL;
1497 
1498 	os_memset(&req, 0, sizeof(req));
1499 	req.action = FST_ACTION_ACK_REQUEST;
1500 	req.dialog_token = g->dialog_token;
1501 	req.fsts_id = host_to_le32(fsts_id);
1502 
1503 	return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
1504 }
1505 
1506 
1507 int fst_test_req_send_ack_response(const char *params)
1508 {
1509 	int fsts_id;
1510 	Boolean is_valid;
1511 	char *endp;
1512 	struct fst_ack_res res;
1513 	struct fst_session s;
1514 	struct fst_group *g;
1515 
1516 	if (params[0] != ' ')
1517 		return -EINVAL;
1518 	params++;
1519 	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1520 	if (!is_valid)
1521 		return -EINVAL;
1522 
1523 	if (get_group_fill_session(&g, &s))
1524 		return -EINVAL;
1525 
1526 	os_memset(&res, 0, sizeof(res));
1527 	res.action = FST_ACTION_ACK_RESPONSE;
1528 	res.dialog_token = g->dialog_token;
1529 	res.fsts_id = host_to_le32(fsts_id);
1530 
1531 	return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
1532 }
1533 
1534 
1535 int fst_test_req_send_tear_down(const char *params)
1536 {
1537 	int fsts_id;
1538 	Boolean is_valid;
1539 	char *endp;
1540 	struct fst_tear_down td;
1541 	struct fst_session s;
1542 	struct fst_group *g;
1543 
1544 	if (params[0] != ' ')
1545 		return -EINVAL;
1546 	params++;
1547 	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1548 	if (!is_valid)
1549 		return -EINVAL;
1550 
1551 	if (get_group_fill_session(&g, &s))
1552 		return -EINVAL;
1553 
1554 	os_memset(&td, 0, sizeof(td));
1555 	td.action = FST_ACTION_TEAR_DOWN;
1556 	td.fsts_id = host_to_le32(fsts_id);
1557 
1558 	return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
1559 }
1560 
1561 
1562 u32 fst_test_req_get_fsts_id(const char *params)
1563 {
1564 	int sid;
1565 	Boolean is_valid;
1566 	char *endp;
1567 	struct fst_session *s;
1568 
1569 	if (params[0] != ' ')
1570 		return FST_FSTS_ID_NOT_FOUND;
1571 	params++;
1572 	sid = fst_read_next_int_param(params, &is_valid, &endp);
1573 	if (!is_valid)
1574 		return FST_FSTS_ID_NOT_FOUND;
1575 
1576 	s = fst_session_get_by_id(sid);
1577 	if (!s)
1578 		return FST_FSTS_ID_NOT_FOUND;
1579 
1580 	return s->data.fsts_id;
1581 }
1582 
1583 
1584 int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
1585 {
1586 	char *endp;
1587 	char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
1588 	struct fst_group *g;
1589 	struct fst_iface *iface;
1590 
1591 	if (request[0] != ' ')
1592 		return -EINVAL;
1593 	request++;
1594 	if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
1595 	    !*ifname)
1596 		goto problem;
1597 	g = dl_list_first(&fst_global_groups_list, struct fst_group,
1598 			  global_groups_lentry);
1599 	if (!g)
1600 		goto problem;
1601 	iface = fst_group_get_iface_by_name(g, ifname);
1602 	if (!iface || !iface->mb_ie)
1603 		goto problem;
1604 	return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
1605 				wpabuf_len(iface->mb_ie));
1606 
1607 problem:
1608 	return os_snprintf(buf, buflen, "FAIL\n");
1609 }
1610 
1611 #endif /* CONFIG_FST_TEST */
1612