FST: Fix handling of Rx FST Setup Request when session already exists
[mech_eap.git] / src / fst / fst_session.c
index 6ce5e54..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,
@@ -615,11 +631,6 @@ static void fst_session_handle_tear_down(struct fst_session *s,
                },
        };
 
-       if (!fst_session_is_in_progress(s)) {
-               fst_printf_session(s, MSG_WARNING, "no FST Setup to tear down");
-               return;
-       }
-
        if (plen < sizeof(*td)) {
                fst_printf_session(s, MSG_WARNING,
                                   "Too short FST Tear Down dropped");
@@ -856,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;
@@ -898,7 +911,7 @@ int fst_session_initiate_setup(struct fst_session *s)
        req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
        /* 8.4.2.147 Session Transition element */
        req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
-       req.stie.length = sizeof(req.stie);
+       req.stie.length = sizeof(req.stie) - 2;
        req.stie.fsts_id = host_to_le32(fsts_id);
        req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
@@ -959,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;
@@ -973,9 +987,10 @@ int fst_session_respond(struct fst_session *s, u8 status_code)
        res.dialog_token = s->data.pending_setup_req_dlgt;
        res.status_code = status_code;
 
+       res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+       res.stie.length = sizeof(res.stie) - 2;
+
        if (status_code == WLAN_STATUS_SUCCESS) {
-               res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
-               res.stie.length = sizeof(res.stie);
                res.stie.fsts_id = s->data.fsts_id;
                res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
@@ -1320,28 +1335,26 @@ 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;
+               return -EINVAL;
 
        s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
                                          struct fst_iface, group_lentry);
        if (!s->data.old_iface)
-               return EINVAL;
+               return -EINVAL;
 
        old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
        if (!old_addr)
-               return EINVAL;
+               return -EINVAL;
 
        new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
        if (!new_addr)
-               return EINVAL;
+               return -EINVAL;
 
        os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
        os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
@@ -1364,19 +1377,22 @@ int fst_test_req_send_fst_request(const char *params)
        u8 channel;
        char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
 
+       if (params[0] != ' ')
+               return -EINVAL;
+       params++;
        fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
-               return EINVAL;
+               return -EINVAL;
 
        if (get_group_fill_session(&g, &s))
-               return EINVAL;
+               return -EINVAL;
 
        req.action = FST_ACTION_SETUP_REQUEST;
        req.dialog_token = g->dialog_token;
        req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
        /* 8.4.2.147 Session Transition element */
        req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
-       req.stie.length = sizeof(req.stie);
+       req.stie.length = sizeof(req.stie) - 2;
        req.stie.fsts_id = host_to_le32(fsts_id);
        req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
@@ -1415,12 +1431,15 @@ int fst_test_req_send_fst_response(const char *params)
        char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
        struct fst_session *_s;
 
+       if (params[0] != ' ')
+               return -EINVAL;
+       params++;
        fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
-               return EINVAL;
+               return -EINVAL;
 
        if (get_group_fill_session(&g, &s))
-               return EINVAL;
+               return -EINVAL;
 
        status_code = WLAN_STATUS_SUCCESS;
        if (!fst_read_next_text_param(endp, response, sizeof(response),
@@ -1442,9 +1461,10 @@ int fst_test_req_send_fst_response(const char *params)
                _s->data.pending_setup_req_dlgt : g->dialog_token;
        res.status_code  = status_code;
 
+       res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+       res.stie.length = sizeof(res.stie) - 2;
+
        if (res.status_code == WLAN_STATUS_SUCCESS) {
-               res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
-               res.stie.length = sizeof(res.stie);
                res.stie.fsts_id = fsts_id;
                res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
@@ -1481,12 +1501,15 @@ int fst_test_req_send_ack_request(const char *params)
        struct fst_session s;
        struct fst_group *g;
 
+       if (params[0] != ' ')
+               return -EINVAL;
+       params++;
        fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
-               return EINVAL;
+               return -EINVAL;
 
        if (get_group_fill_session(&g, &s))
-               return EINVAL;
+               return -EINVAL;
 
        os_memset(&req, 0, sizeof(req));
        req.action = FST_ACTION_ACK_REQUEST;
@@ -1506,12 +1529,15 @@ int fst_test_req_send_ack_response(const char *params)
        struct fst_session s;
        struct fst_group *g;
 
+       if (params[0] != ' ')
+               return -EINVAL;
+       params++;
        fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
-               return EINVAL;
+               return -EINVAL;
 
        if (get_group_fill_session(&g, &s))
-               return EINVAL;
+               return -EINVAL;
 
        os_memset(&res, 0, sizeof(res));
        res.action = FST_ACTION_ACK_RESPONSE;
@@ -1531,12 +1557,15 @@ int fst_test_req_send_tear_down(const char *params)
        struct fst_session s;
        struct fst_group *g;
 
+       if (params[0] != ' ')
+               return -EINVAL;
+       params++;
        fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
-               return EINVAL;
+               return -EINVAL;
 
        if (get_group_fill_session(&g, &s))
-               return EINVAL;
+               return -EINVAL;
 
        os_memset(&td, 0, sizeof(td));
        td.action = FST_ACTION_TEAR_DOWN;
@@ -1553,6 +1582,9 @@ u32 fst_test_req_get_fsts_id(const char *params)
        char *endp;
        struct fst_session *s;
 
+       if (params[0] != ' ')
+               return FST_FSTS_ID_NOT_FOUND;
+       params++;
        sid = fst_read_next_int_param(params, &is_valid, &endp);
        if (!is_valid)
                return FST_FSTS_ID_NOT_FOUND;
@@ -1572,6 +1604,9 @@ int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
        struct fst_group *g;
        struct fst_iface *iface;
 
+       if (request[0] != ' ')
+               return -EINVAL;
+       request++;
        if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
            !*ifname)
                goto problem;