FST: Fix handling of Rx FST Setup Request when session already exists
[mech_eap.git] / src / fst / fst_session.c
index ac49fcf..9e4dada 100644 (file)
@@ -376,6 +376,12 @@ static void fst_session_handle_setup_request(struct fst_iface *iface,
        plen = frame_len - IEEE80211_HDRLEN - 1;
        req = (const struct fst_setup_req *)
                (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+       if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+           req->stie.length < 11) {
+               fst_printf_iface(iface, MSG_WARNING,
+                                "FST Request dropped: invalid STIE");
+               return;
+       }
 
        if (req->stie.new_band_id == req->stie.old_band_id) {
                fst_printf_iface(iface, MSG_WARNING,
@@ -441,7 +447,9 @@ static void fst_session_handle_setup_request(struct fst_iface *iface,
                 * the initiator’s MAC address, in which case, the responder
                 * shall delete the received FST Setup Request.
                 */
-               if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
+               if (fst_session_is_ready_pending(s) &&
+                   /* waiting for Setup Response */
+                   os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
                        fst_printf_session(s, MSG_WARNING,
                                           "FST Request dropped due to MAC comparison (our MAC is "
                                           MACSTR ")",
@@ -449,23 +457,26 @@ static void fst_session_handle_setup_request(struct fst_iface *iface,
                        return;
                }
 
-               if (!fst_session_is_ready_pending(s)) {
-                       fst_printf_session(s, MSG_WARNING,
-                                          "FST Request from " MACSTR
-                                          " dropped due to inappropriate state %s",
-                                          MAC2STR(mgmt->da),
-                                          fst_session_state_name(s->state));
-                       return;
-               }
+               /*
+                * State is SETUP_COMPLETION (either in transition or not) or
+                * TRANSITION_DONE (in transition).
+                * Setup Request arriving in this state could mean:
+                * 1. peer sent it before receiving our Setup Request (race
+                *    condition)
+                * 2. peer didn't receive our Setup Response. Peer is retrying
+                *    after STT timeout
+                * 3. peer's FST state machines are out of sync due to some
+                *    other reason
+                *
+                * We will reset our session and create a new one instead.
+                */
 
+               fst_printf_session(s, MSG_WARNING,
+                       "resetting due to FST request");
 
                /*
                 * If FST Setup Request arrived with the same FSTS ID as one we
-                * initialized before, it means the other side either didn't
-                * receive our FST Request or skipped it for some reason (for
-                * example, due to numerical MAC comparison).
-                *
-                * In this case, there's no need to tear down the session.
+                * initialized before, there's no need to tear down the session.
                 * Moreover, as FSTS ID is the same, the other side will
                 * associate this tear down with the session it initiated that
                 * will break the sync.
@@ -477,7 +488,6 @@ static void fst_session_handle_setup_request(struct fst_iface *iface,
                                           "Skipping TearDown as the FST request has the same FSTS ID as initiated");
                fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
                fst_session_stt_disarm(s);
-               fst_printf_session(s, MSG_WARNING, "reset due to FST request");
        }
 
        s = fst_session_create(g);
@@ -539,6 +549,12 @@ static void fst_session_handle_setup_response(struct fst_session *s,
        }
        res = (const struct fst_setup_res *)
                (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+       if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+           res->stie.length < 11) {
+               fst_printf_iface(iface, MSG_WARNING,
+                                "FST Response dropped: invalid STIE");
+               return;
+       }
 
        if (res->dialog_token != s->data.pending_setup_req_dlgt)  {
                fst_printf_session(s, MSG_WARNING,
@@ -851,13 +867,15 @@ int fst_session_initiate_setup(struct fst_session *s)
                return -EINVAL;
        }
 
-       if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
+       if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
+                                   FALSE)) {
                fst_printf_session(s, MSG_ERROR,
                                   "The preset old peer address is not connected");
                return -EINVAL;
        }
 
-       if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) {
+       if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
+                                   FALSE)) {
                fst_printf_session(s, MSG_ERROR,
                                   "The preset new peer address is not connected");
                return -EINVAL;
@@ -954,7 +972,8 @@ int fst_session_respond(struct fst_session *s, u8 status_code)
                return -EINVAL;
        }
 
-       if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
+       if (!fst_iface_is_connected(s->data.old_iface,
+                                   s->data.old_peer_addr, FALSE)) {
                fst_printf_session(s, MSG_ERROR,
                                   "The preset peer address is not in the peer list");
                return -EINVAL;
@@ -1316,13 +1335,11 @@ static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
        struct fst_get_peer_ctx *ctx;
 
        os_memset(s, 0, sizeof(*s));
-       *g = dl_list_first(&fst_global_groups_list,
-                          struct fst_group, global_groups_lentry);
-       if (!*g)
-               return -EINVAL;
-
-       s->data.new_iface = dl_list_first(&(*g)->ifaces, struct fst_iface,
-                                         group_lentry);
+       foreach_fst_group(*g) {
+               s->data.new_iface = fst_group_first_iface(*g);
+               if (s->data.new_iface)
+                       break;
+       }
        if (!s->data.new_iface)
                return -EINVAL;