Cumulative patch from commit 077dcfb8c48d2509a6e116c0de3ad57d2fbfe4fe
077dcfb AP: Debug print management frame TX result
ca911d6 MBO: Parse non-preferred channel list on the AP
3f48274 WNM: Fix a memory leak on AP error path
f5ca176 VLAN: Fix vlan_compare() for tagged VLANs
1260564 hostapd_cli: Add support for RAW command
940491c MBO: Mandate use of PMF for WPA2+MBO association (STA)
4c57228 MBO: Mandate use of PMF for WPA2+MBO association (AP)
8dd49f0 MBO: Update STA cellular data capability based on WNM Notification
6332aaf MBO: Track STA cellular data capability from association request
f3cb7a6 WNM: Minimal processing for WNM Notification Request frames on AP
e578343 MBO: Indicate WNM-Notification support on AP when MBO is enabled
990b7b6 Simplify hostapd_build_ap_extra_ies() with helper functions
d010048 MBO: Expire non-matching bss_tmp_disallowed entries as part of check
f4c74e1 MBO: Parse MBO IE in ieee802_11_parse_elems()
016082e MBO: Send WNM-Notification when cellular capabilities change
c0e2a17 hostapd: Add MBO IE to BSS Transition Management Request frame
fb9a1c3 hostapd: Add MBO IE to Beacon, Probe Response, Association Response
c484b19 Move Hotspot 2.0 element in (Re)Association Request frames
a0c38e5 Re-order elements in (Re)Association Request frames
9a493fa WNM: Add candidate list to BSS transition query
84d1c0f WNM: Add candidate list to BSS transition response
cf11ab7 utils: Derive phy type by frequency and bandwidth
c8082d2 MBO: Add MBO IE to BSS Transition Management Response frame
dd59990 MBO: Parse MBO IE in BSS Transition Management Request frames
5e57ba2 MBO: Add Supported Operating Classes element to Association Request
7d46f58 MBO: Add global operating class definitions
cb06cf3 MBO: Prevent association to APs that explicitly disallow this
c5d193d MBO: Add cellular capability to MBO IE
2d5b861 MBO: Send MBO WNM-Notification Request frames to notify changes
92c6e2e MBO: Implement MBO non-preferred channel report in Association Request
facf2c7 MBO: Add non-preferred channel configuration in wpa_supplicant
425dd78 MBO: Add Multi Band Operation definitions
a159958 ndis: Use the new get_ie() helper to avoid duplicated code
231b04b utils: Share a single helper function to get IE by ID
ea69d97 wpa_supplicant: Share a single get_mode() implementation
75cc211 VLAN: Check vlan_desc validity in a failure debug print
43022ab Use 64-bit TX/RX byte counters for statistics
3f81ac0 AP: Set STA assoc flag in the driver before sending Assoc Resp frame
bb598c3 AP: Add support for full station state
dc55b6b nl80211: Add support for full station state operations
5558b99 EAP-FAST peer: Remove fixed return value from eap_fast_parse_phase1()
4b16c15 EAP-pwd server: Use os_get_random() for unpredictable token
239952b DFS: Remove the os_random() fallback
98a516e WPS: Use only os_get_random() for PIN generation
f441e5a Use os_get_random() for Shared Key authentication challenge
8c676b5 Add RADIUS Service-Type attribute with a value of Framed
09d96de mesh: Drop Authentication frames from BLOCKED STA
70c9396 SAE: Fix PMKID calculation for PMKSA cache
1492fbb Print Acct-Session-Id and Acct-Multi-Session-Id 64-bit values
e21ceca kqueue: Use 0 instead of NULL for udata
640b0b9 ctype functions require an unsigned char
a5a3efc Fix compile on NetBSD for vlan
a084c24 wired: Fix compile on NetBSD for wired driver
634e2e2 Add CONFIG_ELOOP_KQUEUE to defconfig
99a94f5 nl80211: Avoid wpa_printf %s call with NULL pointer in set_param()
ba91e92 wpa_supplicant: Parse ifname argument from DATA_TEST_CONFIG
8be640b VLAN: Add per-STA vif option
d0bdc96 VLAN: Actually add tagged VLANs to AP_VLAN
f9c0018 VLAN: Factor out per-vid code in newlink/dellink
8e44c19 radius: Add tagged VLAN parsing
1889af2 VLAN: Separate station grouping and uplink configuration
3a583e0 OpenSSL: Fix PKCS#12 parsing of extra certificates with OpenSSL 1.0.1
ddd0032 wpa_cli: Clean up logical operation
24c382a TDLS: Clean up os_memcmp use
6136d43 trace: Free symbols on program exit
8bcf8de OpenSSL: Fix memory leak in PKCS12 additional certificate parsing
03e3ddf OpenSSL: Fix memory leak in HMAC_CTX compatibility wrapper function
d9a0f69 OpenSSL: Fix memory leak in OCSP parsing
29bc76e OpenSSL: Do not use library init/deinit functions with 1.1.0
0f09637 OpenSSL: Fix memory leak in subjectAltName parsing
e60913b curl: Fix memory leak in subjectAltName parsing
6014890 OpenSSL: Fix memory leak with EVP_CIPHER_CTX_new()
99a1735 rfkill: Fix a memory leak
1f1e599 OpenSSL: Fix memory leak on error path
b907491 wpa_supplicant: Basic support for PBSS/PCP
86b5c40 nl80211: Basic support for PBSS/PCP
afa453a Sync with mac80211-next.git include/uapi/linux/nl80211.h
d1d8a2b EAP peer: Simplify buildNotify return
1314bc1 Clean up EAP peer PCSC identity functions
Change-Id: I9db475a2a4ebc88d2ee024319ed59a850636bb16
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index ec6f8a7..84e6e15 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -42,6 +42,7 @@
#include "hw_features.h"
#include "ieee802_11.h"
#include "dfs.h"
+#include "mbo_ap.h"
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -207,16 +208,17 @@
if (!sta->challenge) {
/* Generate a pseudo-random challenge */
u8 key[8];
- struct os_time now;
- int r;
+
sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
if (sta->challenge == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- os_get_time(&now);
- r = os_random();
- os_memcpy(key, &now.sec, 4);
- os_memcpy(key + 4, &r, 4);
+ if (os_get_random(key, sizeof(key)) < 0) {
+ os_free(sta->challenge);
+ sta->challenge = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
rc4_skip(key, sizeof(key), 0,
sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
}
@@ -250,19 +252,20 @@
#endif /* CONFIG_NO_RC4 */
-static void send_auth_reply(struct hostapd_data *hapd,
- const u8 *dst, const u8 *bssid,
- u16 auth_alg, u16 auth_transaction, u16 resp,
- const u8 *ies, size_t ies_len)
+static int send_auth_reply(struct hostapd_data *hapd,
+ const u8 *dst, const u8 *bssid,
+ u16 auth_alg, u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len)
{
struct ieee80211_mgmt *reply;
u8 *buf;
size_t rlen;
+ int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
buf = os_zalloc(rlen);
if (buf == NULL)
- return;
+ return -1;
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -283,9 +286,13 @@
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len);
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
- wpa_printf(MSG_INFO, "send_auth_reply: send");
+ wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+ else
+ reply_res = WLAN_STATUS_SUCCESS;
os_free(buf);
+
+ return reply_res;
}
@@ -296,17 +303,25 @@
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ int reply_res;
- send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
- status, ies, ies_len);
-
- if (status != WLAN_STATUS_SUCCESS)
- return;
+ reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
+ auth_transaction, status, ies, ies_len);
sta = ap_get_sta(hapd, dst);
if (sta == NULL)
return;
+ if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
+ status != WLAN_STATUS_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
sta->flags |= WLAN_STA_AUTH;
@@ -369,18 +384,19 @@
const u8 *bssid, int update)
{
struct wpabuf *data;
+ int reply_res;
data = auth_build_sae_commit(hapd, sta, update);
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- send_auth_reply(hapd, sta->addr, bssid,
- WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
- wpabuf_head(data), wpabuf_len(data));
+ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
+ WLAN_STATUS_SUCCESS, wpabuf_head(data),
+ wpabuf_len(data));
wpabuf_free(data);
- return WLAN_STATUS_SUCCESS;
+ return reply_res;
}
@@ -389,18 +405,19 @@
const u8 *bssid)
{
struct wpabuf *data;
+ int reply_res;
data = auth_build_sae_confirm(hapd, sta);
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- send_auth_reply(hapd, sta->addr, bssid,
- WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
- wpabuf_head(data), wpabuf_len(data));
+ reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
+ WLAN_STATUS_SUCCESS, wpabuf_head(data),
+ wpabuf_len(data));
wpabuf_free(data);
- return WLAN_STATUS_SUCCESS;
+ return reply_res;
}
@@ -665,7 +682,7 @@
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->sae->state = SAE_ACCEPTED;
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
- sta->sae->pmk);
+ sta->sae->pmk, sta->sae->pmkid);
}
break;
case SAE_ACCEPTED:
@@ -698,15 +715,20 @@
const struct ieee80211_mgmt *mgmt, size_t len,
u16 auth_transaction, u16 status_code)
{
- u16 resp = WLAN_STATUS_SUCCESS;
+ int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
if (!sta->sae) {
- if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
- return;
+ if (auth_transaction != 1 ||
+ status_code != WLAN_STATUS_SUCCESS) {
+ resp = -1;
+ goto remove_sta;
+ }
sta->sae = os_zalloc(sizeof(*sta->sae));
- if (sta->sae == NULL)
- return;
+ if (!sta->sae) {
+ resp = -1;
+ goto remove_sta;
+ }
sta->sae->state = SAE_NOTHING;
sta->sae->sync = 0;
}
@@ -746,7 +768,8 @@
if (sta->sae->tmp->anti_clogging_token == NULL) {
wpa_printf(MSG_ERROR,
"SAE: Failed to alloc for anti-clogging token");
- return;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto remove_sta;
}
/*
@@ -756,10 +779,11 @@
* Authentication frame, and the commit-scalar and
* COMMIT-ELEMENT previously sent.
*/
- if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0);
+ if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
"SAE: Failed to send commit message");
- return;
+ goto remove_sta;
}
sta->sae->state = SAE_COMMITTED;
sta->sae->sync = 0;
@@ -768,7 +792,7 @@
}
if (status_code != WLAN_STATUS_SUCCESS)
- return;
+ goto remove_sta;
resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
((const u8 *) mgmt) + len -
@@ -778,14 +802,15 @@
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
MAC2STR(sta->addr));
- return;
+ goto remove_sta;
}
if (token && check_sae_token(hapd, sta->addr, token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
MAC2STR(sta->addr));
- return;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto remove_sta;
}
if (resp != WLAN_STATUS_SUCCESS)
@@ -810,7 +835,7 @@
"SAE authentication (RX confirm, status=%u)",
status_code);
if (status_code != WLAN_STATUS_SUCCESS)
- return;
+ goto remove_sta;
if (sta->sae->state >= SAE_CONFIRMED ||
!(hapd->conf->mesh & MESH_ENABLED)) {
if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
@@ -827,7 +852,7 @@
"unexpected SAE authentication transaction %u (status=%u)",
auth_transaction, status_code);
if (status_code != WLAN_STATUS_SUCCESS)
- return;
+ goto remove_sta;
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
@@ -838,6 +863,13 @@
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0);
}
+
+remove_sta:
+ if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+ status_code != WLAN_STATUS_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
wpabuf_free(data);
}
@@ -882,11 +914,11 @@
u16 auth_alg, auth_transaction, status_code;
u16 resp = WLAN_STATUS_SUCCESS;
struct sta_info *sta = NULL;
- int res;
+ int res, reply_res;
u16 fc;
const u8 *challenge = NULL;
u32 session_timeout, acct_interim_interval;
- int vlan_id = 0;
+ struct vlan_description vlan_id;
struct hostapd_sta_wpa_psk_short *psk = NULL;
u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
size_t resp_ies_len = 0;
@@ -894,6 +926,8 @@
char *radius_cui = NULL;
u16 seq_ctrl;
+ os_memset(&vlan_id, 0, sizeof(vlan_id));
+
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
(unsigned long) len);
@@ -1067,13 +1101,22 @@
seq_ctrl);
return;
}
+#ifdef CONFIG_MESH
+ if ((hapd->conf->mesh & MESH_ENABLED) &&
+ sta->plink_state == PLINK_BLOCKED) {
+ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+ " is blocked - drop Authentication frame",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_MESH */
} else {
#ifdef CONFIG_MESH
if (hapd->conf->mesh & MESH_ENABLED) {
/* if the mesh peer is not available, we don't do auth.
*/
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
- " not yet known - drop Authentiation frame",
+ " not yet known - drop Authentication frame",
MAC2STR(mgmt->sa));
/*
* Save a copy of the frame so that it can be processed
@@ -1095,19 +1138,23 @@
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
- if (vlan_id > 0) {
- if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
- "%d received from RADIUS server",
- vlan_id);
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
- sta->vlan_id = vlan_id;
+ if (vlan_id.notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_id.untagged,
+ vlan_id.tagged[0] ? "+" : "");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (sta->vlan_id)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
- }
hostapd_free_psk_list(sta->psk);
if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
@@ -1132,6 +1179,46 @@
else
ap_sta_no_session_timeout(hapd, sta);
+ /*
+ * If the driver supports full AP client state, add a station to the
+ * driver before sending authentication reply to make sure the driver
+ * has resources, and not to go through the entire authentication and
+ * association handshake, and fail it at the end.
+ *
+ * If this is not the first transaction, in a multi-step authentication
+ * algorithm, the station already exists in the driver
+ * (sta->added_unassoc = 1) so skip it.
+ *
+ * In mesh mode, the station was already added to the driver when the
+ * NEW_PEER_CANDIDATE event is received.
+ */
+ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+ !(hapd->conf->mesh & MESH_ENABLED) &&
+ !(sta->added_unassoc)) {
+ /*
+ * If a station that is already associated to the AP, is trying
+ * to authenticate again, remove the STA entry, in order to make
+ * sure the STA PS state gets cleared and configuration gets
+ * updated. To handle this, station's added_unassoc flag is
+ * cleared once the station has completed association.
+ */
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
+ WLAN_STA_AUTHORIZED);
+
+ if (hostapd_sta_add(hapd, sta->addr, 0, 0, 0, 0, 0,
+ NULL, NULL, sta->flags, 0, 0, 0)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "Could not add STA to kernel driver");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ sta->added_unassoc = 1;
+ }
+
switch (auth_alg) {
case WLAN_AUTH_OPEN:
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1205,8 +1292,15 @@
os_free(radius_cui);
hostapd_free_psk_list(psk);
- send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
- auth_transaction + 1, resp, resp_ies, resp_ies_len);
+ reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+ auth_transaction + 1, resp, resp_ies,
+ resp_ies_len);
+
+ if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+ reply_res != WLAN_STATUS_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
}
@@ -1620,6 +1714,18 @@
sta->mb_ies = NULL;
#endif /* CONFIG_FST */
+#ifdef CONFIG_MBO
+ mbo_ap_check_sta_assoc(hapd, sta, &elems);
+
+ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ wpa_printf(MSG_INFO,
+ "MBO: Reject WPA2 association without PMF");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_MBO */
+
return WLAN_STATUS_SUCCESS;
}
@@ -1646,9 +1752,65 @@
}
-static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- u16 status_code, int reassoc, const u8 *ies,
- size_t ies_len)
+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 to ensure 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
+ * does not 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 ACK frame for the (Re)Association Response frame
+ * is processed (TX status driver event).
+ */
+ if (hostapd_sta_add(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)
{
int send_len;
u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
@@ -1766,11 +1928,17 @@
p = hostapd_eid_p2p_manage(hapd, p);
#endif /* CONFIG_P2P_MANAGER */
+ p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+
send_len += p - reply->u.assoc_resp.variable;
- if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
+ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
}
@@ -1779,7 +1947,7 @@
int reassoc)
{
u16 capab_info, listen_interval, seq_ctrl, fc;
- u16 resp = WLAN_STATUS_SUCCESS;
+ u16 resp = WLAN_STATUS_SUCCESS, reply_res;
const u8 *pos;
int left, i;
struct sta_info *sta;
@@ -1846,6 +2014,12 @@
wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
"prior to authentication since it is using "
"over-the-DS FT", MAC2STR(mgmt->sa));
+
+ /*
+ * Mark station as authenticated, to avoid adding station
+ * entry in the driver as associated and not authenticated
+ */
+ sta->flags |= WLAN_STA_AUTH;
} else
#endif /* CONFIG_IEEE80211R */
if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
@@ -1889,6 +2063,13 @@
goto fail;
}
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+#endif /* CONFIG_MBO */
+
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -1973,7 +2154,39 @@
sta->timeout_next = STA_NULLFUNC;
fail:
- send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+ /*
+ * In case of a successful response, add the station to the driver.
+ * Otherwise, the kernel may ignore Data frames before we process the
+ * ACK frame (TX status). In case of a failure, this station will be
+ * removed.
+ *
+ * Note that this is not compliant with the IEEE 802.11 standard that
+ * states that a non-AP station should transition into the
+ * authenticated/associated state only after the station acknowledges
+ * the (Re)Association Response frame. However, still do this as:
+ *
+ * 1. In case the station does not acknowledge the (Re)Association
+ * Response frame, it will be removed.
+ * 2. Data frames will be dropped in the kernel until the station is
+ * set into authorized state, and there are no significant known
+ * issues with processing other non-Data Class 3 frames during this
+ * window.
+ */
+ 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);
+
+ /*
+ * Remove the station in case tranmission of a success response fails
+ * (the STA 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;
+ }
}
@@ -2015,6 +2228,7 @@
hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
ap_sta_ip6addr_del(hapd, sta);
hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
if (sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC) {
@@ -2391,16 +2605,10 @@
u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta;
- if (!ok) {
- hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_NOTICE,
- "did not acknowledge authentication response");
- return;
- }
-
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
- (unsigned long) len);
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
return;
}
@@ -2408,11 +2616,17 @@
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
- sta = ap_get_sta(hapd, mgmt->da);
- if (!sta) {
- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
- MAC2STR(mgmt->da));
- return;
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "did not acknowledge authentication response");
+ goto fail;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+ (unsigned long) len);
+ goto fail;
}
if (status_code == WLAN_STATUS_SUCCESS &&
@@ -2421,6 +2635,15 @@
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "authenticated");
sta->flags |= WLAN_STA_AUTH;
+ if (sta->added_unassoc)
+ hostapd_set_sta_flags(hapd, sta);
+ return;
+ }
+
+fail:
+ if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
}
}
@@ -2456,15 +2679,6 @@
u16 status;
struct sta_info *sta;
int new_assoc = 1;
- struct ieee80211_ht_capabilities ht_cap;
- struct ieee80211_vht_capabilities vht_cap;
-
- if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
- sizeof(mgmt->u.assoc_resp))) {
- wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
- reassoc, (unsigned long) len);
- return;
- }
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
@@ -2473,11 +2687,12 @@
return;
}
- 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;
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+ sizeof(mgmt->u.assoc_resp))) {
+ wpa_printf(MSG_INFO,
+ "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+ reassoc, (unsigned long) len);
+ hostapd_drv_sta_remove(hapd, sta->addr);
return;
}
@@ -2486,6 +2701,18 @@
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;
+ /* 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;
@@ -2520,38 +2747,6 @@
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 in case of reassociation back
- * to the same AP.
- */
- 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(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)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_NOTICE,
- "Could not add STA to kernel driver");
-
- ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_DISASSOC_AP_BUSY);
-
- return;
- }
-
if (sta->flags & WLAN_STA_WDS) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
@@ -2583,7 +2778,6 @@
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);
}
@@ -2673,7 +2867,7 @@
handle_assoc_cb(hapd, mgmt, len, 1, ok);
break;
case WLAN_FC_STYPE_PROBE_RESP:
- wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
+ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
break;
case WLAN_FC_STYPE_DEAUTH:
wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
@@ -2684,7 +2878,7 @@
handle_disassoc_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_ACTION:
- wpa_printf(MSG_DEBUG, "mgmt::action cb");
+ wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
break;
default:
wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);