[PATCH v2 2/2] AP: Set STA's assoc flag in the driver before sending assoc. resp.

Ilan Peer ilan.peer at intel.com
Mon Jan 25 02:29:20 PST 2016


From: Andrei Otcheretianski <andrei.otcheretianski at intel.com>

Previously stations were added to the driver only after the association
response is acked. In the time period between the station has acked the
association response and between the time the station was added to the
kernel, the station can already start sending data frames, which will be
dropped by the HW. In addition to the data loss, the driver may ignore
NDPs with PM bit set from this STA.

Fix this by setting/adding the STA with associated flag set to the
driver before the AP sends the association response with status success.
If the association response wasn't acked, remove the station from
the driver.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/ap/ieee802_11.c | 151 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 86 insertions(+), 65 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e664e03..eeeebe2 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1721,6 +1721,63 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
 }
 
 
+static int add_associated_sta(struct hostapd_data *hapd,
+			      struct sta_info *sta)
+{
+	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
+
+	/*
+	 * Remove the STA entry in order to make sure the STA PS state gets
+	 * cleared and configuration gets updated.
+	 * This is relevant for cases, such as FT over the DS, where a station
+	 * re-associates back to the same AP but skips the authentication flow.
+	 * Or, if working with a driver that doesn't support full AP client
+	 * state.
+	 */
+	if (!sta->added_unassoc)
+		hostapd_drv_sta_remove(hapd, sta->addr);
+
+#ifdef CONFIG_IEEE80211N
+	if (sta->flags & WLAN_STA_HT)
+		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (sta->flags & WLAN_STA_VHT)
+		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
+
+	/*
+	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
+	 * will be set when the assoc. resp. ACK is processed.
+	 */
+	if (hostapd_sta_add_set(hapd, sta->addr, sta->aid, sta->capability,
+				sta->supported_rates, sta->supported_rates_len,
+				sta->listen_interval,
+				sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+				sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+				sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
+				sta->vht_opmode,
+				sta->added_unassoc)) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+			       "Could not %s STA to kernel driver",
+			       sta->added_unassoc ? "set" : "add");
+
+		if (sta->added_unassoc) {
+			hostapd_drv_sta_remove(hapd, sta->addr);
+			sta->added_unassoc = 0;
+		}
+
+		return -1;
+	}
+
+	sta->added_unassoc = 0;
+
+	return 0;
+}
+
+
 static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 status_code, int reassoc, const u8 *ies,
 			   size_t ies_len)
@@ -2058,9 +2115,23 @@ static void handle_assoc(struct hostapd_data *hapd,
 	sta->timeout_next = STA_NULLFUNC;
 
  fail:
+	/*
+	 * In case of successful response add the station to the driver,
+	 * otherwise the kernel may ignore data frames before we process the
+	 * ack. In case of failure, this station will be removed
+	 */
+	if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta))
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
 	reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
-	if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
-				   reply_res != WLAN_STATUS_SUCCESS)) {
+
+	/*
+	 * Remove the station in case of failure tx a successful response (it
+	 * was added associated to the driver) or if the station was previously
+	 * added unassociated.
+	 */
+	if ((reply_res != WLAN_STATUS_SUCCESS &&
+	     resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) {
 		hostapd_drv_sta_remove(hapd, sta->addr);
 		sta->added_unassoc = 0;
 	}
@@ -2556,8 +2627,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 	u16 status;
 	struct sta_info *sta;
 	int new_assoc = 1;
-	struct ieee80211_ht_capabilities ht_cap;
-	struct ieee80211_vht_capabilities vht_cap;
 
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
@@ -2571,21 +2640,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 		wpa_printf(MSG_INFO,
 			   "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
 			   reassoc, (unsigned long)len);
-		goto remove_sta;
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		return;
 	}
 
+	if (reassoc)
+		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	else
+		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
 	if (!ok) {
 		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "did not acknowledge association response");
 		sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
-		goto remove_sta;
-	}
 
-	if (reassoc)
-		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
-	else
-		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+		/* The STA is added only in case of SUCCESS */
+		if (status == WLAN_STATUS_SUCCESS)
+			hostapd_drv_sta_remove(hapd, sta->addr);
+
+		return;
+	}
 
 	if (status != WLAN_STATUS_SUCCESS)
 		return;
@@ -2621,53 +2696,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 	sta->sa_query_timed_out = 0;
 #endif /* CONFIG_IEEE80211W */
 
-	/*
-	 * Remove the STA entry in order to make sure the STA PS state gets
-	 * cleared and configuration gets updated.
-	 * This is relevant for cases, such as FT over the DS, where a station
-	 * re-associates back to the same AP but skips the authentication flow.
-	 * Or, if working with a driver that doesn't support full AP client
-	 * state.
-	 */
-	if (!sta->added_unassoc)
-		hostapd_drv_sta_remove(hapd, sta->addr);
-
-#ifdef CONFIG_IEEE80211N
-	if (sta->flags & WLAN_STA_HT)
-		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
-#endif /* CONFIG_IEEE80211N */
-#ifdef CONFIG_IEEE80211AC
-	if (sta->flags & WLAN_STA_VHT)
-		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
-#endif /* CONFIG_IEEE80211AC */
-
-	if (hostapd_sta_add_set(hapd, sta->addr, sta->aid, sta->capability,
-				sta->supported_rates, sta->supported_rates_len,
-				sta->listen_interval,
-				sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
-				sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
-				sta->flags, sta->qosinfo, sta->vht_opmode,
-				sta->added_unassoc)) {
-		hostapd_logger(hapd, sta->addr,
-			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
-			       "Could not %s STA to kernel driver",
-			       sta->added_unassoc ? "set" : "add");
-		ap_sta_disconnect(hapd, sta, sta->addr,
-				  WLAN_REASON_DISASSOC_AP_BUSY);
-		if (sta->added_unassoc)
-			goto remove_sta;
-		return;
-	}
-
-	/*
-	 * added_unassoc flag is set for a station that was added to the driver
-	 * in unassociated state.
-	 * Clear this flag once the station has completed association, to make
-	 * sure STA entry will be cleared from the driver in case of
-	 * reassocition back to the same AP.
-	 */
-	sta->added_unassoc = 0;
-
 	if (sta->flags & WLAN_STA_WDS) {
 		int ret;
 		char ifname_wds[IFNAMSIZ + 1];
@@ -2699,14 +2727,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 	else
 		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);
-
-remove_sta:
-	if (sta->added_unassoc) {
-		hostapd_drv_sta_remove(hapd, sta->addr);
-		sta->added_unassoc = 0;
-	}
 }
 
 
-- 
1.9.1




More information about the Hostap mailing list