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