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