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:
parent
3d799c0b2c
commit
22a299ee9d
2 changed files with 48 additions and 13 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue