diff mbox

AP: save EAPOL received before assoc resp

Message ID 1456937426-8128-1-git-send-email-eliad@wizery.com
State Changes Requested
Headers show

Commit Message

Eliad Peller March 2, 2016, 4:50 p.m. UTC
There is a race condition in which wpa_supplicant might
receive the EAPOL-Start frame (from the just-associated
station) before the tx completion of the assoc response.

This in turn will cause the EAPOL-Start frame to get
dropped, and potentially failing the connection.

Solve this by saving EAPOLs from authenticated-but-not-
associated stations, and handling them during the assoc
response tx completion processing.

Signed-off-by: Eliad Peller <eliad@wizery.com>
---
 src/ap/ieee802_11.c | 19 +++++++++++++++++++
 src/ap/ieee802_1x.c | 15 +++++++++++++++
 src/ap/sta_info.h   |  4 ++++
 3 files changed, 38 insertions(+)

Comments

Jouni Malinen March 4, 2016, 7:32 p.m. UTC | #1
On Wed, Mar 02, 2016 at 06:50:26PM +0200, Eliad Peller wrote:
> There is a race condition in which wpa_supplicant might
> receive the EAPOL-Start frame (from the just-associated
> station) before the tx completion of the assoc response.

What is the point of that wpa_supplicant reference here? Isn't this
supposed to apply to AP side functionality? In other words, this applies
to both hostapd and wpa_supplicant (if using AP mode).

> This in turn will cause the EAPOL-Start frame to get
> dropped, and potentially failing the connection.

What kind of connection fails due to one EAPOL-Start frame being
dropped?

> Solve this by saving EAPOLs from authenticated-but-not-
> associated stations, and handling them during the assoc
> response tx completion processing.

I'm probably fine with this change in general, but couple of comments on
the implementation:

> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
> @@ -2782,6 +2782,25 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
> +	if (sta->pending_eapol_rx) {
> +		struct os_reltime now, age;
> +		os_get_reltime(&now);
> +		os_reltime_sub(&now, &sta->pending_eapol_rx_time, &age);
> +		if (age.sec == 0 && age.usec < 200000 &&
> +		    os_memcmp(sta->pending_eapol_rx_src,
> +			      mgmt->da, ETH_ALEN) == 0) {

What is the point of comparing sta->pending_eapol_rx_src to mgmt->da?
This is after sta = ap_get_sta(hapd, mgmt->da) and there is not really
any possibility for these to be different..

> diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
> @@ -891,6 +891,18 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
> +		if (sta && (sta->flags & WLAN_STA_AUTH)) {
> +			wpa_printf(MSG_DEBUG, "Saving EAPOL for later use");
> +			wpabuf_free(sta->pending_eapol_rx);
> +			sta->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
> +			if (sta->pending_eapol_rx) {
> +				os_get_reltime(&sta->pending_eapol_rx_time);
> +				os_memcpy(sta->pending_eapol_rx_src, sa,
> +					  ETH_ALEN);

Similarly here.. Why is sa copied into sta->pending_eapol_rx_src when
sta->addr already contains the MAC address of the STA which is sa here
after sta = ap_get_sta(hapd, sa).

> diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
> @@ -113,6 +113,10 @@ struct sta_info {
> +	struct wpabuf *pending_eapol_rx;
> +	struct os_reltime pending_eapol_rx_time;
> +	u8 pending_eapol_rx_src[ETH_ALEN];

pending_eapol_rx_src should go away. One allocation could be used to
store both the pending frame and pending_eapol_rx_time to reduce the
size of struct sta_info due to this type of short-lived data.
Eliad Peller March 6, 2016, 8:43 a.m. UTC | #2
On Fri, Mar 4, 2016 at 9:32 PM, Jouni Malinen <j@w1.fi> wrote:
> On Wed, Mar 02, 2016 at 06:50:26PM +0200, Eliad Peller wrote:
>> There is a race condition in which wpa_supplicant might
>> receive the EAPOL-Start frame (from the just-associated
>> station) before the tx completion of the assoc response.
>
> What is the point of that wpa_supplicant reference here? Isn't this
> supposed to apply to AP side functionality? In other words, this applies
> to both hostapd and wpa_supplicant (if using AP mode).
>
i encountered it on during P2P tests, so i (mistakenly) referenced
wpa_supplicant. i'll fix it.

>> This in turn will cause the EAPOL-Start frame to get
>> dropped, and potentially failing the connection.
>
> What kind of connection fails due to one EAPOL-Start frame being
> dropped?
>
i guess the problem comes because the EAPOL wasn't simply dropped over
the air, but by the remote peer (after it was acked).

>> Solve this by saving EAPOLs from authenticated-but-not-
>> associated stations, and handling them during the assoc
>> response tx completion processing.
>
> I'm probably fine with this change in general, but couple of comments on
> the implementation:
>
>> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
>> @@ -2782,6 +2782,25 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
>> +     if (sta->pending_eapol_rx) {
>> +             struct os_reltime now, age;
>> +             os_get_reltime(&now);
>> +             os_reltime_sub(&now, &sta->pending_eapol_rx_time, &age);
>> +             if (age.sec == 0 && age.usec < 200000 &&
>> +                 os_memcmp(sta->pending_eapol_rx_src,
>> +                           mgmt->da, ETH_ALEN) == 0) {
>
> What is the point of comparing sta->pending_eapol_rx_src to mgmt->da?
> This is after sta = ap_get_sta(hapd, mgmt->da) and there is not really
> any possibility for these to be different..
>
you're right. this is a leftover from previous version where only a
single eapol was saved (in the hapd struct).

>> diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
>> @@ -891,6 +891,18 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
>> +             if (sta && (sta->flags & WLAN_STA_AUTH)) {
>> +                     wpa_printf(MSG_DEBUG, "Saving EAPOL for later use");
>> +                     wpabuf_free(sta->pending_eapol_rx);
>> +                     sta->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
>> +                     if (sta->pending_eapol_rx) {
>> +                             os_get_reltime(&sta->pending_eapol_rx_time);
>> +                             os_memcpy(sta->pending_eapol_rx_src, sa,
>> +                                       ETH_ALEN);
>
> Similarly here.. Why is sa copied into sta->pending_eapol_rx_src when
> sta->addr already contains the MAC address of the STA which is sa here
> after sta = ap_get_sta(hapd, sa).
>
ditto.

>> diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
>> @@ -113,6 +113,10 @@ struct sta_info {
>> +     struct wpabuf *pending_eapol_rx;
>> +     struct os_reltime pending_eapol_rx_time;
>> +     u8 pending_eapol_rx_src[ETH_ALEN];
>
> pending_eapol_rx_src should go away. One allocation could be used to
> store both the pending frame and pending_eapol_rx_time to reduce the
> size of struct sta_info due to this type of short-lived data.
>
thanks for the review.
i'll update the patch.

Eliad.
diff mbox

Patch

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index b36e68d..d221825 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2782,6 +2782,25 @@  static void handle_assoc_cb(struct hostapd_data *hapd,
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+	if (sta->pending_eapol_rx) {
+		struct os_reltime now, age;
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &sta->pending_eapol_rx_time, &age);
+		if (age.sec == 0 && age.usec < 200000 &&
+		    os_memcmp(sta->pending_eapol_rx_src,
+			      mgmt->da, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG, "Process pending EAPOL "
+				   "frame that was received just before "
+				   "association notification");
+			ieee802_1x_receive(
+				hapd, sta->pending_eapol_rx_src,
+				wpabuf_head(sta->pending_eapol_rx),
+				wpabuf_len(sta->pending_eapol_rx));
+		}
+		wpabuf_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+	}
 }
 
 
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index c774d5c..e34d0d3 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -891,6 +891,18 @@  void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
 		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
 			   "associated/Pre-authenticating STA");
+
+		if (sta && (sta->flags & WLAN_STA_AUTH)) {
+			wpa_printf(MSG_DEBUG, "Saving EAPOL for later use");
+			wpabuf_free(sta->pending_eapol_rx);
+			sta->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
+			if (sta->pending_eapol_rx) {
+				os_get_reltime(&sta->pending_eapol_rx_time);
+				os_memcpy(sta->pending_eapol_rx_src, sa,
+					  ETH_ALEN);
+			}
+		}
+
 		return;
 	}
 
@@ -1183,6 +1195,9 @@  void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
 	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
 #endif /* CONFIG_HS20 */
 
+	wpabuf_free(sta->pending_eapol_rx);
+	sta->pending_eapol_rx = NULL;
+
 	if (sm == NULL)
 		return;
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e223341..ecba920 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -113,6 +113,10 @@  struct sta_info {
 	/* IEEE 802.1X related data */
 	struct eapol_state_machine *eapol_sm;
 
+	struct wpabuf *pending_eapol_rx;
+	struct os_reltime pending_eapol_rx_time;
+	u8 pending_eapol_rx_src[ETH_ALEN];
+
 	u64 acct_session_id;
 	struct os_reltime acct_session_start;
 	int acct_session_started;