]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: Drop retransmitted auth/assoc/action frames
authorIlan Peer <ilan.peer@intel.com>
Wed, 5 Nov 2014 08:50:34 +0000 (03:50 -0500)
committerJouni Malinen <j@w1.fi>
Wed, 19 Nov 2014 13:20:47 +0000 (15:20 +0200)
It is possible that a station device might miss an ACK for an
authentication, association, or action frame, and thus retransmit the
same frame although the frame is already being processed in the stack.
While the duplicated frame should really be dropped in the kernel or
firmware code where duplicate detection is implemented for data frames,
it is possible that pre-association cases are not fully addressed (which
is the case at least with mac80211 today) and the frame may be delivered
to upper layer stack.

In such a case, the local AP will process the retransmitted frame although
it has already handled the request, which might cause the station to get
confused and as a result disconnect from the AP, blacklist it, etc.

To avoid such a case, save the sequence control of the last processed
management frame and in case of retransmissions drop them.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
src/ap/ieee802_11.c
src/ap/sta_info.c
src/ap/sta_info.h
src/common/ieee802_11_defs.h

index 4e389d07b8e7826b5606327ef8e011f60e04338f..8b903b3f8278928cec6884e605c6d55b7129da01 100644 (file)
@@ -709,6 +709,7 @@ static void handle_auth(struct hostapd_data *hapd,
        size_t resp_ies_len = 0;
        char *identity = NULL;
        char *radius_cui = NULL;
+       u16 seq_ctrl;
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
                wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -730,6 +731,7 @@ static void handle_auth(struct hostapd_data *hapd,
        auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
        status_code = le_to_host16(mgmt->u.auth.status_code);
        fc = le_to_host16(mgmt->frame_control);
+       seq_ctrl = le_to_host16(mgmt->seq_ctrl);
 
        if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
            2 + WLAN_AUTH_CHALLENGE_LEN &&
@@ -738,10 +740,12 @@ static void handle_auth(struct hostapd_data *hapd,
                challenge = &mgmt->u.auth.variable[2];
 
        wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
-                  "auth_transaction=%d status_code=%d wep=%d%s",
+                  "auth_transaction=%d status_code=%d wep=%d%s "
+                  "seq_ctrl=0x%x%s",
                   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
                   status_code, !!(fc & WLAN_FC_ISWEP),
-                  challenge ? " challenge" : "");
+                  challenge ? " challenge" : "",
+                  seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 
        if (hapd->tkip_countermeasures) {
                resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
@@ -802,21 +806,36 @@ static void handle_auth(struct hostapd_data *hapd,
                return;
        }
 
+       sta = ap_get_sta(hapd, mgmt->sa);
+       if (sta) {
+               if ((fc & WLAN_FC_RETRY) &&
+                   sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+                   sta->last_seq_ctrl == seq_ctrl &&
+                   sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Drop repeated authentication frame seq_ctrl=0x%x",
+                                      seq_ctrl);
+                       return;
+               }
+       } else {
 #ifdef CONFIG_MESH
-       if (hapd->conf->mesh & MESH_ENABLED) {
-               /* if the mesh peer is not available, we don't do auth. */
-               sta = ap_get_sta(hapd, mgmt->sa);
-               if (!sta)
+               if (hapd->conf->mesh & MESH_ENABLED) {
+                       /* if the mesh peer is not available, we don't do auth.
+                        */
                        return;
-       } else
+               }
 #endif /* CONFIG_MESH */
-       {
+
                sta = ap_sta_add(hapd, mgmt->sa);
                if (!sta) {
                        resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
                        goto fail;
                }
        }
+       sta->last_seq_ctrl = seq_ctrl;
+       sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
        if (vlan_id > 0) {
                if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
@@ -1464,7 +1483,7 @@ static void handle_assoc(struct hostapd_data *hapd,
                         const struct ieee80211_mgmt *mgmt, size_t len,
                         int reassoc)
 {
-       u16 capab_info, listen_interval;
+       u16 capab_info, listen_interval, seq_ctrl, fc;
        u16 resp = WLAN_STATUS_SUCCESS;
        const u8 *pos;
        int left, i;
@@ -1497,15 +1516,19 @@ static void handle_assoc(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_TESTING_OPTIONS */
 
+       fc = le_to_host16(mgmt->frame_control);
+       seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
        if (reassoc) {
                capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
                listen_interval = le_to_host16(
                        mgmt->u.reassoc_req.listen_interval);
                wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
                           " capab_info=0x%02x listen_interval=%d current_ap="
-                          MACSTR,
+                          MACSTR " seq_ctrl=0x%x%s",
                           MAC2STR(mgmt->sa), capab_info, listen_interval,
-                          MAC2STR(mgmt->u.reassoc_req.current_ap));
+                          MAC2STR(mgmt->u.reassoc_req.current_ap),
+                          seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
                left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
                pos = mgmt->u.reassoc_req.variable;
        } else {
@@ -1513,8 +1536,10 @@ static void handle_assoc(struct hostapd_data *hapd,
                listen_interval = le_to_host16(
                        mgmt->u.assoc_req.listen_interval);
                wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
-                          " capab_info=0x%02x listen_interval=%d",
-                          MAC2STR(mgmt->sa), capab_info, listen_interval);
+                          " capab_info=0x%02x listen_interval=%d "
+                          "seq_ctrl=0x%x%s",
+                          MAC2STR(mgmt->sa), capab_info, listen_interval,
+                          seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
                left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
                pos = mgmt->u.assoc_req.variable;
        }
@@ -1540,6 +1565,21 @@ static void handle_assoc(struct hostapd_data *hapd,
                return;
        }
 
+       if ((fc & WLAN_FC_RETRY) &&
+           sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+           sta->last_seq_ctrl == seq_ctrl &&
+           sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+           WLAN_FC_STYPE_ASSOC_REQ) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "Drop repeated association frame seq_ctrl=0x%x",
+                              seq_ctrl);
+               return;
+       }
+       sta->last_seq_ctrl = seq_ctrl;
+       sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+               WLAN_FC_STYPE_ASSOC_REQ;
+
        if (hapd->tkip_countermeasures) {
                resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
                goto fail;
@@ -1665,6 +1705,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
        }
 
        ap_sta_set_authorized(hapd, sta, 0);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
        wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
        hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1716,6 +1757,7 @@ static void handle_deauth(struct hostapd_data *hapd,
        }
 
        ap_sta_set_authorized(hapd, sta, 0);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
                        WLAN_STA_ASSOC_REQ_OK);
        wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
@@ -1815,6 +1857,26 @@ static int handle_action(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_IEEE80211W */
 
+       if (sta) {
+               u16 fc = le_to_host16(mgmt->frame_control);
+               u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+               if ((fc & WLAN_FC_RETRY) &&
+                   sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+                   sta->last_seq_ctrl == seq_ctrl &&
+                   sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Drop repeated action frame seq_ctrl=0x%x",
+                                      seq_ctrl);
+                       return 1;
+               }
+
+               sta->last_seq_ctrl = seq_ctrl;
+               sta->last_subtype = WLAN_FC_STYPE_ACTION;
+       }
+
        switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
        case WLAN_ACTION_FT:
index c48222e40a33861fd990a0bc7b76d7a18cad8054..19292a445f589eb5821992e14ae4de6ded3aef3b 100644 (file)
@@ -604,6 +604,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
        ap_sta_hash_add(hapd, sta);
        sta->ssid = &hapd->conf->ssid;
        ap_sta_remove_in_other_bss(hapd, sta);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 
        return sta;
 }
@@ -668,6 +669,7 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 {
        wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
        ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_DEAUTH;
@@ -706,6 +708,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 {
        wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
        ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_REMOVE;
index 76e34e4052074ac345c758e10e85a3a36b6d3780..ac7269f6ab9ae9f98a237e2fd9798214fe49f154 100644 (file)
@@ -158,6 +158,12 @@ struct sta_info {
 #endif /* CONFIG_SAE */
 
        u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+       /* Last Authentication/(Re)Association Request/Action frame sequence
+        * control */
+       u16 last_seq_ctrl;
+       /* Last Authentication/(Re)Association Request/Action frame subtype */
+       u8 last_subtype;
 };
 
 
index 14a0ddc94e2078379d7c57de830aaa18aae9e03c..10ad2eeb1f085938e0e2112c41cfcef336ea7dc5 100644 (file)
@@ -25,6 +25,8 @@
 #define WLAN_FC_GET_TYPE(fc)   (((fc) & 0x000c) >> 2)
 #define WLAN_FC_GET_STYPE(fc)  (((fc) & 0x00f0) >> 4)
 
+#define WLAN_INVALID_MGMT_SEQ   0xFFFF
+
 #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
 #define WLAN_GET_SEQ_SEQ(seq) \
        (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)