Improve EAPOL-Key handshake stability with retransmitted frames
authorJouni Malinen <jouni.malinen@atheros.com>
Tue, 16 Dec 2008 12:17:33 +0000 (14:17 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 16 Dec 2008 12:17:33 +0000 (14:17 +0200)
Accept response to any pending request, not just the last one. This
gives the Supplicant more time to reply since hostapd will now allow up
to three seconds for the reply to the first EAPOL-Key frame transmission
(and two seconds for the first retry and one second for the last) while
the previous version invalidated any old request immediately when
sending a retransmitted frame.

If the Supplicant replies to more than one request, only the first reply
to arrive at the Authenticator will be processed. As far as the
Supplicant is concerned, this behavior does not differ from the previous
one except for being less likely to cause unneeded retransmissions of
EAPOL-Key frames.

This can help in cases where power saving is used when the group key is
rekeyed or when there is excessive traffic on the channel that can delay
(or drop) EAPOL-Key frames.

hostapd/wpa.c
hostapd/wpa_auth_i.h

index 946552c..4f56354 100644 (file)
@@ -498,7 +498,7 @@ void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_IEEE80211R */
 
        if (sm->started) {
-               os_memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+               os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
                sm->ReAuthenticationRequest = TRUE;
                wpa_sm_step(sm);
                return;
@@ -574,6 +574,21 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
 }
 
 
+static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
+                                   const u8 *replay_counter)
+{
+       int i;
+       for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+               if (!sm->key_replay[i].valid)
+                       break;
+               if (os_memcmp(replay_counter, sm->key_replay[i].counter,
+                             WPA_REPLAY_COUNTER_LEN) == 0)
+                       return 1;
+       }
+       return 0;
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
@@ -672,14 +687,18 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        }
 
        if (!(key_info & WPA_KEY_INFO_REQUEST) &&
-           (!sm->key_replay_counter_valid ||
-            os_memcmp(key->replay_counter, sm->key_replay_counter,
-                      WPA_REPLAY_COUNTER_LEN) != 0)) {
+           !wpa_replay_counter_valid(sm, key->replay_counter)) {
+               int i;
                wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
                                 "received EAPOL-Key %s with unexpected "
                                 "replay counter", msgtxt);
-               wpa_hexdump(MSG_DEBUG, "expected replay counter",
-                           sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN);
+               for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+                       if (!sm->key_replay[i].valid)
+                               break;
+                       wpa_hexdump(MSG_DEBUG, "pending replay counter",
+                                   sm->key_replay[i].counter,
+                                   WPA_REPLAY_COUNTER_LEN);
+               }
                wpa_hexdump(MSG_DEBUG, "received replay counter",
                            key->replay_counter, WPA_REPLAY_COUNTER_LEN);
                return;
@@ -842,8 +861,12 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_rekey_gtk(wpa_auth, NULL);
                }
        } else {
-               /* Do not allow the same key replay counter to be reused. */
-               sm->key_replay_counter_valid = FALSE;
+               /* Do not allow the same key replay counter to be reused. This
+                * does also invalidate all other pending replay counters if
+                * retransmissions were used, i.e., we will only process one of
+                * the pending replies and ignore rest if more than one is
+                * received. */
+               sm->key_replay[0].valid = FALSE;
        }
 
 #ifdef CONFIG_PEERKEY
@@ -914,6 +937,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        int key_data_len, pad_len = 0;
        u8 *buf, *pos;
        int version, pairwise;
+       int i;
 
        len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
 
@@ -986,10 +1010,16 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                WPA_PUT_BE16(key->key_length, 0);
 
        /* FIX: STSL: what to use as key_replay_counter? */
-       inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN);
-       os_memcpy(key->replay_counter, sm->key_replay_counter,
+       for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+               sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+               os_memcpy(sm->key_replay[i].counter,
+                         sm->key_replay[i - 1].counter,
+                         WPA_REPLAY_COUNTER_LEN);
+       }
+       inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+       os_memcpy(key->replay_counter, sm->key_replay[0].counter,
                  WPA_REPLAY_COUNTER_LEN);
-       sm->key_replay_counter_valid = TRUE;
+       sm->key_replay[0].valid = TRUE;
 
        if (nonce)
                os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
index 7770d17..3ee6548 100644 (file)
@@ -15,6 +15,9 @@
 #ifndef WPA_AUTH_I_H
 #define WPA_AUTH_I_H
 
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 3
+
 struct wpa_group;
 
 struct wpa_stsl_negotiation {
@@ -66,8 +69,10 @@ struct wpa_state_machine {
        Boolean pairwise_set;
        int keycount;
        Boolean Pair;
-       u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN];
-       Boolean key_replay_counter_valid;
+       struct {
+               u8 counter[WPA_REPLAY_COUNTER_LEN];
+               Boolean valid;
+       } key_replay[RSNA_MAX_EAPOL_RETRIES];
        Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
        Boolean PTKRequest; /* not in IEEE 802.11i state machine */
        Boolean has_GTK;