Improve EAPOL-Key handshake stability with retransmitted frames

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.
This commit is contained in:
Jouni Malinen 2008-12-16 14:17:33 +02:00 committed by Jouni Malinen
parent 3d799c0b2c
commit 22a299ee9d
2 changed files with 48 additions and 13 deletions

View file

@ -498,7 +498,7 @@ void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
if (sm->started) { 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; sm->ReAuthenticationRequest = TRUE;
wpa_sm_step(sm); wpa_sm_step(sm);
return; 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, void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, struct wpa_state_machine *sm,
u8 *data, size_t data_len) 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) && if (!(key_info & WPA_KEY_INFO_REQUEST) &&
(!sm->key_replay_counter_valid || !wpa_replay_counter_valid(sm, key->replay_counter)) {
os_memcmp(key->replay_counter, sm->key_replay_counter, int i;
WPA_REPLAY_COUNTER_LEN) != 0)) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key %s with unexpected " "received EAPOL-Key %s with unexpected "
"replay counter", msgtxt); "replay counter", msgtxt);
wpa_hexdump(MSG_DEBUG, "expected replay counter", for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); 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", wpa_hexdump(MSG_DEBUG, "received replay counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN); key->replay_counter, WPA_REPLAY_COUNTER_LEN);
return; return;
@ -842,8 +861,12 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_rekey_gtk(wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL);
} }
} else { } else {
/* Do not allow the same key replay counter to be reused. */ /* Do not allow the same key replay counter to be reused. This
sm->key_replay_counter_valid = FALSE; * 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 #ifdef CONFIG_PEERKEY
@ -914,6 +937,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
int key_data_len, pad_len = 0; int key_data_len, pad_len = 0;
u8 *buf, *pos; u8 *buf, *pos;
int version, pairwise; int version, pairwise;
int i;
len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); 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); WPA_PUT_BE16(key->key_length, 0);
/* FIX: STSL: what to use as key_replay_counter? */ /* FIX: STSL: what to use as key_replay_counter? */
inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
os_memcpy(key->replay_counter, sm->key_replay_counter, 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); WPA_REPLAY_COUNTER_LEN);
sm->key_replay_counter_valid = TRUE; sm->key_replay[0].valid = TRUE;
if (nonce) if (nonce)
os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);

View file

@ -15,6 +15,9 @@
#ifndef WPA_AUTH_I_H #ifndef WPA_AUTH_I_H
#define WPA_AUTH_I_H #define WPA_AUTH_I_H
/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
#define RSNA_MAX_EAPOL_RETRIES 3
struct wpa_group; struct wpa_group;
struct wpa_stsl_negotiation { struct wpa_stsl_negotiation {
@ -66,8 +69,10 @@ struct wpa_state_machine {
Boolean pairwise_set; Boolean pairwise_set;
int keycount; int keycount;
Boolean Pair; Boolean Pair;
u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; struct {
Boolean key_replay_counter_valid; u8 counter[WPA_REPLAY_COUNTER_LEN];
Boolean valid;
} key_replay[RSNA_MAX_EAPOL_RETRIES];
Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */
Boolean has_GTK; Boolean has_GTK;