Line data Source code
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 435 : static void fst_session_global_inc_id(void)
90 : {
91 435 : global_session_id++;
92 435 : if (global_session_id == FST_INVALID_SESSION_ID)
93 0 : global_session_id++;
94 435 : }
95 :
96 :
97 74 : int fst_session_global_init(void)
98 : {
99 74 : dl_list_init(&global_sessions_list);
100 74 : return 0;
101 : }
102 :
103 :
104 74 : void fst_session_global_deinit(void)
105 : {
106 : WPA_ASSERT(dl_list_empty(&global_sessions_list));
107 74 : }
108 :
109 :
110 1885 : 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 1885 : foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
115 1885 : }
116 :
117 :
118 1720 : 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 1720 : if (s->state != state) {
123 2558 : union fst_event_extra evext = {
124 : .session_state = {
125 1279 : .old_state = s->state,
126 : .new_state = state,
127 : },
128 : };
129 :
130 1279 : if (extra)
131 629 : evext.session_state.extra = *extra;
132 1279 : fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
133 : &evext);
134 1279 : fst_printf_session(s, MSG_INFO, "State: %s => %s",
135 : fst_session_state_name(s->state),
136 : fst_session_state_name(state));
137 1279 : s->state = state;
138 : }
139 1720 : }
140 :
141 :
142 435 : static u32 fst_find_free_session_id(void)
143 : {
144 435 : u32 i, id = FST_INVALID_SESSION_ID;
145 : struct fst_session *s;
146 :
147 435 : for (i = 0; i < (u32) -1; i++) {
148 435 : Boolean in_use = FALSE;
149 :
150 455 : foreach_fst_session(s) {
151 20 : if (s->id == global_session_id) {
152 0 : fst_session_global_inc_id();
153 0 : in_use = TRUE;
154 0 : break;
155 : }
156 : }
157 435 : if (!in_use) {
158 435 : id = global_session_id;
159 435 : fst_session_global_inc_id();
160 435 : break;
161 : }
162 : }
163 :
164 435 : return id;
165 : }
166 :
167 :
168 22 : static void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
169 : {
170 22 : struct fst_session *s = user_ctx;
171 22 : union fst_session_state_switch_extra extra = {
172 : .to_initial = {
173 : .reason = REASON_STT,
174 : },
175 : };
176 :
177 22 : fst_printf_session(s, MSG_WARNING, "Session State Timeout");
178 22 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
179 22 : }
180 :
181 :
182 635 : static void fst_session_stt_arm(struct fst_session *s)
183 : {
184 : /* Action frames sometimes get delayed. Use relaxed timeout (2*) */
185 635 : eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
186 : fst_session_timeout_handler, NULL, s);
187 635 : s->stt_armed = TRUE;
188 635 : }
189 :
190 :
191 1301 : static void fst_session_stt_disarm(struct fst_session *s)
192 : {
193 1301 : if (s->stt_armed) {
194 635 : eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
195 635 : s->stt_armed = FALSE;
196 : }
197 1301 : }
198 :
199 :
200 629 : static Boolean fst_session_is_in_transition(struct fst_session *s)
201 : {
202 : /* See spec, 10.32.2.2 Transitioning between states */
203 629 : return s->stt_armed;
204 : }
205 :
206 :
207 1910 : static int fst_session_is_in_progress(struct fst_session *s)
208 : {
209 1910 : return s->state != FST_SESSION_STATE_INITIAL;
210 : }
211 :
212 :
213 604 : static int fst_session_is_ready_pending(struct fst_session *s)
214 : {
215 1208 : return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
216 604 : fst_session_is_in_transition(s);
217 : }
218 :
219 :
220 20 : static int fst_session_is_ready(struct fst_session *s)
221 : {
222 38 : return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
223 18 : !fst_session_is_in_transition(s);
224 : }
225 :
226 :
227 18 : static int fst_session_is_switch_requested(struct fst_session *s)
228 : {
229 25 : return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
230 7 : fst_session_is_in_transition(s);
231 : }
232 :
233 :
234 : static struct fst_session *
235 1566 : fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
236 : {
237 : struct fst_session *s;
238 :
239 2212 : foreach_fst_session(s) {
240 2508 : if (s->group == g &&
241 1254 : (os_memcmp(s->data.old_peer_addr, peer_addr,
242 342 : ETH_ALEN) == 0 ||
243 342 : os_memcmp(s->data.new_peer_addr, peer_addr,
244 1254 : ETH_ALEN) == 0) &&
245 1254 : fst_session_is_in_progress(s))
246 608 : return s;
247 : }
248 :
249 958 : return NULL;
250 : }
251 :
252 :
253 436 : static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
254 : {
255 436 : union fst_session_state_switch_extra evext = {
256 : .to_initial = {
257 : .reason = reason,
258 : },
259 : };
260 :
261 815 : if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
262 379 : s->state == FST_SESSION_STATE_TRANSITION_DONE)
263 58 : fst_session_tear_down_setup(s);
264 436 : fst_session_stt_disarm(s);
265 436 : os_memset(&s->data, 0, sizeof(s->data));
266 436 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
267 436 : }
268 :
269 :
270 968 : 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 968 : struct fst_iface *iface =
279 968 : old_iface ? s->data.old_iface : s->data.new_iface;
280 :
281 : WPA_ASSERT(payload != NULL);
282 : WPA_ASSERT(size != 0);
283 :
284 968 : action = *(const u8 *) payload;
285 :
286 : WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
287 :
288 968 : if (!iface) {
289 0 : 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 0 : return -1;
294 : }
295 :
296 968 : len = sizeof(u8) /* category */ + size;
297 968 : if (extra_buf)
298 612 : len += wpabuf_size(extra_buf);
299 :
300 968 : buf = wpabuf_alloc(len);
301 968 : if (!buf) {
302 2 : 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 2 : return -1;
306 : }
307 :
308 966 : wpabuf_put_u8(buf, WLAN_ACTION_FST);
309 966 : wpabuf_put_data(buf, payload, size);
310 966 : if (extra_buf)
311 611 : wpabuf_put_buf(buf, extra_buf);
312 :
313 966 : res = fst_iface_send_action(iface,
314 : old_iface ? s->data.old_peer_addr :
315 : s->data.new_peer_addr, buf);
316 966 : if (res < 0)
317 52 : fst_printf_siface(s, iface, MSG_ERROR,
318 : "failed to send FST Action '%s'",
319 : fst_action_names[action]);
320 : else
321 914 : fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
322 : fst_action_names[action]);
323 966 : wpabuf_free(buf);
324 :
325 966 : return res;
326 : }
327 :
328 :
329 326 : static int fst_session_send_tear_down(struct fst_session *s)
330 : {
331 : struct fst_tear_down td;
332 : int res;
333 :
334 326 : if (!fst_session_is_in_progress(s)) {
335 5 : fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
336 5 : return -1;
337 : }
338 :
339 : WPA_ASSERT(s->data.old_iface != NULL);
340 : WPA_ASSERT(s->data.new_iface != NULL);
341 :
342 321 : os_memset(&td, 0, sizeof(td));
343 :
344 321 : td.action = FST_ACTION_TEAR_DOWN;
345 321 : td.fsts_id = host_to_le32(s->data.fsts_id);
346 :
347 321 : res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
348 321 : if (!res)
349 268 : fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
350 : else
351 53 : fst_printf_sframe(s, TRUE, MSG_ERROR,
352 : "failed to send FST TearDown");
353 :
354 321 : return res;
355 : }
356 :
357 :
358 328 : 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 328 : 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 328 : if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) {
370 1 : fst_printf_iface(iface, MSG_WARNING,
371 : "FST Request dropped: too short (%zu < %zu)",
372 : frame_len,
373 : IEEE80211_HDRLEN + 1 + sizeof(*req));
374 1 : return;
375 : }
376 327 : plen = frame_len - IEEE80211_HDRLEN - 1;
377 327 : req = (const struct fst_setup_req *)
378 : (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
379 653 : if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
380 326 : req->stie.length < 11) {
381 2 : fst_printf_iface(iface, MSG_WARNING,
382 : "FST Request dropped: invalid STIE");
383 2 : return;
384 : }
385 :
386 325 : if (req->stie.new_band_id == req->stie.old_band_id) {
387 3 : fst_printf_iface(iface, MSG_WARNING,
388 : "FST Request dropped: new and old band IDs are the same");
389 3 : return;
390 : }
391 :
392 322 : g = fst_iface_get_group(iface);
393 :
394 322 : if (plen > sizeof(*req)) {
395 318 : fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
396 : plen - sizeof(*req));
397 318 : fst_printf_iface(iface, MSG_INFO,
398 : "FST Request: MB IEs updated for " MACSTR,
399 : MAC2STR(mgmt->sa));
400 : }
401 :
402 322 : new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa,
403 322 : req->stie.new_band_id,
404 : new_iface_peer_addr);
405 322 : if (!new_iface) {
406 1 : fst_printf_iface(iface, MSG_WARNING,
407 : "FST Request dropped: new iface not found");
408 1 : return;
409 : }
410 321 : 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 321 : s = fst_find_session_in_progress(mgmt->sa, g);
416 321 : if (s) {
417 9 : 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 17 : if (fst_session_is_ready_pending(s) &&
433 : /* waiting for Setup Response */
434 8 : os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
435 2 : fst_printf_session(s, MSG_WARNING,
436 : "FST Request dropped due to MAC comparison (our MAC is "
437 : MACSTR ")",
438 : MAC2STR(mgmt->da));
439 2 : 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 7 : 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 7 : if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
467 3 : fst_session_send_tear_down(s);
468 : else
469 4 : fst_printf_session(s, MSG_WARNING,
470 : "Skipping TearDown as the FST request has the same FSTS ID as initiated");
471 7 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
472 7 : fst_session_stt_disarm(s);
473 : }
474 :
475 319 : s = fst_session_create(g);
476 319 : if (!s) {
477 1 : 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 1 : return;
482 : }
483 :
484 318 : fst_session_set_iface(s, iface, TRUE);
485 318 : fst_session_set_peer_addr(s, mgmt->sa, TRUE);
486 318 : fst_session_set_iface(s, new_iface, FALSE);
487 318 : fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
488 318 : fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
489 318 : s->data.pending_setup_req_dlgt = req->dialog_token;
490 318 : s->data.fsts_id = le_to_host32(req->stie.fsts_id);
491 :
492 318 : fst_session_stt_arm(s);
493 :
494 318 : fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
495 :
496 318 : fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
497 : }
498 :
499 :
500 300 : 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 300 : size_t plen = frame_len - IEEE80211_HDRLEN - 1;
507 : enum hostapd_hw_mode hw_mode;
508 : u8 channel;
509 300 : union fst_session_state_switch_extra evext = {
510 : .to_initial = {
511 : .reject_code = 0,
512 : },
513 : };
514 :
515 300 : if (iface != s->data.old_iface) {
516 1 : fst_printf_session(s, MSG_WARNING,
517 : "FST Response dropped: %s is not the old iface",
518 : fst_iface_get_name(iface));
519 1 : return;
520 : }
521 :
522 299 : if (!fst_session_is_ready_pending(s)) {
523 1 : fst_printf_session(s, MSG_WARNING,
524 : "FST Response dropped due to wrong state: %s",
525 : fst_session_state_name(s->state));
526 1 : return;
527 : }
528 :
529 298 : if (plen < sizeof(*res)) {
530 1 : fst_printf_session(s, MSG_WARNING,
531 : "Too short FST Response dropped");
532 1 : return;
533 : }
534 297 : res = (const struct fst_setup_res *)
535 : (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
536 593 : if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
537 296 : res->stie.length < 11) {
538 2 : fst_printf_iface(iface, MSG_WARNING,
539 : "FST Response dropped: invalid STIE");
540 2 : return;
541 : }
542 :
543 295 : if (res->dialog_token != s->data.pending_setup_req_dlgt) {
544 1 : 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 1 : return;
549 : }
550 :
551 585 : if (res->status_code == WLAN_STATUS_SUCCESS &&
552 291 : le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
553 1 : 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 1 : return;
557 : }
558 :
559 293 : fst_session_stt_disarm(s);
560 :
561 293 : 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 3 : evext.to_initial.reason = REASON_REJECT;
571 3 : evext.to_initial.reject_code = res->status_code;
572 3 : evext.to_initial.initiator = FST_INITIATOR_REMOTE;
573 3 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
574 3 : fst_printf_session(s, MSG_WARNING,
575 : "FST Setup rejected by remote side with status %u",
576 : res->status_code);
577 3 : return;
578 : }
579 :
580 290 : fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
581 :
582 290 : if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
583 2 : evext.to_initial.reason = REASON_ERROR_PARAMS;
584 2 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
585 2 : fst_printf_session(s, MSG_WARNING,
586 : "invalid FST Setup parameters");
587 2 : fst_session_tear_down_setup(s);
588 2 : return;
589 : }
590 :
591 288 : 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 288 : fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
598 :
599 288 : if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
600 3 : fst_session_initiate_switch(s);
601 : }
602 :
603 :
604 269 : 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 269 : size_t plen = frame_len - IEEE80211_HDRLEN - 1;
611 269 : union fst_session_state_switch_extra evext = {
612 : .to_initial = {
613 : .reason = REASON_TEARDOWN,
614 : .initiator = FST_INITIATOR_REMOTE,
615 : },
616 : };
617 :
618 269 : if (plen < sizeof(*td)) {
619 1 : fst_printf_session(s, MSG_WARNING,
620 : "Too short FST Tear Down dropped");
621 1 : return;
622 : }
623 268 : td = (const struct fst_tear_down *)
624 : (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
625 :
626 268 : if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
627 3 : fst_printf_siface(s, iface, MSG_WARNING,
628 : "tear down for wrong FST Setup ID (%u)",
629 : le_to_host32(td->fsts_id));
630 3 : return;
631 : }
632 :
633 265 : fst_session_stt_disarm(s);
634 :
635 265 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
636 : }
637 :
638 :
639 12 : 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 12 : size_t plen = frame_len - IEEE80211_HDRLEN - 1;
646 : struct fst_ack_res res;
647 12 : union fst_session_state_switch_extra evext = {
648 : .to_initial = {
649 : .reason = REASON_SWITCH,
650 : .initiator = FST_INITIATOR_REMOTE,
651 : },
652 : };
653 :
654 12 : if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
655 4 : 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 4 : return;
659 : }
660 :
661 : WPA_ASSERT(s->data.new_iface != NULL);
662 :
663 8 : if (iface != s->data.new_iface) {
664 1 : fst_printf_siface(s, iface, MSG_ERROR,
665 : "Ack received on wrong interface");
666 1 : return;
667 : }
668 :
669 7 : if (plen < sizeof(*req)) {
670 0 : fst_printf_session(s, MSG_WARNING,
671 : "Too short FST Ack Request dropped");
672 0 : return;
673 : }
674 7 : req = (const struct fst_ack_req *)
675 : (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
676 :
677 7 : if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
678 2 : fst_printf_siface(s, iface, MSG_WARNING,
679 : "Ack for wrong FST Setup ID (%u)",
680 : le_to_host32(req->fsts_id));
681 2 : return;
682 : }
683 :
684 5 : os_memset(&res, 0, sizeof(res));
685 :
686 5 : res.action = FST_ACTION_ACK_RESPONSE;
687 5 : res.dialog_token = req->dialog_token;
688 5 : res.fsts_id = req->fsts_id;
689 :
690 5 : if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
691 5 : fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
692 5 : fst_session_stt_disarm(s);
693 5 : fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
694 : NULL);
695 5 : fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
696 : NULL);
697 5 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
698 : }
699 : }
700 :
701 :
702 : static void
703 14 : 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 14 : size_t plen = frame_len - IEEE80211_HDRLEN - 1;
710 14 : union fst_session_state_switch_extra evext = {
711 : .to_initial = {
712 : .reason = REASON_SWITCH,
713 : .initiator = FST_INITIATOR_LOCAL,
714 : },
715 : };
716 :
717 14 : if (!fst_session_is_switch_requested(s)) {
718 7 : fst_printf_siface(s, iface, MSG_ERROR,
719 : "Ack Response in inappropriate session state (%s)",
720 : fst_session_state_name(s->state));
721 7 : return;
722 : }
723 :
724 : WPA_ASSERT(s->data.new_iface != NULL);
725 :
726 7 : if (iface != s->data.new_iface) {
727 0 : fst_printf_siface(s, iface, MSG_ERROR,
728 : "Ack response received on wrong interface");
729 0 : return;
730 : }
731 :
732 7 : if (plen < sizeof(*res)) {
733 1 : fst_printf_session(s, MSG_WARNING,
734 : "Too short FST Ack Response dropped");
735 1 : return;
736 : }
737 6 : res = (const struct fst_ack_res *)
738 : (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
739 :
740 6 : if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
741 1 : fst_printf_siface(s, iface, MSG_ERROR,
742 : "Ack response for wrong FST Setup ID (%u)",
743 : le_to_host32(res->fsts_id));
744 1 : return;
745 : }
746 :
747 5 : fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
748 5 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
749 :
750 5 : fst_session_stt_disarm(s);
751 : }
752 :
753 :
754 435 : 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 435 : id = fst_find_free_session_id();
762 435 : if (id == FST_INVALID_SESSION_ID) {
763 0 : fst_printf(MSG_ERROR, "Cannot assign new session ID");
764 0 : return NULL;
765 : }
766 :
767 435 : s = os_zalloc(sizeof(*s));
768 435 : if (!s) {
769 2 : fst_printf(MSG_ERROR, "Cannot allocate new session object");
770 2 : return NULL;
771 : }
772 :
773 433 : s->id = id;
774 433 : s->group = g;
775 433 : s->state = FST_SESSION_STATE_INITIAL;
776 :
777 433 : s->data.llt_ms = FST_LLT_MS_DEFAULT;
778 :
779 433 : fst_printf(MSG_INFO, "Session %u created", s->id);
780 :
781 433 : dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
782 :
783 433 : foreach_fst_ctrl_call(on_session_added, s);
784 :
785 433 : return s;
786 : }
787 :
788 :
789 849 : void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
790 : Boolean is_old)
791 : {
792 849 : if (is_old)
793 427 : s->data.old_iface = iface;
794 : else
795 422 : s->data.new_iface = iface;
796 :
797 849 : }
798 :
799 :
800 408 : void fst_session_set_llt(struct fst_session *s, u32 llt)
801 : {
802 408 : s->data.llt_ms = llt;
803 408 : }
804 :
805 :
806 844 : void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
807 : Boolean is_old)
808 : {
809 844 : u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
810 :
811 844 : os_memcpy(a, addr, ETH_ALEN);
812 844 : }
813 :
814 :
815 325 : 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 325 : if (fst_session_is_in_progress(s)) {
824 1 : fst_printf_session(s, MSG_ERROR, "Session in progress");
825 1 : return -EINVAL;
826 : }
827 :
828 324 : if (is_zero_ether_addr(s->data.old_peer_addr)) {
829 1 : fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
830 1 : return -EINVAL;
831 : }
832 :
833 323 : if (is_zero_ether_addr(s->data.new_peer_addr)) {
834 1 : fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
835 1 : return -EINVAL;
836 : }
837 :
838 322 : if (!s->data.old_iface) {
839 1 : fst_printf_session(s, MSG_ERROR, "No old interface defined");
840 1 : return -EINVAL;
841 : }
842 :
843 321 : if (!s->data.new_iface) {
844 3 : fst_printf_session(s, MSG_ERROR, "No new interface defined");
845 3 : return -EINVAL;
846 : }
847 :
848 318 : if (s->data.new_iface == s->data.old_iface) {
849 1 : fst_printf_session(s, MSG_ERROR,
850 : "Same interface set as old and new");
851 1 : return -EINVAL;
852 : }
853 :
854 317 : if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
855 : FALSE)) {
856 3 : fst_printf_session(s, MSG_ERROR,
857 : "The preset old peer address is not connected");
858 3 : return -EINVAL;
859 : }
860 :
861 314 : if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
862 : FALSE)) {
863 1 : fst_printf_session(s, MSG_ERROR,
864 : "The preset new peer address is not connected");
865 1 : return -EINVAL;
866 : }
867 :
868 313 : _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
869 313 : if (_s) {
870 1 : fst_printf_session(s, MSG_ERROR,
871 : "There is another session in progress (old): %u",
872 : _s->id);
873 1 : return -EINVAL;
874 : }
875 :
876 312 : _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
877 312 : if (_s) {
878 0 : fst_printf_session(s, MSG_ERROR,
879 : "There is another session in progress (new): %u",
880 : _s->id);
881 0 : return -EINVAL;
882 : }
883 :
884 312 : dialog_token = fst_group_assign_dialog_token(s->group);
885 312 : fsts_id = fst_group_assign_fsts_id(s->group);
886 :
887 312 : os_memset(&req, 0, sizeof(req));
888 :
889 312 : 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 312 : req.action = FST_ACTION_SETUP_REQUEST;
894 312 : req.dialog_token = dialog_token;
895 312 : req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
896 : /* 8.4.2.147 Session Transition element */
897 312 : req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
898 312 : req.stie.length = sizeof(req.stie) - 2;
899 312 : req.stie.fsts_id = host_to_le32(fsts_id);
900 312 : req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
901 :
902 312 : req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
903 312 : req.stie.new_band_op = 1;
904 312 : req.stie.new_band_setup = 0;
905 :
906 312 : req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
907 312 : req.stie.old_band_op = 1;
908 312 : req.stie.old_band_setup = 0;
909 :
910 312 : res = fst_session_send_action(s, TRUE, &req, sizeof(req),
911 : fst_iface_get_mbie(s->data.old_iface));
912 312 : if (!res) {
913 311 : s->data.fsts_id = fsts_id;
914 311 : s->data.pending_setup_req_dlgt = dialog_token;
915 311 : fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
916 311 : fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
917 : NULL);
918 :
919 311 : fst_session_stt_arm(s);
920 : }
921 :
922 312 : return res;
923 : }
924 :
925 :
926 294 : 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 294 : if (!fst_session_is_ready_pending(s)) {
933 1 : fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
934 : fst_session_state_name(s->state));
935 1 : return -EINVAL;
936 : }
937 :
938 293 : if (is_zero_ether_addr(s->data.old_peer_addr)) {
939 0 : fst_printf_session(s, MSG_ERROR, "No peer MAC address");
940 0 : return -EINVAL;
941 : }
942 :
943 293 : if (!s->data.old_iface) {
944 0 : fst_printf_session(s, MSG_ERROR, "No old interface defined");
945 0 : return -EINVAL;
946 : }
947 :
948 293 : if (!s->data.new_iface) {
949 0 : fst_printf_session(s, MSG_ERROR, "No new interface defined");
950 0 : return -EINVAL;
951 : }
952 :
953 293 : if (s->data.new_iface == s->data.old_iface) {
954 1 : fst_printf_session(s, MSG_ERROR,
955 : "Same interface set as old and new");
956 1 : return -EINVAL;
957 : }
958 :
959 292 : if (!fst_iface_is_connected(s->data.old_iface,
960 292 : s->data.old_peer_addr, FALSE)) {
961 2 : fst_printf_session(s, MSG_ERROR,
962 : "The preset peer address is not in the peer list");
963 2 : return -EINVAL;
964 : }
965 :
966 290 : fst_session_stt_disarm(s);
967 :
968 290 : os_memset(&res, 0, sizeof(res));
969 :
970 290 : res.action = FST_ACTION_SETUP_RESPONSE;
971 290 : res.dialog_token = s->data.pending_setup_req_dlgt;
972 290 : res.status_code = status_code;
973 :
974 290 : res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
975 290 : res.stie.length = sizeof(res.stie) - 2;
976 :
977 290 : if (status_code == WLAN_STATUS_SUCCESS) {
978 288 : res.stie.fsts_id = host_to_le32(s->data.fsts_id);
979 288 : res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
980 :
981 288 : fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
982 : &channel);
983 288 : res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
984 288 : res.stie.new_band_op = 1;
985 288 : res.stie.new_band_setup = 0;
986 :
987 288 : fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
988 : &channel);
989 288 : res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
990 288 : res.stie.old_band_op = 1;
991 288 : res.stie.old_band_setup = 0;
992 :
993 288 : 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 2 : 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 290 : if (fst_session_send_action(s, TRUE, &res, sizeof(res),
1006 : fst_iface_get_mbie(s->data.old_iface))) {
1007 0 : fst_printf_sframe(s, TRUE, MSG_ERROR,
1008 : "cannot send FST Setup Response with code %d",
1009 : status_code);
1010 0 : return -EINVAL;
1011 : }
1012 :
1013 290 : fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
1014 :
1015 290 : if (status_code != WLAN_STATUS_SUCCESS) {
1016 2 : 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 2 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1024 : }
1025 :
1026 290 : return 0;
1027 : }
1028 :
1029 :
1030 8 : 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 8 : if (!fst_session_is_ready(s)) {
1037 2 : fst_printf_session(s, MSG_ERROR,
1038 : "cannot initiate switch due to wrong setup state (%d)",
1039 : s->state);
1040 2 : return -1;
1041 : }
1042 :
1043 6 : 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 6 : 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 6 : os_memset(&req, 0, sizeof(req));
1053 :
1054 6 : req.action = FST_ACTION_ACK_REQUEST;
1055 6 : req.dialog_token = dialog_token;
1056 6 : req.fsts_id = host_to_le32(s->data.fsts_id);
1057 :
1058 6 : res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
1059 6 : if (!res) {
1060 6 : fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
1061 6 : fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
1062 : NULL);
1063 6 : fst_session_stt_arm(s);
1064 : } else {
1065 0 : fst_printf_sframe(s, FALSE, MSG_ERROR,
1066 : "Cannot send FST Ack Request");
1067 : }
1068 :
1069 6 : return res;
1070 : }
1071 :
1072 :
1073 596 : 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 596 : switch (mgmt->u.action.u.fst_action.action) {
1079 : case FST_ACTION_SETUP_REQUEST:
1080 : WPA_ASSERT(0);
1081 0 : break;
1082 : case FST_ACTION_SETUP_RESPONSE:
1083 300 : fst_session_handle_setup_response(s, iface, mgmt, frame_len);
1084 300 : break;
1085 : case FST_ACTION_TEAR_DOWN:
1086 269 : fst_session_handle_tear_down(s, iface, mgmt, frame_len);
1087 269 : break;
1088 : case FST_ACTION_ACK_REQUEST:
1089 12 : fst_session_handle_ack_request(s, iface, mgmt, frame_len);
1090 12 : break;
1091 : case FST_ACTION_ACK_RESPONSE:
1092 14 : fst_session_handle_ack_response(s, iface, mgmt, frame_len);
1093 14 : break;
1094 : case FST_ACTION_ON_CHANNEL_TUNNEL:
1095 : default:
1096 1 : fst_printf_sframe(s, FALSE, MSG_ERROR,
1097 : "Unsupported FST Action frame");
1098 1 : break;
1099 : }
1100 596 : }
1101 :
1102 :
1103 323 : int fst_session_tear_down_setup(struct fst_session *s)
1104 : {
1105 : int res;
1106 323 : union fst_session_state_switch_extra evext = {
1107 : .to_initial = {
1108 : .reason = REASON_TEARDOWN,
1109 : .initiator = FST_INITIATOR_LOCAL,
1110 : },
1111 : };
1112 :
1113 323 : res = fst_session_send_tear_down(s);
1114 :
1115 323 : fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
1116 :
1117 323 : return res;
1118 : }
1119 :
1120 :
1121 433 : void fst_session_reset(struct fst_session *s)
1122 : {
1123 433 : fst_session_reset_ex(s, REASON_RESET);
1124 433 : }
1125 :
1126 :
1127 433 : void fst_session_delete(struct fst_session *s)
1128 : {
1129 433 : fst_printf(MSG_INFO, "Session %u deleted", s->id);
1130 433 : dl_list_del(&s->global_sessions_lentry);
1131 433 : foreach_fst_ctrl_call(on_session_removed, s);
1132 433 : os_free(s);
1133 433 : }
1134 :
1135 :
1136 650 : struct fst_group * fst_session_get_group(struct fst_session *s)
1137 : {
1138 650 : return s->group;
1139 : }
1140 :
1141 :
1142 4 : struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
1143 : {
1144 4 : return is_old ? s->data.old_iface : s->data.new_iface;
1145 : }
1146 :
1147 :
1148 2168 : u32 fst_session_get_id(struct fst_session *s)
1149 : {
1150 2168 : return s->id;
1151 : }
1152 :
1153 :
1154 10 : const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
1155 : {
1156 10 : return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
1157 : }
1158 :
1159 :
1160 2 : u32 fst_session_get_llt(struct fst_session *s)
1161 : {
1162 2 : return s->data.llt_ms;
1163 : }
1164 :
1165 :
1166 2 : enum fst_session_state fst_session_get_state(struct fst_session *s)
1167 : {
1168 2 : return s->state;
1169 : }
1170 :
1171 :
1172 1878 : struct fst_session * fst_session_get_by_id(u32 id)
1173 : {
1174 : struct fst_session *s;
1175 :
1176 1898 : foreach_fst_session(s) {
1177 1882 : if (id == s->id)
1178 1862 : return s;
1179 : }
1180 :
1181 16 : return NULL;
1182 : }
1183 :
1184 :
1185 499 : void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
1186 : {
1187 : struct fst_session *s;
1188 :
1189 667 : foreach_fst_session(s) {
1190 168 : if (!g || s->group == g)
1191 168 : clb(s->group, s, ctx);
1192 : }
1193 499 : }
1194 :
1195 :
1196 943 : 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 1886 : if (len < IEEE80211_HDRLEN + 2 ||
1203 943 : mgmt->u.action.category != WLAN_ACTION_FST) {
1204 0 : fst_printf_iface(iface, MSG_ERROR,
1205 : "invalid Action frame received");
1206 0 : return;
1207 : }
1208 :
1209 943 : if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
1210 942 : 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 1 : fst_printf_iface(iface, MSG_WARNING,
1215 : "unknown FST Action (%u) received!",
1216 : mgmt->u.action.u.fst_action.action);
1217 1 : return;
1218 : }
1219 :
1220 942 : if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
1221 328 : fst_session_handle_setup_request(iface, mgmt, len);
1222 328 : return;
1223 : }
1224 :
1225 614 : s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
1226 614 : if (s) {
1227 596 : fst_session_handle_action(s, iface, mgmt, len);
1228 : } else {
1229 18 : 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 217 : int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
1237 : Boolean is_old)
1238 : {
1239 217 : struct fst_group *g = fst_session_get_group(s);
1240 : struct fst_iface *i;
1241 :
1242 217 : i = fst_group_get_iface_by_name(g, ifname);
1243 217 : if (!i) {
1244 4 : 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 4 : return -1;
1248 : }
1249 :
1250 213 : fst_session_set_iface(s, i, is_old);
1251 :
1252 213 : return 0;
1253 : }
1254 :
1255 :
1256 214 : 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 214 : int res = fst_read_peer_addr(mac, peer_addr);
1261 :
1262 214 : if (res)
1263 6 : return res;
1264 :
1265 208 : fst_session_set_peer_addr(s, peer_addr, is_old);
1266 :
1267 208 : return 0;
1268 : }
1269 :
1270 :
1271 94 : int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
1272 : {
1273 : char *endp;
1274 94 : long int llt = strtol(llt_str, &endp, 0);
1275 :
1276 94 : if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
1277 4 : 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 4 : return -1;
1281 : }
1282 90 : fst_session_set_llt(s, (u32) llt);
1283 :
1284 90 : return 0;
1285 : }
1286 :
1287 :
1288 544 : void fst_session_global_on_iface_detached(struct fst_iface *iface)
1289 : {
1290 : struct fst_session *s;
1291 :
1292 549 : foreach_fst_session(s) {
1293 8 : if (fst_session_is_in_progress(s) &&
1294 6 : (s->data.new_iface == iface ||
1295 3 : s->data.old_iface == iface))
1296 3 : fst_session_reset_ex(s, REASON_DETACH_IFACE);
1297 : }
1298 544 : }
1299 :
1300 :
1301 559 : struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
1302 : {
1303 : struct fst_session *s;
1304 :
1305 559 : foreach_fst_session(s) {
1306 0 : if (s->group == g)
1307 0 : return s;
1308 : }
1309 :
1310 559 : return NULL;
1311 : }
1312 :
1313 :
1314 : #ifdef CONFIG_FST_TEST
1315 :
1316 34 : 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 34 : os_memset(s, 0, sizeof(*s));
1322 34 : foreach_fst_group(*g) {
1323 34 : s->data.new_iface = fst_group_first_iface(*g);
1324 34 : if (s->data.new_iface)
1325 34 : break;
1326 : }
1327 34 : if (!s->data.new_iface)
1328 0 : return -EINVAL;
1329 :
1330 34 : s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
1331 : struct fst_iface, group_lentry);
1332 34 : if (!s->data.old_iface)
1333 0 : return -EINVAL;
1334 :
1335 34 : old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
1336 34 : if (!old_addr)
1337 0 : return -EINVAL;
1338 :
1339 34 : new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
1340 34 : if (!new_addr)
1341 0 : return -EINVAL;
1342 :
1343 34 : os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
1344 34 : os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
1345 :
1346 34 : return 0;
1347 : }
1348 :
1349 :
1350 : #define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
1351 :
1352 6 : 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 6 : if (params[0] != ' ')
1365 1 : return -EINVAL;
1366 5 : params++;
1367 5 : fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1368 5 : if (!is_valid)
1369 1 : return -EINVAL;
1370 :
1371 4 : if (get_group_fill_session(&g, &s))
1372 0 : return -EINVAL;
1373 :
1374 4 : req.action = FST_ACTION_SETUP_REQUEST;
1375 4 : req.dialog_token = g->dialog_token;
1376 4 : req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
1377 : /* 8.4.2.147 Session Transition element */
1378 4 : req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1379 4 : req.stie.length = sizeof(req.stie) - 2;
1380 4 : req.stie.fsts_id = host_to_le32(fsts_id);
1381 4 : req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1382 :
1383 4 : fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
1384 4 : req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1385 4 : req.stie.new_band_op = 1;
1386 4 : req.stie.new_band_setup = 0;
1387 :
1388 4 : fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
1389 4 : req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1390 4 : req.stie.old_band_op = 1;
1391 4 : req.stie.old_band_setup = 0;
1392 :
1393 4 : if (!fst_read_next_text_param(endp, additional_param,
1394 : sizeof(additional_param), &endp)) {
1395 2 : if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
1396 2 : req.stie.new_band_id = req.stie.old_band_id;
1397 : }
1398 :
1399 4 : return fst_session_send_action(&s, TRUE, &req, sizeof(req),
1400 4 : s.data.old_iface->mb_ie);
1401 : }
1402 :
1403 :
1404 8 : 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 8 : if (params[0] != ' ')
1419 1 : return -EINVAL;
1420 7 : params++;
1421 7 : fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1422 7 : if (!is_valid)
1423 1 : return -EINVAL;
1424 :
1425 6 : if (get_group_fill_session(&g, &s))
1426 0 : return -EINVAL;
1427 :
1428 6 : status_code = WLAN_STATUS_SUCCESS;
1429 6 : if (!fst_read_next_text_param(endp, response, sizeof(response),
1430 : &endp)) {
1431 6 : if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
1432 2 : status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
1433 : }
1434 :
1435 6 : os_memset(&res, 0, sizeof(res));
1436 :
1437 6 : 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 6 : _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
1443 : g);
1444 10 : res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
1445 4 : _s->data.pending_setup_req_dlgt : g->dialog_token;
1446 6 : res.status_code = status_code;
1447 :
1448 6 : res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
1449 6 : res.stie.length = sizeof(res.stie) - 2;
1450 :
1451 6 : if (res.status_code == WLAN_STATUS_SUCCESS) {
1452 4 : res.stie.fsts_id = host_to_le32(fsts_id);
1453 4 : res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
1454 :
1455 4 : fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
1456 : &channel);
1457 4 : res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
1458 4 : res.stie.new_band_op = 1;
1459 4 : res.stie.new_band_setup = 0;
1460 :
1461 4 : fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
1462 : &channel);
1463 4 : res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
1464 4 : res.stie.old_band_op = 1;
1465 4 : res.stie.old_band_setup = 0;
1466 : }
1467 :
1468 6 : if (!fst_read_next_text_param(endp, response, sizeof(response),
1469 : &endp)) {
1470 2 : if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
1471 2 : res.stie.new_band_id = res.stie.old_band_id;
1472 : }
1473 :
1474 6 : return fst_session_send_action(&s, TRUE, &res, sizeof(res),
1475 6 : s.data.old_iface->mb_ie);
1476 : }
1477 :
1478 :
1479 12 : 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 12 : if (params[0] != ' ')
1489 1 : return -EINVAL;
1490 11 : params++;
1491 11 : fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1492 11 : if (!is_valid)
1493 1 : return -EINVAL;
1494 :
1495 10 : if (get_group_fill_session(&g, &s))
1496 0 : return -EINVAL;
1497 :
1498 10 : os_memset(&req, 0, sizeof(req));
1499 10 : req.action = FST_ACTION_ACK_REQUEST;
1500 10 : req.dialog_token = g->dialog_token;
1501 10 : req.fsts_id = host_to_le32(fsts_id);
1502 :
1503 10 : return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
1504 : }
1505 :
1506 :
1507 12 : 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 12 : if (params[0] != ' ')
1517 1 : return -EINVAL;
1518 11 : params++;
1519 11 : fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1520 11 : if (!is_valid)
1521 1 : return -EINVAL;
1522 :
1523 10 : if (get_group_fill_session(&g, &s))
1524 0 : return -EINVAL;
1525 :
1526 10 : os_memset(&res, 0, sizeof(res));
1527 10 : res.action = FST_ACTION_ACK_RESPONSE;
1528 10 : res.dialog_token = g->dialog_token;
1529 10 : res.fsts_id = host_to_le32(fsts_id);
1530 :
1531 10 : return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
1532 : }
1533 :
1534 :
1535 6 : 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 6 : if (params[0] != ' ')
1545 1 : return -EINVAL;
1546 5 : params++;
1547 5 : fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
1548 5 : if (!is_valid)
1549 1 : return -EINVAL;
1550 :
1551 4 : if (get_group_fill_session(&g, &s))
1552 0 : return -EINVAL;
1553 :
1554 4 : os_memset(&td, 0, sizeof(td));
1555 4 : td.action = FST_ACTION_TEAR_DOWN;
1556 4 : td.fsts_id = host_to_le32(fsts_id);
1557 :
1558 4 : return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
1559 : }
1560 :
1561 :
1562 11 : 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 11 : if (params[0] != ' ')
1570 1 : return FST_FSTS_ID_NOT_FOUND;
1571 10 : params++;
1572 10 : sid = fst_read_next_int_param(params, &is_valid, &endp);
1573 10 : if (!is_valid)
1574 1 : return FST_FSTS_ID_NOT_FOUND;
1575 :
1576 9 : s = fst_session_get_by_id(sid);
1577 9 : if (!s)
1578 1 : return FST_FSTS_ID_NOT_FOUND;
1579 :
1580 8 : return s->data.fsts_id;
1581 : }
1582 :
1583 :
1584 44 : 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 44 : if (request[0] != ' ')
1592 1 : return -EINVAL;
1593 43 : request++;
1594 86 : if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
1595 43 : !*ifname)
1596 : goto problem;
1597 43 : g = dl_list_first(&fst_global_groups_list, struct fst_group,
1598 : global_groups_lentry);
1599 43 : if (!g)
1600 0 : goto problem;
1601 43 : iface = fst_group_get_iface_by_name(g, ifname);
1602 43 : if (!iface || !iface->mb_ie)
1603 : goto problem;
1604 40 : return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
1605 40 : wpabuf_len(iface->mb_ie));
1606 :
1607 : problem:
1608 3 : return os_snprintf(buf, buflen, "FAIL\n");
1609 : }
1610 :
1611 : #endif /* CONFIG_FST_TEST */
|