Revert "Revert "[wpa_supplicant] cumilative patch from commit 3a..."
Revert submission 28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR
Reason for revert: Fixed the regression issue (ag/28389573)
Reverted changes: /q/submissionid:28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR
Bug: 329004037
Test: Turn ON/OFF SoftAp multiple times
Change-Id: Ibfff2a847be5678f1a6d77e28506a05936812a91
diff --git a/src/ap/acs.c b/src/ap/acs.c
index e3cfe1d..28b0ba7 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -245,6 +245,8 @@
ACS_BW40,
ACS_BW80,
ACS_BW160,
+ ACS_BW320_1,
+ ACS_BW320_2,
};
struct bw_item {
@@ -286,10 +288,20 @@
{ 6435, 6575, 111 }, { 6595, 6735, 143 },
{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
};
+static const struct bw_item bw_320_1[] = {
+ { 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
+ { -1, -1, -1 }
+};
+static const struct bw_item bw_320_2[] = {
+ { 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
+ { -1, -1, -1 }
+};
static const struct bw_item *bw_desc[] = {
[ACS_BW40] = bw_40,
[ACS_BW80] = bw_80,
[ACS_BW160] = bw_160,
+ [ACS_BW320_1] = bw_320_1,
+ [ACS_BW320_2] = bw_320_2,
};
@@ -768,6 +780,42 @@
#endif /* CONFIG_IEEE80211BE */
+static bool
+acs_usable_bw320_chan(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan, int *bw320_offset)
+{
+ const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
+ int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
+
+ *bw320_offset = 0;
+ switch (conf_bw320_offset) {
+ case 1:
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ break;
+ case 2:
+ if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ case 0:
+ default:
+ conf_bw320_offset = 0;
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ else if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ }
+
+ if (!*bw320_offset)
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
+ chan->chan, bw320_str[conf_bw320_offset]);
+
+ return *bw320_offset != 0;
+}
+
+
static void
acs_find_ideal_chan_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode,
@@ -779,14 +827,18 @@
struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
long double factor;
int i, j;
+ int bw320_offset = 0, ideal_bw320_offset = 0;
unsigned int k;
+ int secondary_channel = 1, freq_offset;
+
+ if (is_24ghz_mode(mode->mode))
+ secondary_channel = iface->conf->secondary_channel;
for (i = 0; i < mode->num_channels; i++) {
- double total_weight;
+ double total_weight = 0;
struct acs_bias *bias, tmp_bias;
- bool update_best = true;
- best = chan = &mode->channels[i];
+ chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
@@ -818,7 +870,7 @@
iface->conf->country[2] == 0x4f)
continue;
- if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
chan->chan, bw);
@@ -839,7 +891,8 @@
}
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be)) {
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_80MHZ &&
!acs_usable_bw_chan(chan, ACS_BW80)) {
@@ -859,13 +912,25 @@
}
}
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211be) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_320MHZ &&
+ !acs_usable_bw320_chan(iface, chan, &bw320_offset))
+ continue;
+ }
+
factor = 0;
- if (acs_usable_chan(chan))
+ best = NULL;
+ if (acs_usable_chan(chan)) {
factor = chan->interference_factor;
- total_weight = 1;
+ total_weight = 1;
+ best = chan;
+ }
for (j = 1; j < n_chans; j++) {
- adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ adj_chan = acs_find_chan(iface, chan->freq +
+ j * secondary_channel * 20);
if (!adj_chan)
break;
@@ -876,16 +941,14 @@
break;
}
- if (acs_usable_chan(adj_chan)) {
- factor += adj_chan->interference_factor;
- total_weight += 1;
- } else {
- update_best = false;
- }
+ if (!acs_usable_chan(adj_chan))
+ continue;
+
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
/* find the best channel in this segment */
- if (update_best &&
- adj_chan->interference_factor <
+ if (!best || adj_chan->interference_factor <
best->interference_factor)
best = adj_chan;
}
@@ -898,8 +961,9 @@
/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
* crowded primary channel if one was found in the segment */
- if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
- chan != best) {
+ if (iface->current_mode &&
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ best && chan != best) {
wpa_printf(MSG_DEBUG,
"ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
best->chan, chan->chan,
@@ -912,8 +976,9 @@
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
for (j = 0; j < n_chans; j++) {
+ freq_offset = j * 20 * secondary_channel;
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 5);
+ freq_offset - 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -921,7 +986,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 10);
+ freq_offset - 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -929,7 +994,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 5);
+ freq_offset + 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -937,7 +1002,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 10);
+ freq_offset + 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -946,6 +1011,9 @@
}
}
+ if (total_weight == 0)
+ continue;
+
factor /= total_weight;
bias = NULL;
@@ -983,6 +1051,7 @@
*ideal_factor = factor;
*ideal_chan = chan;
+ ideal_bw320_offset = bw320_offset;
#ifdef CONFIG_IEEE80211BE
if (iface->conf->ieee80211be)
@@ -993,9 +1062,13 @@
}
/* This channel would at least be usable */
- if (!(*rand_chan))
+ if (!(*rand_chan)) {
*rand_chan = chan;
+ ideal_bw320_offset = bw320_offset;
+ }
}
+
+ hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
}
@@ -1022,19 +1095,12 @@
goto bw_selected;
}
- /* TODO: HT40- support */
-
- if (iface->conf->ieee80211n &&
- iface->conf->secondary_channel == -1) {
- wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
- return NULL;
- }
-
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel)
n_chans = 2;
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_80MHZ:
n_chans = 4;
@@ -1042,6 +1108,9 @@
case CONF_OPER_CHWIDTH_160MHZ:
n_chans = 8;
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ n_chans = 16;
+ break;
default:
break;
}
@@ -1091,7 +1160,8 @@
acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
return;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+ wpa_printf(MSG_DEBUG,
+ "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
if (iface->freq == bw_desc[ACS_BW40][i].first)
@@ -1106,7 +1176,7 @@
{
int center;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
@@ -1125,11 +1195,28 @@
case CONF_OPER_CHWIDTH_160MHZ:
center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ switch (hostapd_get_bw320_offset(iface->conf)) {
+ case 1:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_1);
+ break;
+ case 2:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_2);
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "ACS: BW320 offset is not selected");
+ return;
+ }
+
+ break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
wpa_printf(MSG_INFO,
- "ACS: Only VHT20/40/80/160 is supported now");
+ "ACS: Only VHT20/40/80/160/320 is supported now");
return;
}
@@ -1192,7 +1279,8 @@
iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
acs_adjust_secondary(iface);
acs_adjust_center_freq(iface);
}
diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
index abe817c..6844311 100644
--- a/src/ap/airtime_policy.c
+++ b/src/ap/airtime_policy.c
@@ -232,7 +232,7 @@
struct airtime_sta_weight *wt;
wt = hapd->conf->airtime_weight_list;
- while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+ while (wt && !ether_addr_equal(wt->addr, sta))
wt = wt->next;
return wt ? wt->weight : hapd->conf->airtime_weight;
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 60d3566..445d963 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -165,6 +165,7 @@
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
+ bss->test_assoc_comeback_type = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_PASN
@@ -283,6 +284,10 @@
conf->he_6ghz_max_ampdu_len_exp = 7;
conf->he_6ghz_rx_ant_pat = 1;
conf->he_6ghz_tx_ant_pat = 1;
+ conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
+ conf->reg_def_cli_eirp_psd = -1;
+ conf->reg_sub_cli_eirp_psd = -1;
+ conf->reg_def_cli_eirp = -1;
#endif /* CONFIG_IEEE80211AX */
/* The third octet of the country string uses an ASCII space character
@@ -297,6 +302,8 @@
conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
#endif /* CONFIG_AIRTIME_POLICY */
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
return conf;
}
@@ -695,6 +702,33 @@
}
+#ifdef CONFIG_IEEE80211R_AP
+
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+ r0kh = conf->r0kh_list;
+ conf->r0kh_list = NULL;
+ while (r0kh) {
+ r0kh_prev = r0kh;
+ r0kh = r0kh->next;
+ os_free(r0kh_prev);
+ }
+
+ r1kh = conf->r1kh_list;
+ conf->r1kh_list = NULL;
+ while (r1kh) {
+ r1kh_prev = r1kh;
+ r1kh = r1kh->next;
+ os_free(r1kh_prev);
+ }
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
{
struct anqp_element *elem;
@@ -827,26 +861,9 @@
os_free(conf->time_zone);
#ifdef CONFIG_IEEE80211R_AP
- {
- struct ft_remote_r0kh *r0kh, *r0kh_prev;
- struct ft_remote_r1kh *r1kh, *r1kh_prev;
-
- r0kh = conf->r0kh_list;
- conf->r0kh_list = NULL;
- while (r0kh) {
- r0kh_prev = r0kh;
- r0kh = r0kh->next;
- os_free(r0kh_prev);
- }
-
- r1kh = conf->r1kh_list;
- conf->r1kh_list = NULL;
- while (r1kh) {
- r1kh_prev = r1kh;
- r1kh = r1kh->next;
- os_free(r1kh_prev);
- }
- }
+ hostapd_config_clear_rxkhs(conf);
+ os_free(conf->rxkh_file);
+ conf->rxkh_file = NULL;
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_WPS
@@ -944,6 +961,8 @@
wpabuf_free(conf->rsnxe_override_ft);
wpabuf_free(conf->gtk_rsc_override);
wpabuf_free(conf->igtk_rsc_override);
+ wpabuf_free(conf->eapol_m1_elements);
+ wpabuf_free(conf->eapol_m3_elements);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
@@ -1131,10 +1150,9 @@
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
(psk->group ||
- (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (addr && ether_addr_equal(psk->addr, addr)) ||
(!addr && p2p_dev_addr &&
- os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
- 0))) {
+ ether_addr_equal(psk->p2p_dev_addr, p2p_dev_addr)))) {
if (vlan_id)
*vlan_id = psk->vlan_id;
return psk->psk;
@@ -1558,6 +1576,10 @@
"Cannot set ieee80211be without ieee80211ax");
return -1;
}
+
+ if (full_config)
+ hostapd_set_and_check_bw320_offset(conf,
+ conf->eht_bw320_offset);
#endif /* CONFIG_IEEE80211BE */
if (full_config && conf->mbssid && !conf->ieee80211ax) {
@@ -1750,7 +1772,7 @@
int i = 0;
while (i < *num) {
- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal((*acl)[i].addr, addr)) {
os_remove_in_array(*acl, *num, sizeof(**acl), i);
(*num)--;
} else {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 99a6d18..69db16d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -405,6 +405,7 @@
int ft_over_ds;
int ft_psk_generate_local;
int r1_max_key_lifetime;
+ char *rxkh_file;
#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -704,6 +705,14 @@
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
+ int test_assoc_comeback_type;
+
+#ifdef CONFIG_IEEE80211BE
+ u16 eht_oper_puncturing_override;
+#endif /* CONFIG_IEEE80211BE */
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -948,6 +957,14 @@
/* The AP's MLD MAC address within the AP MLD */
u8 mld_addr[ETH_ALEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * If set indicate the AP as disabled in the RNR element included in the
+ * other APs in the AP MLD.
+ */
+ bool mld_indicate_disabled;
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
};
@@ -1139,6 +1156,19 @@
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
u8 he_6ghz_reg_pwr_type;
+
+ int reg_def_cli_eirp_psd;
+ int reg_sub_cli_eirp_psd;
+
+ /*
+ * This value should be used when regulatory client EIRP PSD values
+ * advertised by an AP that is an SP AP or an indoor SP AP are
+ * insufficient to ensure that regulatory client limits on total EIRP
+ * are always met for all transmission bandwidths within the bandwidth
+ * of the AP’s BSS.
+ */
+ int reg_def_cli_eirp;
+
bool require_he;
#endif /* CONFIG_IEEE80211AX */
@@ -1175,6 +1205,8 @@
struct eht_phy_capabilities_info eht_phy_capab;
u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
u8 punct_acs_threshold;
+ u8 eht_default_pe_duration;
+ u8 eht_bw320_offset;
#endif /* CONFIG_IEEE80211BE */
/* EHT enable/disable config from CHAN_SWITCH */
@@ -1242,7 +1274,8 @@
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+ if (is_6ghz_op_class(conf->op_class) &&
+ center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
oper_centr_freq_seg0_idx +=
conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
#endif /* CONFIG_IEEE80211BE */
@@ -1274,6 +1307,43 @@
conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
}
+static inline u8
+hostapd_get_bw320_offset(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ)
+ return conf->eht_bw320_offset;
+#endif /* CONFIG_IEEE80211BE */
+ return 0;
+}
+
+static inline void
+hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ u8 bw320_offset)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ op_class_to_ch_width(conf->op_class) == CONF_OPER_CHWIDTH_320MHZ) {
+ if (conf->channel) {
+ /* If the channel is set, then calculate bw320_offset
+ * by center frequency segment 0.
+ */
+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+ conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1;
+ } else {
+ /* If the channel is not set, bw320_offset indicates
+ * preferred offset of 320 MHz.
+ */
+ conf->eht_bw320_offset = bw320_offset;
+ }
+ } else {
+ conf->eht_bw320_offset = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void);
@@ -1282,6 +1352,7 @@
void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8f9cc5b..60d66e4 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -265,9 +265,35 @@
}
+static bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return true;
+#endif /* CONFIG_IEEE80211BE */
+
+ return false;
+}
+
+
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized)
{
+ /*
+ * The WPA_STA_AUTHORIZED flag is relevant only for the MLD station and
+ * not to the link stations (as the authorization is done between the
+ * MLD peers). Thus, do not propagate the change to the driver for the
+ * link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
if (authorized) {
return hostapd_sta_set_flags(hapd, sta->addr,
hostapd_sta_flags_to_drv(
@@ -285,11 +311,24 @@
{
int set_flags, total_flags, flags_and, flags_or;
total_flags = hostapd_sta_flags_to_drv(sta->flags);
- set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
- if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
- sta->auth_alg == WLAN_AUTH_FT) &&
- sta->flags & WLAN_STA_AUTHORIZED)
- set_flags |= WPA_STA_AUTHORIZED;
+ set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP |
+ WPA_STA_AUTHORIZED;
+
+ /*
+ * All the station flags other than WPA_STA_SHORT_PREAMBLE are relevant
+ * only for the MLD station and not to the link stations (as these flags
+ * are related to the MLD state and not the link state). As for the
+ * WPA_STA_SHORT_PREAMBLE, since the station is an EHT station, it must
+ * support short preamble. Thus, do not propagate the change to the
+ * driver for the link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
flags_or = total_flags & set_flags;
flags_and = total_flags | ~set_flags;
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
@@ -793,15 +832,21 @@
const u8 *addr, int reason)
{
int link_id = -1;
+ const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
link_id = hapd->mld_link_id;
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+ }
#endif /* CONFIG_IEEE80211BE */
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_deauth(hapd->drv_priv, own_addr, addr,
reason, link_id);
}
@@ -809,9 +854,20 @@
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
const u8 *addr, int reason)
{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_disassoc(hapd->drv_priv, own_addr, addr,
reason);
}
@@ -826,22 +882,22 @@
}
-int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
- unsigned int wait, const u8 *dst, const u8 *data,
- size_t len)
+static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *data, size_t len, bool addr3_ap)
{
+ const u8 *own_addr = hapd->own_addr;
const u8 *bssid;
const u8 wildcard_bssid[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
+ struct sta_info *sta;
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
return 0;
bssid = hapd->own_addr;
- if (!is_multicast_ether_addr(dst) &&
+ if (!addr3_ap && !is_multicast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
- struct sta_info *sta;
-
/*
* Public Action frames to a STA that is not a member of the BSS
* shall use wildcard BSSID value.
@@ -849,7 +905,7 @@
sta = ap_get_sta(hapd, dst);
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
bssid = wildcard_bssid;
- } else if (is_broadcast_ether_addr(dst) &&
+ } else if (!addr3_ap && is_broadcast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
/*
* The only current use case of Public Action frames with
@@ -858,9 +914,27 @@
* so have to use the wildcard BSSID value.
*/
bssid = wildcard_bssid;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->conf->mld_ap) {
+ sta = ap_get_sta(hapd, dst);
+
+ if (ap_sta_is_mld(hapd, sta)) {
+ own_addr = hapd->mld_addr;
+ bssid = own_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
+
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, bssid, data, len, 0);
+ own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+{
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false);
}
@@ -869,11 +943,7 @@
unsigned int wait, const u8 *dst,
const u8 *data, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->send_action == NULL)
- return 0;
- return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, hapd->own_addr, data,
- len, 0);
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
}
@@ -1025,6 +1095,12 @@
os_memset(¶ms, 0, sizeof(params));
params.hw_mode = hapd->iface->conf->hw_mode;
+ params.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
+ !hapd->conf->disable_11be)
+ params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
/*
* If no chanlist config parameter is provided, include all enabled
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 20be7f8..13facab 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -55,7 +55,7 @@
struct ap_info *s;
s = iface->ap_hash[STA_HASH(ap)];
- while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+ while (s != NULL && !ether_addr_equal(s->addr, ap))
s = s->hnext;
return s;
}
@@ -100,13 +100,13 @@
s = iface->ap_hash[STA_HASH(ap->addr)];
if (s == NULL) return;
- if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(s->addr, ap->addr)) {
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
return;
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, ap->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 1b5cea9..e50f0a0 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -239,12 +239,10 @@
continue; /* can use same entry */
}
- if (start && prev) {
+ if (start && prev)
pos = hostapd_eid_country_add(hapd, pos, end,
chan_spacing,
start, prev);
- start = NULL;
- }
/* Start new group */
start = prev = chan;
@@ -565,19 +563,78 @@
}
-static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *req,
- int is_p2p, size_t *resp_len,
- const u8 *known_bss, u8 known_bss_len)
+static size_t he_elem_len(struct hostapd_data *hapd)
{
+ size_t len = 0;
+
+#ifdef CONFIG_IEEE80211AX
+ if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ return len;
+
+ len += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+ 3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+ /* An additional Transmit Power Envelope element for
+ * subordinate client */
+ if (he_reg_is_indoor(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+
+ /* An additional Transmit Power Envelope element for
+ * default client with unit interpretation of regulatory
+ * client EIRP */
+ if (hapd->iconf->reg_def_cli_eirp != -1 &&
+ he_reg_is_sp(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ return len;
+}
+
+
+struct probe_resp_params {
+ const struct ieee80211_mgmt *req;
+ bool is_p2p;
+
+ /* Generated IEs will be included inside an ML element */
+ bool is_ml_sta_info;
+ struct hostapd_data *mld_ap;
+ struct mld_info *mld_info;
+
struct ieee80211_mgmt *resp;
- u8 *pos, *epos, *csa_pos;
- size_t buflen;
+ size_t resp_len;
+ u8 *csa_pos;
+ u8 *ecsa_pos;
+ const u8 *known_bss;
+ u8 known_bss_len;
- hapd = hostapd_mbssid_get_tx_bss(hapd);
+#ifdef CONFIG_IEEE80211AX
+ u8 *cca_pos;
+#endif /* CONFIG_IEEE80211AX */
+};
-#define MAX_PROBERESP_LEN 768
- buflen = MAX_PROBERESP_LEN;
+
+static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!params)
+ return;
+ ap_sta_free_sta_profile(params->mld_info);
+ os_free(params->mld_info);
+ params->mld_info = NULL;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ size_t buflen = 0;
+
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
@@ -597,23 +654,7 @@
2 + sizeof(struct ieee80211_vht_operation);
}
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- buflen += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ buflen += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -622,58 +663,45 @@
if (hapd->iconf->punct_bitmap)
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
- /*
- * TODO: Multi-Link element has variable length and can be
- * long based on the common info and number of per
- * station profiles. For now use 256.
- */
- if (hapd->conf->mld_ap)
- buflen += 256;
+ if (!params->is_ml_sta_info && hapd->conf->mld_ap) {
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ buflen += hostapd_eid_eht_ml_beacon_len(
+ ml_elem_ap, params->mld_info, !!params->mld_ap);
+ }
}
#endif /* CONFIG_IEEE80211BE */
buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
- known_bss, known_bss_len, NULL);
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ params->known_bss,
+ params->known_bss_len, NULL);
+ if (!params->is_ml_sta_info)
+ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
- resp = os_zalloc(buflen);
- if (resp == NULL)
- return NULL;
+ return buflen;
+}
- epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
- resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_RESP);
- /* Unicast the response to all requests on bands other than 6 GHz. For
- * the 6 GHz, unicast is used only if the actual SSID is not included in
- * the Beacon frames. Otherwise, broadcast response is used per IEEE
- * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
- * the Probe Response frame template for the unsolicited (i.e., not as
- * a response to a specific request) case. */
- if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
- hapd->conf->ignore_broadcast_ssid))
- os_memcpy(resp->da, req->sa, ETH_ALEN);
- else
- os_memset(resp->da, 0xff, ETH_ALEN);
+static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ u8 *pos, size_t len)
+{
+ u8 *csa_pos;
+ u8 *epos;
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ epos = pos + len;
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
- resp->u.probe_resp.beacon_int =
- host_to_le16(hapd->iconf->beacon_int);
-
- /* hardware or low-level driver will setup seq_ctrl and timestamp */
- resp->u.probe_resp.capab_info =
- host_to_le16(hostapd_own_capab_info(hapd));
-
- pos = resp->u.probe_resp.variable;
- *pos++ = WLAN_EID_SSID;
- *pos++ = hapd->conf->ssid.ssid_len;
- os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
- pos += hapd->conf->ssid.ssid_len;
+ if (!params->is_ml_sta_info) {
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ }
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
@@ -686,11 +714,18 @@
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
- /* CSA IE */
- csa_pos = hostapd_eid_csa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_pos;
+ /*
+ * CSA IE
+ * TODO: This should be included inside the ML sta profile
+ */
+ if (!params->is_ml_sta_info) {
+ csa_pos = hostapd_eid_csa(hapd, pos);
+ if (csa_pos != pos)
+ params->csa_pos = csa_pos - 1;
+ else
+ params->csa_pos = NULL;
+ pos = csa_pos;
+ }
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -701,16 +736,23 @@
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
- NULL, known_bss, known_bss_len, NULL, NULL,
- NULL, 0);
+ NULL, params->known_bss, params->known_bss_len,
+ NULL, NULL, NULL, 0);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
- /* eCSA IE */
- csa_pos = hostapd_eid_ecsa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_pos;
+ /*
+ * eCSA IE
+ * TODO: This should be included inside the ML sta profile
+ */
+ if (!params->is_ml_sta_info) {
+ csa_pos = hostapd_eid_ecsa(hapd, pos);
+ if (csa_pos != pos)
+ params->ecsa_pos = csa_pos - 1;
+ else
+ params->ecsa_pos = NULL;
+ pos = csa_pos;
+ }
pos = hostapd_eid_supported_op_classes(hapd, pos);
pos = hostapd_eid_ht_capabilities(hapd, pos);
@@ -720,7 +762,7 @@
* when a list of known BSSes is included in the Probe Request frame. */
pos = hostapd_eid_ext_capab(hapd, pos,
hapd->iconf->mbssid >= MBSSID_ENABLED &&
- !known_bss_len);
+ !params->known_bss_len);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
@@ -754,7 +796,8 @@
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ if (!params->is_ml_sta_info)
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
@@ -768,7 +811,9 @@
/* BSS Color Change Announcement element */
cca_pos = hostapd_eid_cca(hapd, pos);
if (cca_pos != pos)
- hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+ params->cca_pos = cca_pos - 2;
+ else
+ params->cca_pos = NULL;
pos = cca_pos;
pos = hostapd_eid_spatial_reuse(hapd, pos);
@@ -779,8 +824,14 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
- if (hapd->conf->mld_ap)
- pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true);
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ if (ml_elem_ap->conf->mld_ap)
+ pos = hostapd_eid_eht_ml_beacon(
+ ml_elem_ap, params->mld_info,
+ pos, !!params->mld_ap);
+
pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_eht_operation(hapd, pos);
}
@@ -807,7 +858,7 @@
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
- if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+ if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p &&
hapd->p2p_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
wpabuf_len(hapd->p2p_probe_resp_ie));
@@ -824,9 +875,9 @@
pos = hostapd_eid_hs20_indication(hapd, pos);
#endif /* CONFIG_HS20 */
- pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_mbo(hapd, pos, epos - pos);
+ pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
+ pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -834,11 +885,171 @@
pos += wpabuf_len(hapd->conf->vendor_elements);
}
- *resp_len = pos - (u8 *) resp;
- return (u8 *) resp;
+ return pos;
}
+static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ u8 *pos;
+ size_t buflen;
+
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
+#define MAX_PROBERESP_LEN 768
+ buflen = MAX_PROBERESP_LEN;
+ buflen += hostapd_probe_resp_elems_len(hapd, params);
+ params->resp = os_zalloc(buflen);
+ if (!params->resp) {
+ params->resp_len = 0;
+ return;
+ }
+
+ params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_PROBE_RESP);
+ /* Unicast the response to all requests on bands other than 6 GHz. For
+ * the 6 GHz, unicast is used only if the actual SSID is not included in
+ * the Beacon frames. Otherwise, broadcast response is used per IEEE
+ * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+ * the Probe Response frame template for the unsolicited (i.e., not as
+ * a response to a specific request) case. */
+ if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+ hapd->conf->ignore_broadcast_ssid))
+ os_memcpy(params->resp->da, params->req->sa, ETH_ALEN);
+ else
+ os_memset(params->resp->da, 0xff, ETH_ALEN);
+ os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN);
+
+ os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN);
+ params->resp->u.probe_resp.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ params->resp->u.probe_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+
+ pos = hostapd_probe_resp_fill_elems(hapd, params,
+ params->resp->u.probe_resp.variable,
+ buflen);
+
+ params->resp_len = pos - (u8 *) params->resp;
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ const struct ieee80211_mgmt *mgmt,
+ int mld_id, u16 links)
+{
+ struct probe_resp_params sta_info_params;
+ struct hostapd_data *link;
+ unsigned int probed_mld_id, i, j;
+
+ params->mld_ap = NULL;
+ params->mld_info = os_zalloc(sizeof(*params->mld_info));
+ if (!params->mld_info)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Got ML probe request with AP MLD ID %d for links %04x",
+ mld_id, links);
+
+ /*
+ * We want to include the AP MLD ID in the response if it was
+ * included in the request.
+ */
+ probed_mld_id = mld_id != -1 ? mld_id : hapd->conf->mld_id;
+
+ for_each_mld_link(link, i, j, hapd->iface->interfaces,
+ probed_mld_id) {
+ struct mld_link_info *link_info;
+ size_t buflen;
+ u8 mld_link_id = link->mld_link_id;
+ u8 *epos;
+ u8 buf[EHT_ML_MAX_STA_PROF_LEN];
+
+ /*
+ * Set mld_ap iff the ML probe request explicitly
+ * requested a specific MLD ID. In that case, the targeted
+ * AP may have been a nontransmitted BSSID on the same
+ * interface.
+ */
+ if (mld_id != -1 && link->iface == hapd->iface)
+ params->mld_ap = link;
+
+ /* Never duplicate main Probe Response frame body */
+ if (link == hapd)
+ continue;
+
+ /* Only include requested links */
+ if (!(BIT(mld_link_id) & links))
+ continue;
+
+ link_info = ¶ms->mld_info->links[mld_link_id];
+
+ sta_info_params.req = params->req;
+ sta_info_params.is_p2p = false;
+ sta_info_params.is_ml_sta_info = true;
+ sta_info_params.mld_ap = NULL;
+ sta_info_params.mld_info = NULL;
+
+ buflen = MAX_PROBERESP_LEN;
+ buflen += hostapd_probe_resp_elems_len(link, &sta_info_params);
+
+ if (buflen > EHT_ML_MAX_STA_PROF_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Not including link %d in ML probe response (%zu bytes is too long)",
+ mld_link_id, buflen);
+ goto fail;
+ }
+
+ /*
+ * NOTE: This does not properly handle inheritance and
+ * various other things.
+ */
+ link_info->valid = true;
+ epos = buf;
+
+ /* Capabilities is the only fixed parameter */
+ WPA_PUT_LE16(epos, hostapd_own_capab_info(hapd));
+ epos += 2;
+
+ epos = hostapd_probe_resp_fill_elems(
+ link, &sta_info_params, epos,
+ EHT_ML_MAX_STA_PROF_LEN - 2);
+ link_info->resp_sta_profile_len = epos - buf;
+ os_free(link_info->resp_sta_profile);
+ link_info->resp_sta_profile = os_memdup(
+ buf, link_info->resp_sta_profile_len);
+ if (!link_info->resp_sta_profile)
+ link_info->resp_sta_profile_len = 0;
+ os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe response includes link sta info for %d: %u bytes (estimate %zu)",
+ mld_link_id, link_info->resp_sta_profile_len,
+ buflen);
+ }
+
+ if (mld_id != -1 && !params->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No nontransmitted BSSID for MLD ID %d",
+ mld_id);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ hostapd_free_probe_resp_params(params);
+ params->mld_ap = NULL;
+ params->mld_info = NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
enum ssid_match_result {
NO_SSID_MATCH,
EXACT_SSID_MATCH,
@@ -953,7 +1164,7 @@
struct hostapd_sta_info *info;
dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
- if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, info->addr))
return info;
return NULL;
@@ -1037,21 +1248,109 @@
#endif /* CONFIG_TAXONOMY */
+#ifdef CONFIG_IEEE80211BE
+static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
+ int *mld_id, u16 *links)
+{
+ u16 ml_control;
+ const struct element *sub;
+ const u8 *pos;
+ size_t len;
+
+ *mld_id = -1;
+ *links = 0xffff;
+
+ if (ml_len < sizeof(struct ieee80211_eht_ml))
+ return false;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_PROBE_REQ) {
+ wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req");
+ return false;
+ }
+
+ if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: ML probe req too short");
+ return false;
+ }
+
+ pos = ml->variable;
+ len = pos[0];
+ if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe request with invalid length");
+ return false;
+ }
+
+ if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) {
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req too short for MLD ID");
+ return false;
+ }
+
+ *mld_id = pos[1];
+ }
+ pos += len;
+
+ /* Parse subelements (if there are any) */
+ len = ml_len - len - sizeof(struct ieee80211_eht_ml);
+ for_each_element_id(sub, 0, pos, len) {
+ const struct ieee80211_eht_per_sta_profile *sta;
+ u16 sta_control;
+
+ if (*links == 0xffff)
+ *links = 0;
+
+ if (sub->datalen <
+ sizeof(struct ieee80211_eht_per_sta_profile)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req %d too short for sta profile",
+ sub->datalen);
+ return false;
+ }
+
+ sta = (struct ieee80211_eht_per_sta_profile *) sub->data;
+
+ /*
+ * Extract the link ID, do not return whether a complete or
+ * partial profile was requested.
+ */
+ sta_control = le_to_host16(sta->sta_control);
+ *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
+ }
+
+ if (!for_each_element_completed(sub, pos, len)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req sub-elements parsing error");
+ return false;
+ }
+
+ return true;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal)
{
- u8 *resp;
struct ieee802_11_elems elems;
const u8 *ie;
size_t ie_len;
- size_t i, resp_len;
+ size_t i;
int noack;
enum ssid_match_result res;
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
struct radius_sta rad_info;
+ struct probe_resp_params params;
+#ifdef CONFIG_IEEE80211BE
+ int mld_id;
+ u16 links;
+#endif /* CONFIG_IEEE80211BE */
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
@@ -1217,7 +1516,7 @@
else
hessid = elems.interworking + 1 + 2;
if (!is_broadcast_ether_addr(hessid) &&
- os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ !ether_addr_equal(hessid, hapd->conf->hessid)) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for mismatching HESSID " MACSTR
" ignored",
@@ -1283,10 +1582,28 @@
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
" signal=%d", MAC2STR(mgmt->sa), ssi_signal);
- resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
- &resp_len, elems.mbssid_known_bss,
- elems.mbssid_known_bss_len);
- if (resp == NULL)
+ os_memset(¶ms, 0, sizeof(params));
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && elems.probe_req_mle &&
+ parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle,
+ elems.probe_req_mle_len, &mld_id, &links)) {
+ hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt,
+ mld_id, links);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ params.req = mgmt;
+ params.is_p2p = !!elems.p2p;
+ params.known_bss = elems.mbssid_known_bss;
+ params.known_bss_len = elems.mbssid_known_bss_len;
+ params.is_ml_sta_info = false;
+
+ hostapd_gen_probe_resp(hapd, ¶ms);
+
+ hostapd_free_probe_resp_params(¶ms);
+
+ if (!params.resp)
return;
/*
@@ -1298,24 +1615,23 @@
csa_offs_len = 0;
if (hapd->csa_in_progress) {
- if (hapd->cs_c_off_proberesp)
+ if (params.csa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_proberesp;
+ params.csa_pos - (u8 *) params.resp;
- if (hapd->cs_c_off_ecsa_proberesp)
+ if (params.ecsa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_ecsa_proberesp;
+ params.ecsa_pos - (u8 *) params.resp;
}
- ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
- resp_len, noack,
+ ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
if (ret < 0)
wpa_printf(MSG_INFO, "handle_probe_req: send failed");
- os_free(resp);
+ os_free(params.resp);
wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
@@ -1326,6 +1642,8 @@
static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
size_t *resp_len)
{
+ struct probe_resp_params params;
+
/* check probe response offloading caps and print warnings */
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
return NULL;
@@ -1355,7 +1673,32 @@
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+ os_memset(¶ms, 0, sizeof(params));
+ params.req = NULL;
+ params.is_p2p = false;
+ params.known_bss = NULL;
+ params.known_bss_len = 0;
+ params.is_ml_sta_info = false;
+ params.mld_ap = NULL;
+ params.mld_info = NULL;
+
+ hostapd_gen_probe_resp(hapd, ¶ms);
+ *resp_len = params.resp_len;
+ if (!params.resp)
+ return NULL;
+
+ /* TODO: Avoid passing these through struct hostapd_data */
+ if (params.csa_pos)
+ hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp;
+ if (params.ecsa_pos)
+ hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos -
+ (u8 *) params.resp;
+#ifdef CONFIG_IEEE80211AX
+ if (params.cca_pos)
+ hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp;
+#endif /* CONFIG_IEEE80211AX */
+
+ return (u8 *) params.resp;
}
#endif /* NEED_AP_MLME */
@@ -1366,15 +1709,26 @@
static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
+ struct probe_resp_params probe_params;
+
if (!is_6ghz_op_class(hapd->iconf->op_class))
return NULL;
params->unsol_bcast_probe_resp_interval =
hapd->conf->unsol_bcast_probe_resp_interval;
- return hostapd_gen_probe_resp(hapd, NULL, 0,
- ¶ms->unsol_bcast_probe_resp_tmpl_len,
- NULL, 0);
+ os_memset(&probe_params, 0, sizeof(probe_params));
+ probe_params.req = NULL;
+ probe_params.is_p2p = false;
+ probe_params.known_bss = NULL;
+ probe_params.known_bss_len = 0;
+ probe_params.is_ml_sta_info = false;
+ probe_params.mld_ap = NULL;
+ probe_params.mld_info = NULL;
+
+ hostapd_gen_probe_resp(hapd, &probe_params);
+ params->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
+ return (u8 *) probe_params.resp;
}
#endif /* CONFIG_IEEE80211AX */
@@ -1606,14 +1960,9 @@
buf_len = pos - buf;
total_len += buf_len;
-#ifdef CONFIG_IEEE80211AX
- /* Transmit Power Envelope element(s) */
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- total_len += 4;
- if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
- total_len += 4;
- }
-#endif /* CONFIG_IEEE80211AX */
+ /* he_elem_len() may return too large a value for FD frame, but that is
+ * fine here since this is used as the maximum length of the buffer. */
+ total_len += he_elem_len(hapd);
head = os_zalloc(total_len);
if (!head)
@@ -1763,23 +2112,7 @@
}
#endif /* CONFIG_IEEE80211AC */
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- tail_len += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ tail_len += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -1966,8 +2299,8 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
- true);
+ tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
+ tailpos, false);
tailpos = hostapd_eid_eht_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_eht_operation(hapd, tailpos);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index a6fcb7e..5378671 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -830,6 +830,17 @@
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+
+ if (is_6ghz_op_class(iface->conf->op_class) &&
+ hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_320MHZ) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "eht_bw320_offset=%d\n",
+ iface->conf->eht_bw320_offset);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -1094,7 +1105,7 @@
return -1;
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
- pmkid, expiration, akmp);
+ pmkid, expiration, akmp, NULL);
}
@@ -1315,6 +1326,8 @@
req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
if (os_strstr(cmd, " disassoc_imminent=1"))
req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ if (os_strstr(cmd, " link_removal_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
#ifdef CONFIG_MBO
pos = os_strstr(cmd, "mbo=");
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 9a5d3c8..5e4c810 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -188,7 +188,7 @@
* If it's not allowed to use the first channel as primary, decline the
* whole channel range. */
if (!chan_pri_allowed(first_chan)) {
- wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
+ wpa_printf(MSG_DEBUG, "DFS: primary channel not allowed");
return 0;
}
@@ -551,6 +551,8 @@
if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
return NULL;
chan_idx = _rand % num_available_chandefs;
+ wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ chan_idx, num_available_chandefs);
dfs_find_channel(iface, &chan, chan_idx, type);
if (!chan) {
wpa_printf(MSG_DEBUG, "DFS: no random channel found");
@@ -983,6 +985,11 @@
os_memset(&csa_settings, 0, sizeof(csa_settings));
csa_settings.cs_count = 5;
csa_settings.block_tx = 1;
+ csa_settings.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+ csa_settings.link_id = iface->bss[0]->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_MESH
if (iface->mconf)
ieee80211_mode = IEEE80211_MODE_MESH;
@@ -1044,7 +1051,7 @@
}
-static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
{
int sec = 0;
enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
@@ -1119,7 +1126,7 @@
hostapd_set_oper_centr_freq_seg1_idx(
iface->conf, iface->radar_background.centr_freq_seg1_idx);
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return hostapd_dfs_request_channel_switch(
iface, iface->conf->channel, iface->freq,
@@ -1183,7 +1190,7 @@
}
} else if (hostapd_dfs_is_background_event(iface, freq)) {
iface->radar_background.cac_started = 0;
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
return 0;
@@ -1317,7 +1324,7 @@
* Just select a new random channel according to the
* regulations for monitoring.
*/
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return 0;
}
@@ -1479,7 +1486,7 @@
} else if (dfs_use_radar_background(iface) &&
iface->radar_background.channel == -1) {
/* Reset radar background chain if disabled */
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
return 0;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 7a8ea4e..3f89bc2 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -539,8 +539,15 @@
return;
}
- if (hapd->dpp_auth_ok_on_ack)
+ if (hapd->dpp_auth_ok_on_ack) {
hostapd_dpp_auth_success(hapd, 1);
+ if (!hapd->dpp_auth) {
+ /* The authentication session could have been removed in
+ * some error cases, e.g., when starting GAS client and
+ * failing to send the initial request. */
+ return;
+ }
+ }
if (!is_broadcast_ether_addr(dst) && !ok) {
wpa_printf(MSG_DEBUG,
@@ -1413,7 +1420,7 @@
}
if (!is_zero_ether_addr(auth->peer_mac_addr) &&
- os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1463,7 +1470,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1572,7 +1579,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1858,7 +1865,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2133,7 +2140,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -2907,7 +2914,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -3073,7 +3080,7 @@
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
- os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, auth->peer_mac_addr)) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
data_len) == 0) {
@@ -3094,6 +3101,13 @@
* exchange. */
dpp_notify_auth_success(hapd->dpp_auth, 1);
hapd->dpp_auth_ok_on_ack = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_hexdump(MSG_DEBUG,
@@ -3948,11 +3962,25 @@
ifaces->dpp_pb_time.usec = 0;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
+ hapd->dpp_pkex_bi = NULL;
os_free(hapd->dpp_pkex_auth_cmd);
hapd->dpp_pkex_auth_cmd = NULL;
if (ifaces->dpp_pb_bi) {
char id[20];
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (h->dpp_pkex_bi == ifaces->dpp_pb_bi)
+ h->dpp_pkex_bi = NULL;
+ }
+ }
os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
dpp_bootstrap_remove(ifaces->dpp, id);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 98794c2..533cc54 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -42,6 +42,7 @@
#include "dpp_hostapd.h"
#include "fils_hlp.h"
#include "neighbor_db.h"
+#include "nan_usd_ap.h"
#ifdef CONFIG_FILS
@@ -52,6 +53,7 @@
struct ieee802_11_elems elems;
u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
int new_assoc;
+ bool updated;
wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
__func__, MAC2STR(sta->addr));
@@ -76,11 +78,13 @@
sta->fils_pending_assoc_is_reassoc,
WLAN_STATUS_SUCCESS,
buf, p - buf);
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
@@ -158,7 +162,7 @@
return -1;
}
- mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlebuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlebuf) {
wpa_printf(MSG_ERROR,
"MLO: Basic Multi-Link element not found in (Re)Association Response frame");
@@ -263,6 +267,7 @@
#ifdef CONFIG_OWE
struct hostapd_iface *iface = hapd->iface;
#endif /* CONFIG_OWE */
+ bool updated = false;
if (addr == NULL) {
/*
@@ -279,7 +284,7 @@
if (is_multicast_ether_addr(addr) ||
is_zero_ether_addr(addr) ||
- os_memcmp(addr, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(addr, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -358,7 +363,7 @@
int i, num_valid_links = 0;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, addr, ETH_ALEN);
info->links[link_id].valid = true;
@@ -509,7 +514,7 @@
return -1;
}
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
@@ -845,18 +850,30 @@
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
/* Keep compiler silent about unused variables */
if (status) {
}
#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
+ !!reassoc, WLAN_STATUS_SUCCESS,
+ true)) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -1164,6 +1181,8 @@
hostapd_set_oper_chwidth(hapd->iconf, chwidth);
hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+ /* Auto-detect new bw320_offset */
+ hostapd_set_and_check_bw320_offset(hapd->iconf, 0);
#ifdef CONFIG_IEEE80211BE
hapd->iconf->punct_bitmap = punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
@@ -1270,6 +1289,18 @@
int err = 0;
struct hostapd_channel_data *pri_chan;
+#ifdef CONFIG_IEEE80211BE
+ if (acs_res->link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(hapd, acs_res->link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for EVENT_ACS_CHANNEL_SELECTED link_id=%d",
+ acs_res->link_id);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (hapd->iconf->channel) {
wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
hapd->iconf->channel);
@@ -1576,6 +1607,7 @@
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
if (plen >= 2 + 4 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
@@ -1591,6 +1623,23 @@
return;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] == NAN_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.variable;
+ end = drv_mgmt->frame + drv_mgmt->frame_len;
+ pos++;
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
+ pos, end - pos);
+ return;
+ }
+#endif /* CONFIG_NAN_USD */
}
#endif /* NEED_AP_MLME */
@@ -1628,7 +1677,7 @@
return HAPD_BROADCAST;
for (i = 0; i < iface->num_bss; i++) {
- if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
return iface->bss[i];
}
@@ -1682,7 +1731,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
+ ether_addr_equal(hapd->mld_addr, bssid))
is_mld = true;
#endif /* CONFIG_IEEE80211BE */
@@ -1754,8 +1803,7 @@
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
} else if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, get_hdr_bssid(hdr, len),
- ETH_ALEN) == 0) {
+ ether_addr_equal(hapd->mld_addr, get_hdr_bssid(hdr, len))) {
/* AP MLD address match - use hapd pointer as-is */
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -1803,14 +1851,15 @@
static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
- const u8 *src)
+ const u8 *src, bool rsn)
{
struct sta_info *sta;
unsigned int j;
for (j = 0; j < iface->num_bss; j++) {
sta = ap_get_sta(iface->bss[j], src);
- if (sta && sta->flags & WLAN_STA_ASSOC)
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+ (!rsn || sta->wpa_sm))
return iface->bss[j];
}
@@ -1818,6 +1867,40 @@
}
+#ifdef CONFIG_IEEE80211BE
+static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
+{
+ struct hostapd_data *hapd = *p_hapd;
+ unsigned int i;
+
+ /* Search for STA on other MLO BSSs */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h =
+ hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (!hconf->mld_ap ||
+ hconf->mld_id != hapd->conf->mld_id)
+ continue;
+
+ h_hapd = hostapd_find_by_sta(h, src, false);
+ if (h_hapd) {
+ struct sta_info *sta = ap_get_sta(h_hapd, src);
+
+ if (sta && sta->mld_info.mld_sta &&
+ sta->mld_assoc_link_id != h_hapd->mld_link_id)
+ continue;
+ *p_hapd = h_hapd;
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
const u8 *data, size_t data_len,
enum frame_encryption encrypted,
@@ -1830,36 +1913,24 @@
struct hostapd_data *h_hapd;
hapd = switch_link_hapd(hapd, link_id);
- h_hapd = hostapd_find_by_sta(hapd->iface, src);
+ h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
if (!h_hapd)
- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
+ h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+ true);
+ if (!h_hapd)
+ h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
+ if (!h_hapd)
+ h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
+ false);
if (h_hapd)
hapd = h_hapd;
} else if (hapd->conf->mld_ap) {
- unsigned int i;
-
- /* Search for STA on other MLO BSSs */
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *h =
- hapd->iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
- struct hostapd_bss_config *hconf = h_hapd->conf;
-
- if (!hconf->mld_ap ||
- hconf->mld_id != hapd->conf->mld_id)
- continue;
-
- h_hapd = hostapd_find_by_sta(h, src);
- if (h_hapd) {
- hapd = h_hapd;
- break;
- }
- }
+ search_mld_sta(&hapd, src);
} else {
- hapd = hostapd_find_by_sta(hapd->iface, src);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false);
}
#else /* CONFIG_IEEE80211BE */
- hapd = hostapd_find_by_sta(hapd->iface, src);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false);
#endif /* CONFIG_IEEE80211BE */
if (!hapd) {
@@ -2164,8 +2235,8 @@
struct mld_info *info = &sta->mld_info;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
- sta->mld_assoc_link_id = link_id;;
+ ap_sta_set_mld(sta, true);
+ sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, peer, ETH_ALEN);
info->links[link_id].valid = true;
os_memcpy(info->links[link_id].local_addr, hapd->own_addr,
@@ -2364,6 +2435,18 @@
case EVENT_CH_SWITCH:
if (!data)
break;
+#ifdef CONFIG_IEEE80211BE
+ if (data->ch_switch.link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(
+ hapd, data->ch_switch.link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link (ID %d) BSS for EVENT_CH_SWITCH/EVENT_CH_SWITCH_STARTED",
+ data->ch_switch.link_id);
+ break;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset,
@@ -2390,26 +2473,31 @@
case EVENT_DFS_RADAR_DETECTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
break;
case EVENT_DFS_PRE_CAC_EXPIRED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_ABORTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
break;
case EVENT_DFS_NOP_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
break;
case EVENT_CHANNEL_LIST_CHANGED:
@@ -2423,6 +2511,7 @@
case EVENT_DFS_CAC_STARTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
break;
#endif /* NEED_AP_MLME */
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index d64fb8c..a34b5ba 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -546,7 +546,7 @@
" src=" MACSTR " len=%u)",
MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
(unsigned int) len);
- if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta->addr, pos + ETH_ALEN)) {
wpa_printf(MSG_DEBUG,
"FILS: Ignore HLP request with unexpected source address"
MACSTR, MAC2STR(pos + ETH_ALEN));
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
index 3d94407..a471c79 100644
--- a/src/ap/gas_query_ap.c
+++ b/src/ap/gas_query_ap.c
@@ -185,7 +185,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(q->addr, addr) &&
q->dialog_token == dialog_token)
return q;
}
@@ -223,7 +223,7 @@
wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
" ok=%d query=%p dialog_token=%u dur=%d ms",
MAC2STR(dst), ok, query, query->dialog_token, dur);
- if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(dst, query->addr)) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
@@ -618,7 +618,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(dst, q->addr) &&
dialog_token == q->dialog_token)
return 0;
}
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 236381f..ddbcabc 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -35,6 +35,7 @@
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "dpp_hostapd.h"
+#include "nan_usd_ap.h"
#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
@@ -413,6 +414,61 @@
}
+#ifdef CONFIG_IEEE80211BE
+#ifdef CONFIG_TESTING_OPTIONS
+
+#define TU_TO_USEC(_val) ((_val) * 1024)
+
+static void hostapd_link_remove_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+
+ if (hapd->eht_mld_link_removal_count == 0)
+ return;
+ hapd->eht_mld_link_removal_count--;
+
+ wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id,
+ hapd->eht_mld_link_removal_count);
+
+ ieee802_11_set_beacon(hapd);
+
+ if (!hapd->eht_mld_link_removal_count) {
+ hostapd_disable_iface(hapd->iface);
+ return;
+ }
+
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
+}
+
+
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
+{
+ if (!hapd->conf->mld_ap)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id, count);
+
+ hapd->eht_mld_link_removal_count = count;
+ hapd->eht_mld_bss_param_change++;
+
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
+
+ ieee802_11_set_beacon(hapd);
+ return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
os_free(hapd->probereq_cb);
@@ -441,6 +497,24 @@
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
if (!hapd->mld_first_bss) {
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (hapd == h)
+ continue;
+ if (h->radius == hapd->radius)
+ h->radius = NULL;
+ if (h->radius_das == hapd->radius_das)
+ h->radius_das = NULL;
+ }
+ }
radius_client_deinit(hapd->radius);
radius_das_deinit(hapd->radius_das);
}
@@ -455,6 +529,9 @@
gas_query_ap_deinit(hapd->gas);
hapd->gas = NULL;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ hostapd_nan_usd_deinit(hapd);
+#endif /* CONFIG_NAN_USD */
authsrv_deinit(hapd);
@@ -502,7 +579,9 @@
hapd->setup_complete_cb = NULL;
#endif /* CONFIG_MESH */
+#ifndef CONFIG_NO_RRM
hostapd_clean_rrm(hapd);
+#endif /* CONFIG_NO_RRM */
fils_hlp_deinit(hapd);
#ifdef CONFIG_OCV
@@ -525,6 +604,12 @@
#ifdef CONFIG_IEEE80211AX
eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_IEEE80211BE
+ eloop_cancel_timeout(hostapd_link_remove_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211BE */
+#endif /* CONFIG_TESTING_OPTIONS */
+
#endif /* CONFIG_IEEE80211AX */
}
@@ -1435,6 +1520,11 @@
return -1;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (hostapd_nan_usd_init(hapd) < 0)
+ return -1;
+#endif /* CONFIG_NAN_USD */
+
if (authsrv_init(hapd) < 0)
return -1;
@@ -3117,6 +3207,31 @@
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd_iface->bss[0]->conf->mld_ap &&
+ !hapd_iface->bss[0]->mld_first_bss) {
+ /* Do not allow mld_first_bss disabling before other BSSs */
+ for (j = 0; j < hapd_iface->interfaces->count; ++j) {
+ struct hostapd_iface *h_iface =
+ hapd_iface->interfaces->iface[j];
+ struct hostapd_data *h_hapd = h_iface->bss[0];
+ struct hostapd_bss_config *h_conf = h_hapd->conf;
+
+ if (!h_conf->mld_ap ||
+ h_conf->mld_id !=
+ hapd_iface->bss[0]->conf->mld_id ||
+ h_iface == hapd_iface)
+ continue;
+
+ if (h_iface->state != HAPD_IFACE_DISABLED) {
+ wpa_printf(MSG_INFO,
+ "Do not allow disable mld_first_bss first");
+ return -1;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
driver = hapd_iface->bss[0]->driver;
drv_priv = hapd_iface->bss[0]->drv_priv;
@@ -3536,7 +3651,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
sta->mld_assoc_link_id != hapd->mld_link_id)
return;
#endif /* CONFIG_IEEE80211BE */
@@ -3768,7 +3883,7 @@
struct hostapd_freq_params *old_params)
{
int channel;
- u8 seg0, seg1;
+ u8 seg0 = 0, seg1 = 0;
struct hostapd_hw_modes *mode;
if (!params->channel) {
@@ -3844,10 +3959,14 @@
conf->ieee80211n = params->ht_enabled;
conf->ieee80211ac = params->vht_enabled;
conf->secondary_channel = params->sec_channel_offset;
- ieee80211_freq_to_chan(params->center_freq1,
- &seg0);
- ieee80211_freq_to_chan(params->center_freq2,
- &seg1);
+ if (params->center_freq1 &&
+ ieee80211_freq_to_chan(params->center_freq1, &seg0) ==
+ NUM_HOSTAPD_MODES)
+ return -1;
+ if (params->center_freq2 &&
+ ieee80211_freq_to_chan(params->center_freq2,
+ &seg1) == NUM_HOSTAPD_MODES)
+ return -1;
hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
@@ -3945,6 +4064,11 @@
settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+ settings->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ settings->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
return 0;
}
@@ -4040,13 +4164,17 @@
bw = CONF_OPER_CHWIDTH_USE_HT;
break;
case 80:
- if (freq_params->center_freq2)
+ if (freq_params->center_freq2) {
bw = CONF_OPER_CHWIDTH_80P80MHZ;
- else
+ iface->conf->vht_capab |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ } else {
bw = CONF_OPER_CHWIDTH_80MHZ;
+ }
break;
case 160:
bw = CONF_OPER_CHWIDTH_160MHZ;
+ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
break;
case 320:
bw = CONF_OPER_CHWIDTH_320MHZ;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 7f703be..bcf980f 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -469,6 +469,17 @@
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+
+#ifdef CONFIG_IEEE80211BE
+ u8 eht_mld_bss_param_change;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 eht_mld_link_removal_count;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_NAN_USD
+ struct nan_de *nan_de;
+#endif /* CONFIG_NAN_USD */
};
@@ -771,5 +782,25 @@
int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id);
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+
+#ifdef CONFIG_IEEE80211BE
+#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+ for (_iface_idx = 0; \
+ _iface_idx < (_ifaces)->count; \
+ _iface_idx++) \
+ for (_bss_idx = 0; \
+ _bss_idx < \
+ (_ifaces)->iface[_iface_idx]->num_bss; \
+ _bss_idx++) \
+ for (_link = \
+ (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
+ _link && _link->conf->mld_ap && \
+ _link->conf->mld_id == _mld_id; \
+ _link = NULL)
+#else /* CONFIG_IEEE80211BE */
+#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+ if (false)
+#endif /* CONFIG_IEEE80211BE */
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 9edbb5a..596f2f0 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -1001,7 +1001,7 @@
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
- int err;
+ int err, err2;
if (!iface->current_mode)
return 0;
@@ -1044,15 +1044,15 @@
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_freq = iface->freq + 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+ err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_freq = iface->freq - 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+ err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 1f39107..8b8c1f0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -56,6 +56,7 @@
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#include "comeback_token.h"
+#include "nan_usd_ap.h"
#include "pasn/pasn_common.h"
@@ -407,7 +408,7 @@
* the MLD MAC address. Thus, use the MLD address instead of translating
* the addresses.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
sa = hapd->mld_addr;
ml_resp = hostapd_ml_auth_resp(hapd);
@@ -556,7 +557,7 @@
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
(!sta ||
- os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0))
+ !ether_addr_equal(pw->peer_addr, sta->addr)))
continue;
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
@@ -608,7 +609,7 @@
const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
own_addr = hapd->mld_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -877,7 +878,7 @@
params.status = status;
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
params.bssid =
sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -902,23 +903,27 @@
" to VLAN ID %d",
MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
- vlan_desc.notempty = 1;
- vlan_desc.untagged = sta->sae->tmp->vlan_id;
- if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
- wpa_printf(MSG_INFO,
- "Invalid VLAN ID %d in sae_password",
- sta->sae->tmp->vlan_id);
- return;
- }
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = sta->sae->tmp->vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO,
+ "Invalid VLAN ID %d in sae_password",
+ sta->sae->tmp->vlan_id);
+ return;
+ }
- if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
- ap_sta_bind_vlan(hapd, sta) < 0) {
- wpa_printf(MSG_INFO,
- "Failed to assign VLAN ID %d from sae_password to "
- MACSTR, sta->sae->tmp->vlan_id,
- MAC2STR(sta->addr));
- return;
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from sae_password to "
+ MACSTR, sta->sae->tmp->vlan_id,
+ MAC2STR(sta->addr));
+ return;
+ }
+ } else {
+ sta->vlan_id = sta->sae->tmp->vlan_id;
}
}
#endif /* CONFIG_NO_VLAN */
@@ -1273,7 +1278,8 @@
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
resp = status_code;
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
goto remove_sta;
@@ -1281,7 +1287,8 @@
if (hapd->conf->sae_commit_override && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
wpabuf_len(hapd->conf->sae_commit_override),
@@ -1552,7 +1559,8 @@
data = wpabuf_alloc_copy(pos, 2);
sae_sme_send_external_auth_status(hapd, sta, resp);
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-sae");
@@ -1664,7 +1672,7 @@
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
- if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(mgmt->sa, mgmt2->sa) &&
mgmt->u.auth.auth_transaction ==
mgmt2->u.auth.auth_transaction) {
wpa_printf(MSG_DEBUG,
@@ -1695,7 +1703,7 @@
dl_list_for_each(q, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt = (const struct ieee80211_mgmt *) q->msg;
- if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, mgmt->sa))
return 1;
}
@@ -2021,7 +2029,7 @@
}
os_memcpy(ie_buf, ie, ielen);
- if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid, true) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2149,7 +2157,8 @@
pmk, pmk_len,
sta->fils_erp_pmkid,
session_timeout,
- wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+ wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ NULL) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
}
@@ -2520,8 +2529,8 @@
FILS_SESSION_LEN);
os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
+ true);
if (!fils_wd) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
@@ -2612,7 +2621,7 @@
return -1;
}
- if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(entry->own_addr, own_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: own addr " MACSTR " and PTKSA entry own addr "
MACSTR " differ",
@@ -2639,8 +2648,10 @@
struct pasn_data *pasn = sta->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
+#ifdef CONFIG_FILS
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
+#endif /* CONFIG_FILS */
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
@@ -2690,8 +2701,8 @@
return;
}
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len, true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
return;
@@ -2906,7 +2917,7 @@
goto fail;
}
- if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2914,8 +2925,8 @@
}
if (mld_sta &&
- (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
- os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+ (ether_addr_equal(sa, hapd->own_addr) ||
+ ether_addr_equal(sa, hapd->mld_addr))) {
wpa_printf(MSG_INFO,
"Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
@@ -3068,12 +3079,13 @@
#ifdef CONFIG_IEEE80211BE
if (auth_transaction == 1) {
+ ap_sta_free_sta_profile(&sta->mld_info);
os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
if (mld_sta) {
u8 link_id = hapd->mld_link_id;
- sta->mld_info.mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
/*
@@ -3236,7 +3248,7 @@
* the MLD MAC address. It is the responsibility of the driver to
* handle the translations.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
dst = sta->addr;
bssid = hapd->mld_addr;
}
@@ -3281,7 +3293,7 @@
/* Do not assign an AID that is in use on any of the affiliated links
* when finding an AID for a non-AP MLD. */
- if (hapd->conf->mld_ap) {
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
int j;
for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
@@ -3655,7 +3667,7 @@
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
- sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
return WLAN_STATUS_SUCCESS;
}
@@ -3727,7 +3739,7 @@
goto end;
}
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
sta->mld_assoc_link_id, &sta->mld_info);
#endif /* CONFIG_IEEE80211BE */
@@ -4009,7 +4021,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (info->mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
wpa_auth_set_ml_info(sta->wpa_sm,
@@ -4309,22 +4321,23 @@
#ifdef CONFIG_IEEE80211BE
-static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
- u16 status_code,
- u8 *buf, size_t buflen)
+static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ struct mld_link_info *link)
{
+ u8 buf[EHT_ML_MAX_STA_PROF_LEN];
u8 *p = buf;
+ size_t buflen = sizeof(buf);
/* Capability Info */
WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
p += 2;
/* Status Code */
- WPA_PUT_LE16(p, status_code);
+ WPA_PUT_LE16(p, link->status);
p += 2;
- if (status_code != WLAN_STATUS_SUCCESS)
- return p - buf;
+ if (link->status != WLAN_STATUS_SUCCESS)
+ goto out;
/* AID is not included */
p = hostapd_eid_supp_rates(hapd, p);
@@ -4362,20 +4375,24 @@
p += wpabuf_len(hapd->conf->assocresp_elements);
}
- return p - buf;
+out:
+ os_free(link->resp_sta_profile);
+ link->resp_sta_profile = os_memdup(buf, p - buf);
+ link->resp_sta_profile_len = link->resp_sta_profile ? p - buf : 0;
}
-static void ieee80211_ml_process_link(struct hostapd_data *hapd,
- struct sta_info *origin_sta,
- struct mld_link_info *link,
- const u8 *ies, size_t ies_len,
- bool reassoc)
+static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ struct sta_info *origin_sta,
+ struct mld_link_info *link,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, bool offload)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf = NULL;
struct sta_info *sta = NULL;
u16 status = WLAN_STATUS_SUCCESS;
+ int i;
wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
hapd->mld_link_id, MAC2STR(link->peer_addr));
@@ -4401,7 +4418,7 @@
goto out;
}
- mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlbuf)
goto out;
@@ -4421,25 +4438,33 @@
goto out;
}
- sta->mld_info.mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ struct mld_link_info *li = &sta->mld_info.links[i];
- /*
- * Get the AID from the station on which the association was performed,
- * and mark it as used.
- */
- sta->aid = origin_sta->aid;
- if (sta->aid == 0) {
- wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto out;
+ li->resp_sta_profile = NULL;
+ li->resp_sta_profile_len = 0;
}
- hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
- sta->listen_interval = origin_sta->listen_interval;
- if (update_ht_state(hapd, sta) > 0)
- ieee802_11_update_beacons(hapd->iface);
+
+ if (!offload) {
+ /*
+ * Get the AID from the station on which the association was
+ * performed, and mark it as used.
+ */
+ sta->aid = origin_sta->aid;
+ if (sta->aid == 0) {
+ wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+ hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+ sta->listen_interval = origin_sta->listen_interval;
+ if (update_ht_state(hapd, sta) > 0)
+ ieee802_11_update_beacons(hapd->iface);
+ }
/* RSN Authenticator should always be the one on the original station */
wpa_auth_sta_deinit(sta->wpa_sm);
@@ -4465,20 +4490,23 @@
/* TODO: What other processing is required? */
- if (add_associated_sta(hapd, sta, reassoc))
+ if (!offload && add_associated_sta(hapd, sta, reassoc))
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
out:
wpabuf_free(mlbuf);
link->status = status;
- wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
- if (sta && status != WLAN_STATUS_SUCCESS)
- ap_free_sta(hapd, sta);
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+ if (status != WLAN_STATUS_SUCCESS) {
+ if (sta)
+ ap_free_sta(hapd, sta);
+ return -1;
+ }
+
+ return 0;
}
@@ -4497,16 +4525,17 @@
#endif /* CONFIG_IEEE80211BE */
-static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *ies, size_t ies_len,
- bool reassoc)
+int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, int tx_link_status,
+ bool offload)
{
#ifdef CONFIG_IEEE80211BE
unsigned int i, j;
if (!hostapd_is_mld_ap(hapd))
- return;
+ return 0;
/*
* This is not really needed, but make the interaction with the RSN
@@ -4536,22 +4565,29 @@
break;
}
- if (!iface || j == hapd->iface->interfaces->count) {
+ if (!iface || j == hapd->iface->interfaces->count ||
+ TEST_FAIL()) {
wpa_printf(MSG_DEBUG,
"MLD: No link match for link_id=%u", i);
link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(
- hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
+ } else if (tx_link_status != WLAN_STATUS_SUCCESS) {
+ /* TX link rejected the connection */
+ link->status = WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED;
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
} else {
- ieee80211_ml_process_link(iface->bss[0], sta, link,
- ies, ies_len, reassoc);
+ if (ieee80211_ml_process_link(iface->bss[0], sta, link,
+ ies, ies_len, reassoc,
+ offload))
+ return -1;
}
}
#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
}
@@ -4589,7 +4625,7 @@
bool mld_link_sta = false;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
@@ -4700,7 +4736,7 @@
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
const u8 *ies, size_t ies_len, int rssi,
- int omit_rsnxe)
+ int omit_rsnxe, bool allow_mld_addr_trans)
{
int send_len;
u8 *buf;
@@ -4750,7 +4786,7 @@
* Once a non-AP MLD is added to the driver, the addressing should use
* MLD MAC address.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
sa = hapd->mld_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -4893,7 +4929,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+ p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_eht_operation(hapd, p);
}
@@ -5116,7 +5152,8 @@
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len, 0, 0);
+ sta->fils_pending_assoc_req_len, 0, 0,
+ true);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -5151,6 +5188,48 @@
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool reassoc,
+ struct hostapd_data **assoc_hapd)
+{
+ struct sta_info *sta;
+ struct ieee802_11_elems elems;
+ u8 mld_addr[ETH_ALEN];
+ const u8 *pos;
+
+ if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
+ return NULL;
+
+ if (reassoc) {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req);
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req);
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return NULL;
+
+ if (hostapd_process_ml_assoc_req_addr(hapd, elems.basic_mle,
+ elems.basic_mle_len,
+ mld_addr))
+ return NULL;
+
+ sta = ap_get_sta(hapd, mld_addr);
+ if (!sta)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "MLD: assoc: mld=" MACSTR ", link=" MACSTR,
+ MAC2STR(mld_addr), MAC2STR(mgmt->sa));
+
+ return hostapd_ml_get_assoc_sta(hapd, sta, assoc_hapd);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc, int rssi)
@@ -5167,6 +5246,7 @@
#endif /* CONFIG_FILS */
int omit_rsnxe = 0;
bool set_beacon = false;
+ bool mld_addrs_not_translated = false;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -5224,6 +5304,28 @@
}
sta = ap_get_sta(hapd, mgmt->sa);
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * It is possible that the association frame is from an associated
+ * non-AP MLD station, that tries to re-associate using different link
+ * addresses. In such a case, try to find the station based on the AP
+ * MLD MAC address.
+ */
+ if (!sta) {
+ struct hostapd_data *assoc_hapd;
+
+ sta = handle_mlo_translate(hapd, mgmt, len, reassoc,
+ &assoc_hapd);
+ if (sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Switching to assoc hapd/station");
+ hapd = assoc_hapd;
+ mld_addrs_not_translated = true;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
@@ -5500,8 +5602,9 @@
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS)
- hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+ if (sta)
+ hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
+ resp, false);
if (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
@@ -5545,8 +5648,12 @@
#endif /* CONFIG_FILS */
if (resp >= 0)
- reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
- pos, left, rssi, omit_rsnxe);
+ reply_res = send_assoc_resp(hapd,
+ mld_addrs_not_translated ?
+ NULL : sta,
+ mgmt->sa, resp, reassoc,
+ pos, left, rssi, omit_rsnxe,
+ !mld_addrs_not_translated);
os_free(tmp);
/*
@@ -5637,44 +5744,6 @@
}
-#ifdef CONFIG_IEEE80211BE
-static struct sta_info *
-hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- struct hostapd_data **assoc_hapd)
-{
- struct hostapd_data *other_hapd = NULL;
- struct sta_info *tmp_sta;
-
- *assoc_hapd = hapd;
-
- /* The station is the one on which the association was performed */
- if (sta->mld_assoc_link_id == hapd->mld_link_id)
- return sta;
-
- other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
- if (!other_hapd) {
- wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
- sta->mld_assoc_link_id);
- return sta;
- }
-
- /*
- * Iterate over the stations and find the one with the matching link ID
- * and association ID.
- */
- for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
- if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
- tmp_sta->aid == sta->aid) {
- *assoc_hapd = other_hapd;
- return tmp_sta;
- }
- }
-
- return sta;
-}
-#endif /* CONFIG_IEEE80211BE */
-
-
static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
struct sta_info *sta,
const struct ieee80211_mgmt *mgmt,
@@ -5693,6 +5762,8 @@
* the information about all the other links.
*/
assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
@@ -5964,6 +6035,25 @@
return 1;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ len >= IEEE80211_HDRLEN + 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ NAN_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.variable;
+ end = ((const u8 *) mgmt) + len;
+ pos++;
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
+ pos, end - pos);
+ return 1;
+ }
+#endif /* CONFIG_NAN_USD */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len, freq);
@@ -5982,9 +6072,11 @@
return 1;
}
break;
+#ifndef CONFIG_NO_RRM
case WLAN_ACTION_RADIO_MEASUREMENT:
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
+#endif /* CONFIG_NO_RRM */
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6069,6 +6161,10 @@
int ret = 0;
unsigned int freq;
int ssi_signal = fi ? fi->ssi_signal : 0;
+#ifdef CONFIG_NAN_USD
+ static const u8 nan_network_id[ETH_ALEN] =
+ { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
+#endif /* CONFIG_NAN_USD */
if (len < 24)
return 0;
@@ -6084,7 +6180,7 @@
if (is_multicast_ether_addr(mgmt->sa) ||
is_zero_ether_addr(mgmt->sa) ||
- os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mgmt->sa, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -6110,9 +6206,9 @@
#endif /* CONFIG_MESH */
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
MAC2STR(mgmt->bssid));
return 0;
@@ -6133,9 +6229,12 @@
stype != WLAN_FC_STYPE_ACTION) &&
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_NAN_USD
+ !ether_addr_equal(mgmt->da, nan_network_id) &&
+#endif /* CONFIG_NAN_USD */
+ !ether_addr_equal(mgmt->da, hapd->own_addr)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
@@ -6285,6 +6384,8 @@
struct mld_link_info *link,
bool ok)
{
+ bool updated = false;
+
if (!ok) {
hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -6305,9 +6406,11 @@
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
/*
* TODOs:
@@ -6340,7 +6443,7 @@
struct hostapd_data *tmp_hapd =
hapd->iface->interfaces->iface[i]->bss[0];
- if (tmp_hapd->conf->mld_ap ||
+ if (!tmp_hapd->conf->mld_ap ||
hapd->conf->mld_id != tmp_hapd->conf->mld_id)
continue;
@@ -6379,7 +6482,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
hapd->mld_link_id != sta->mld_assoc_link_id) {
/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
wpa_printf(MSG_DEBUG,
@@ -6589,7 +6692,9 @@
size_t len, int ok)
{
struct sta_info *sta;
+#ifndef CONFIG_NO_RRM
const struct rrm_measurement_report_element *report;
+#endif /* CONFIG_NO_RRM */
#ifdef CONFIG_DPP
if (len >= IEEE80211_HDRLEN + 6 &&
@@ -6643,6 +6748,7 @@
}
#endif /* CONFIG_HS20 */
+#ifndef CONFIG_NO_RRM
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
@@ -6653,6 +6759,7 @@
report->len >= 3 &&
report->type == MEASURE_TYPE_BEACON)
hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+#endif /* CONFIG_NO_RRM */
}
@@ -6864,7 +6971,7 @@
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
- os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(src, hapd->own_addr)) {
/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
* silently. */
return;
@@ -6963,21 +7070,35 @@
tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
/* Default Transmit Power Envelope for Global Operating Class */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+ if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+ tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+ else
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+
eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
REG_DEFAULT_CLIENT, tx_pwr);
/* Indoor Access Point must include an additional TPE for
* subordinate devices */
- if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
/* TODO: Extract PSD limits from channel data */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+ if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+ tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+ else
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
eid = hostapd_add_tpe_info(eid, tx_pwr_count,
tx_pwr_intrpn,
REG_SUBORDINATE_CLIENT,
tx_pwr);
}
+ if (iconf->reg_def_cli_eirp != -1 &&
+ he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+ eid = hostapd_add_tpe_info(
+ eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+ REG_DEFAULT_CLIENT,
+ hapd->iconf->reg_def_cli_eirp);
+
return eid;
}
#endif /* CONFIG_IEEE80211AX */
@@ -7392,7 +7513,7 @@
/* BSS parameters */
*eid++ = nr->bss_parameters;
/* 20 MHz PSD */
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
len += RNR_TBTT_INFO_LEN;
*size_offset = (eid - size_offset) - 1;
}
@@ -7402,17 +7523,98 @@
}
+static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t i, u8 *tbtt_count, size_t *len,
+ u8 **pos)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *bss = iface->bss[i];
+ u8 bss_param = 0;
+ bool ap_mld = false;
+ u8 *eid = *pos;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!bss || !bss->conf || !bss->started ||
+ bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
+ return false;
+
+ if (skip_profiles
+ && i >= skip_profiles->start && i < skip_profiles->end)
+ return false;
+
+ if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ return true;
+
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+ os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+ eid += 4;
+ if (bss->conf->ssid.short_ssid == reporting_hapd->conf->ssid.short_ssid)
+ bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+ if (iface->conf->mbssid != MBSSID_DISABLED && iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (bss == hostapd_mbssid_get_tx_bss(hapd))
+ bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ bss->conf->unsol_bcast_probe_resp_interval)
+ bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+ bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
+
+ if (!ap_mld) {
+ *len += RNR_TBTT_INFO_LEN;
+ } else {
+#ifdef CONFIG_IEEE80211BE
+ u8 param_ch = hapd->eht_mld_bss_param_change;
+
+ if (reporting_hapd->conf->mld_ap &&
+ bss->conf->mld_id == reporting_hapd->conf->mld_id)
+ *eid++ = 0;
+ else
+ *eid++ = hapd->conf->mld_id;
+
+ *eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
+ *eid = (param_ch >> 4) & 0xF;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->mld_indicate_disabled)
+ *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+#endif /* CONFIG_TESTING_OPTIONS */
+ eid++;
+
+ *len += RNR_TBTT_INFO_MLD_LEN;
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ (*tbtt_count)++;
+ *pos = eid;
+
+ return false;
+}
+
+
static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
u8 *eid, size_t *current_len,
struct mbssid_ie_profiles *skip_profiles)
{
- struct hostapd_data *bss;
struct hostapd_iface *iface = hapd->iface;
size_t i, start = 0;
size_t len = *current_len;
u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
- u8 tbtt_count = 0, op_class, channel, bss_param;
+ u8 tbtt_count = 0, op_class, channel;
bool ap_mld = false;
#ifdef CONFIG_IEEE80211BE
@@ -7447,62 +7649,10 @@
len += RNR_TBTT_HEADER_LEN;
for (i = start; i < iface->num_bss; i++) {
- bss_param = 0;
- bss = iface->bss[i];
- if (!bss || !bss->conf || !bss->started)
- continue;
-
- if (bss == reporting_hapd ||
- bss->conf->ignore_broadcast_ssid)
- continue;
-
- if (skip_profiles &&
- i >= skip_profiles->start && i < skip_profiles->end)
- continue;
-
- if (len + RNR_TBTT_INFO_LEN > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
+ skip_profiles, i,
+ &tbtt_count, &len, &eid))
break;
-
- *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- os_memcpy(eid, bss->own_addr, ETH_ALEN);
- eid += ETH_ALEN;
- os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
- eid += 4;
- if (bss->conf->ssid.short_ssid ==
- reporting_hapd->conf->ssid.short_ssid)
- bss_param |= RNR_BSS_PARAM_SAME_SSID;
-
- if (iface->conf->mbssid != MBSSID_DISABLED &&
- iface->num_bss > 1) {
- bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
- if (bss == hostapd_mbssid_get_tx_bss(hapd))
- bss_param |=
- RNR_BSS_PARAM_TRANSMITTED_BSSID;
- }
-
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
- bss->conf->unsol_bcast_probe_resp_interval)
- bss_param |=
- RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
-
- bss_param |= RNR_BSS_PARAM_CO_LOCATED;
-
- *eid++ = bss_param;
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
-
- if (!ap_mld) {
- len += RNR_TBTT_INFO_LEN;
- } else {
-#ifdef CONFIG_IEEE80211BE
- *eid++ = hapd->conf->mld_id;
- *eid++ = hapd->mld_link_id | (1 << 4);
- *eid++ = 0;
- len += RNR_TBTT_INFO_MLD_LEN;
-#endif /* CONFIG_IEEE80211BE */
- }
-
- tbtt_count += 1;
}
start = i;
@@ -7579,7 +7729,7 @@
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
¤t_len, NULL);
break;
@@ -7610,7 +7760,18 @@
size_t known_bss_len)
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- size_t len = 3, i;
+ size_t len, i;
+
+ /* Element ID: 1 octet
+ * Length: 1 octet
+ * MaxBSSID Indicator: 1 octet
+ * Optional Subelements: vatiable
+ *
+ * Total fixed length: 3 octets
+ *
+ * 1 octet in len for the MaxBSSID Indicator field.
+ */
+ len = 1;
for (i = *bss_index; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
@@ -7663,7 +7824,9 @@
}
*bss_index = i;
- return len;
+
+ /* Add 2 octets to get the full size of the element */
+ return len + 2;
}
@@ -7792,13 +7955,14 @@
eid += 2 + rsnx[1];
}
}
+ /* List of Element ID values in increasing order */
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
non_inherit_ie[ie_count++] = WLAN_EID_RSN;
- if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
- non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
if (hapd->conf->xrates_supported &&
!bss->conf->xrates_supported)
non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
if (ie_count) {
*eid++ = WLAN_EID_EXTENSION;
*eid++ = 2 + ie_count + 1;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 4b58fee..5fd380a 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -23,6 +23,7 @@
struct sae_pk;
struct sae_pt;
struct sae_password_entry;
+struct mld_info;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -88,8 +89,14 @@
const struct ieee80211_eht_capabilities *src,
struct ieee80211_eht_capabilities *dest,
size_t len);
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+ struct mld_info *mld_info,
+ u8 *eid, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid);
+size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ struct mld_info *info,
+ bool include_mld_id);
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
@@ -97,6 +104,9 @@
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta);
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
@@ -245,5 +255,13 @@
struct sta_info *sta, const char *rx_id,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt, const struct sae_pk **s_pk);
+struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd);
+int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, int tx_link_status,
+ bool offload);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 4277d82..e723ae7 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -84,7 +84,7 @@
os_get_reltime(&now);
for (entry = hapd->acl_cache; entry; entry = entry->next) {
- if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->addr, addr))
continue;
if (os_reltime_expired(&now, &entry->timestamp,
@@ -281,7 +281,7 @@
query = hapd->acl_queries;
while (query) {
- if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(query->addr, addr)) {
/* pending query in RADIUS retransmit queue;
* do not generate a new one */
return HOSTAPD_ACL_PENDING;
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index c1ccda4..840fc20 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -44,12 +44,13 @@
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- u8 he_oper_chwidth, const u8 *he_phy_cap,
+ int he_oper_chwidth, const u8 *he_phy_cap,
const u8 *eht_phy_cap)
{
u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
bool band24, band5, band6;
u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+ u8 cap_chwidth;
switch (he_oper_chwidth) {
case CONF_OPER_CHWIDTH_80P80MHZ:
@@ -66,7 +67,11 @@
break;
}
- he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ if (he_oper_chwidth != -1)
+ he_phy_cap_chwidth &= cap_chwidth;
+ else
+ he_phy_cap_chwidth = cap_chwidth;
band24 = mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G ||
@@ -199,12 +204,33 @@
struct ieee80211_eht_operation *oper;
u8 *pos = eid, seg0 = 0, seg1 = 0;
enum oper_chan_width chwidth;
- size_t elen = 1 + 4 + 3;
+ size_t elen = 1 + 4;
+ bool eht_oper_info_present;
+ u16 punct_bitmap = conf->punct_bitmap;
if (!hapd->iface->current_mode)
return eid;
- if (hapd->iconf->punct_bitmap)
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!punct_bitmap && hapd->conf->eht_oper_puncturing_override) {
+ wpa_printf(MSG_DEBUG, "EHT: Puncturing mask override=0x%x",
+ hapd->conf->eht_oper_puncturing_override);
+ punct_bitmap = hapd->conf->eht_oper_puncturing_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (is_6ghz_op_class(conf->op_class))
+ chwidth = op_class_to_ch_width(conf->op_class);
+ else
+ chwidth = conf->eht_oper_chwidth;
+
+ eht_oper_info_present = chwidth == CONF_OPER_CHWIDTH_320MHZ ||
+ punct_bitmap;
+
+ if (eht_oper_info_present)
+ elen += 3;
+
+ if (punct_bitmap)
elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
*pos++ = WLAN_EID_EXTENSION;
@@ -212,7 +238,10 @@
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
oper = (struct ieee80211_eht_operation *) pos;
- oper->oper_params = EHT_OPER_INFO_PRESENT;
+ oper->oper_params = 0;
+
+ if (hapd->iconf->eht_default_pe_duration)
+ oper->oper_params |= EHT_OPER_DEFAULT_PE_DURATION;
/* TODO: Fill in appropriate EHT-MCS max Nss information */
oper->basic_eht_mcs_nss_set[0] = 0x11;
@@ -220,11 +249,10 @@
oper->basic_eht_mcs_nss_set[2] = 0x00;
oper->basic_eht_mcs_nss_set[3] = 0x00;
- if (is_6ghz_op_class(conf->op_class))
- chwidth = op_class_to_ch_width(conf->op_class);
- else
- chwidth = conf->eht_oper_chwidth;
+ if (!eht_oper_info_present)
+ return pos + elen;
+ oper->oper_params |= EHT_OPER_INFO_PRESENT;
seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
switch (chwidth) {
@@ -258,10 +286,10 @@
oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
oper->oper_info.ccfs1 = seg1;
- if (hapd->iconf->punct_bitmap) {
+ if (punct_bitmap) {
oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
oper->oper_info.disabled_chan_bitmap =
- host_to_le16(hapd->iconf->punct_bitmap);
+ host_to_le16(punct_bitmap);
}
return pos + elen;
@@ -338,9 +366,8 @@
static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
- u8 opclass, u8 he_oper_chwidth,
- const u8 *he_cap, const u8 *eht_cap,
- size_t len)
+ u8 opclass, const u8 *he_cap,
+ const u8 *eht_cap, size_t len)
{
const struct ieee80211_he_capabilities *he_capab;
struct ieee80211_eht_capabilities *cap;
@@ -355,8 +382,8 @@
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
- he_phy_cap, cap->phy_cap);
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap,
+ cap->phy_cap);
if (len < cap_len)
return true;
@@ -380,7 +407,6 @@
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
@@ -421,8 +447,9 @@
}
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id)
+static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ u8 *eid, struct mld_info *mld_info,
+ bool include_mld_id)
{
struct wpabuf *buf;
u16 control;
@@ -454,7 +481,8 @@
* BSS Parameters Change Count (1) + EML Capabilities (2) +
* MLD Capabilities and Operations (2)
*/
- common_info_len = 13;
+#define EHT_ML_COMMON_INFO_LEN 13
+ common_info_len = EHT_ML_COMMON_INFO_LEN;
if (include_mld_id) {
/* AP MLD ID */
@@ -489,12 +517,12 @@
wpabuf_put_u8(buf, hapd->conf->mld_id);
}
- if (!info)
+ if (!mld_info)
goto out;
/* Add link info for the other links */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- struct mld_link_info *link = &info->mld_info.links[link_id];
+ struct mld_link_info *link = &mld_info->links[link_id];
struct hostapd_data *link_bss;
/*
@@ -502,8 +530,9 @@
* beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
* parameters change counter (1) + station profile length.
*/
- const size_t fixed_len = 22;
- size_t total_len = fixed_len + link->resp_sta_profile_len;
+#define EHT_ML_STA_INFO_LEN 22
+ size_t total_len = EHT_ML_STA_INFO_LEN +
+ link->resp_sta_profile_len;
/* Skip the local one */
if (link_id == hapd->mld_link_id || !link->valid)
@@ -537,7 +566,7 @@
/* STA Info */
/* STA Info Length */
- wpabuf_put_u8(buf, fixed_len - 2);
+ wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
@@ -552,9 +581,10 @@
wpabuf_put_le16(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
- /* TODO: Currently hard code the BSS Parameters Change Count to
- * 0x1 */
- wpabuf_put_u8(buf, 0x1);
+ wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+
+ if (!link->resp_sta_profile)
+ continue;
/* Fragment the sub element if needed */
if (total_len <= 255) {
@@ -564,7 +594,7 @@
ptr = link->resp_sta_profile;
len = link->resp_sta_profile_len;
- slice_len = 255 - fixed_len;
+ slice_len = 255 - EHT_ML_STA_INFO_LEN;
wpabuf_put_data(buf, ptr, slice_len);
len -= slice_len;
@@ -625,6 +655,151 @@
}
+static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct hostapd_data *other_hapd;
+ u16 control;
+ u8 *pos = eid;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML");
+
+ /* First check if the element needs to be added */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: %u",
+ other_hapd->eht_mld_link_removal_count);
+
+ if (other_hapd->eht_mld_link_removal_count)
+ break;
+ }
+
+ /* No link is going to be removed */
+ if (i == hapd->iface->interfaces->count)
+ return eid;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: Adding element");
+
+ /* The length will be set at the end */
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 0;
+ *pos++ = WLAN_EID_EXT_MULTI_LINK;
+
+ /* Set the Multi-Link Control field */
+ control = MULTI_LINK_CONTROL_TYPE_RECONF;
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* Common Info doesn't include any information */
+ *pos++ = 1;
+
+ /* Add the per station profiles */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+ if (!other_hapd->eht_mld_link_removal_count)
+ continue;
+
+ /* Subelement ID is 0 */
+ *pos++ = 0;
+ *pos++ = 5;
+
+ control = other_hapd->mld_link_id |
+ EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER;
+
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* STA profile length */
+ *pos++ = 3;
+
+ WPA_PUT_LE16(pos, other_hapd->eht_mld_link_removal_count);
+ pos += 2;
+ }
+
+ eid[1] = pos - eid - 2;
+
+ wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration ML", eid, eid[1] + 2);
+ return pos;
+#else /* CONFIG_TESTING_OPTIONS */
+ return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ bool include_mld_id)
+{
+ size_t len = 0;
+ size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
+ u8 link_id;
+
+ if (include_mld_id)
+ eht_ml_len++;
+
+ for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
+ link_id++) {
+ struct mld_link_info *link;
+ size_t sta_len = EHT_ML_STA_INFO_LEN;
+
+ link = &info->links[link_id];
+ if (!link->valid)
+ continue;
+
+ sta_len += link->resp_sta_profile_len;
+
+ /* Element data and (fragmentation) headers */
+ eht_ml_len += sta_len;
+ eht_ml_len += 2 + sta_len / 255 * 2;
+ }
+
+ /* Element data */
+ len += eht_ml_len;
+
+ /* First header (254 bytes of data) */
+ len += 3;
+
+ /* Fragmentation headers; +1 for shorter first chunk */
+ len += (eht_ml_len + 1) / 255 * 2;
+
+ return len;
+}
+#undef EHT_ML_COMMON_INFO_LEN
+#undef EHT_ML_STA_INFO_LEN
+
+
+u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+ struct mld_info *info,
+ u8 *eid, bool include_mld_id)
+{
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid)
+{
+ if (!ap_sta_is_mld(hapd, info))
+ return eid;
+
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
+ false);
+ ap_sta_free_sta_profile(&info->mld_info);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ struct mld_info *info,
+ bool include_mld_id)
+{
+ return hostapd_eid_eht_ml_len(info, include_mld_id);
+}
+
+
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
{
struct wpabuf *buf = wpabuf_alloc(12);
@@ -841,11 +1016,12 @@
static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- struct mld_info *info)
+ struct sta_info *sta)
{
u8 i, link_id;
+ struct mld_info *info = &sta->mld_info;
- if (!info->mld_sta) {
+ if (!ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
return 0;
}
@@ -894,6 +1070,85 @@
}
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr)
+{
+ struct wpabuf *mlbuf = ieee802_11_defrag(basic_mle, basic_mle_len,
+ true);
+ struct ieee80211_eht_ml *ml;
+ struct eht_ml_basic_common_info *common_info;
+ size_t ml_len, common_info_len;
+ int ret = -1;
+ u16 ml_control;
+
+ if (!mlbuf)
+ return WLAN_STATUS_SUCCESS;
+
+ ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+
+ if (ml_len < sizeof(*ml))
+ goto out;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+ ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+ goto out;
+ }
+
+ /* Common Info Length and MLD MAC Address must always be present */
+ common_info_len = 1 + ETH_ALEN;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+ wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: BSS Parameters Change Count not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Medium Synchronization Delay Information not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
+ common_info_len += 2;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
+ common_info_len += 2;
+
+ if (sizeof(*ml) + common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+ goto out;
+ }
+
+ common_info = (struct eht_ml_basic_common_info *) ml->variable;
+
+ /* Common information length includes the length octet */
+ if (common_info->len != common_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid common info len=%u", common_info->len);
+ goto out;
+ }
+
+ /* Get the MLD MAC Address */
+ os_memcpy(mld_addr, common_info->mld_addr, ETH_ALEN);
+ ret = 0;
+
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta)
@@ -908,7 +1163,7 @@
int ret = -1;
u16 ml_control;
- mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
if (!mlbuf)
return WLAN_STATUS_SUCCESS;
@@ -990,8 +1245,8 @@
info->common_info.eml_capa, info->common_info.mld_capa);
/* Check the MLD MAC Address */
- if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(info->common_info.mld_addr,
+ common_info->mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: MLD address mismatch between authentication ("
MACSTR ") and association (" MACSTR ")",
@@ -1000,7 +1255,7 @@
goto out;
}
- info->links[hapd->mld_link_id].valid = true;
+ info->links[hapd->mld_link_id].valid = 1;
/* Parse the link info field */
ml_len -= sizeof(*ml) + common_info_len;
@@ -1031,9 +1286,11 @@
if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
wpa_printf(MSG_DEBUG,
- "MLD: Unexpected Multi-Link element subelement ID=%u",
+ "MLD: Skip unknown Multi-Link element subelement ID=%u",
*pos);
- goto out;
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
}
/* Skip the subelement ID and the length */
@@ -1138,7 +1395,7 @@
goto out;
}
- ret = hostapd_mld_validate_assoc_info(hapd, info);
+ ret = hostapd_mld_validate_assoc_info(hapd, sta);
out:
wpabuf_free(mlbuf);
if (ret) {
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 548a448..4b693a7 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -254,16 +254,15 @@
control = 3;
else
control = center_idx_to_bw_6ghz(seg0);
- if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
- control |= HE_6GHZ_STANDARD_POWER_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
- else
- control |= HE_6GHZ_INDOOR_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
+ control |= hapd->iconf->he_6ghz_reg_pwr_type <<
+ HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
*pos++ = control;
/* Channel Center Freq Seg0/Seg1 */
- if (oper_chwidth == 2) {
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+ oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) {
/*
* Seg 0 indicates the channel center frequency index of
* the 160 MHz channel.
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 31dfb62..0c38483 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,16 +21,25 @@
#include "ieee802_11.h"
+static u8 * hostapd_eid_timeout_interval(u8 *pos, u8 type, u32 value)
+{
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = type;
+ WPA_PUT_LE32(pos, value);
+ pos += 4;
+
+ return pos;
+}
+
+
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid)
{
- u8 *pos = eid;
u32 timeout, tu;
struct os_reltime now, passed;
+ u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK;
- *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
- *pos++ = 5;
- *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
os_get_reltime(&now);
os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
@@ -40,10 +49,12 @@
timeout = 0;
if (timeout < hapd->conf->assoc_sa_query_max_timeout)
timeout++; /* add some extra time for local timers */
- WPA_PUT_LE32(pos, timeout);
- pos += 4;
- return pos;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->test_assoc_comeback_type != -1)
+ type = hapd->conf->test_assoc_comeback_type;
+#endif /* CONFIG_TESTING_OPTIONS */
+ return hostapd_eid_timeout_interval(eid, type, timeout);
}
@@ -51,13 +62,14 @@
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
-#ifdef CONFIG_OCV
- struct sta_info *sta;
-#endif /* CONFIG_OCV */
+#if defined(CONFIG_OCV) || defined(CONFIG_IEEE80211BE)
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+#endif /* CONFIG_OCV || CONFIG_IEEE80211BE */
struct ieee80211_mgmt *mgmt;
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
MACSTR, MAC2STR(addr));
@@ -65,7 +77,6 @@
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
- sta = ap_get_sta(hapd, addr);
if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
@@ -108,11 +119,16 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
@@ -141,6 +157,7 @@
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
MACSTR, MAC2STR(sa));
@@ -200,11 +217,16 @@
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(resp->da, sa, ETH_ALEN);
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->sa, own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, own_addr, ETH_ALEN);
resp->u.action.category = WLAN_ACTION_SA_QUERY;
resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
@@ -1137,3 +1159,44 @@
return WLAN_STATUS_SUCCESS;
}
+
+
+struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *other_hapd = NULL;
+ struct sta_info *tmp_sta;
+
+ if (!ap_sta_is_mld(hapd, sta))
+ return NULL;
+
+ *assoc_hapd = hapd;
+
+ /* The station is the one on which the association was performed */
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ return sta;
+
+ other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+ sta->mld_assoc_link_id);
+ return sta;
+ }
+
+ /*
+ * Iterate over the stations and find the one with the matching link ID
+ * and association ID.
+ */
+ for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+ tmp_sta->aid == sta->aid) {
+ *assoc_hapd = other_hapd;
+ return tmp_sta;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return sta;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 052231e..f13c60a 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -114,12 +114,15 @@
bool authorized, bool mld)
{
int res;
+ bool update;
if (sta->flags & WLAN_STA_PREAUTH)
return;
- ap_sta_set_authorized(hapd, sta, authorized);
+ update = ap_sta_set_authorized_flag(hapd, sta, authorized);
res = hostapd_set_authorized(hapd, sta, authorized);
+ if (update)
+ ap_sta_set_authorized_event(hapd, sta, authorized);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
authorized ? "" : "un");
diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
new file mode 100644
index 0000000..52a967a
--- /dev/null
+++ b/src/ap/nan_usd_ap.c
@@ -0,0 +1,267 @@
+/*
+ * NAN unsynchronized service discovery (USD)
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "common/nan_de.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "nan_usd_ap.h"
+
+
+static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
+ unsigned int wait_time,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const struct wpabuf *buf)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
+ " A3=" MACSTR " len=%zu",
+ MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ wpabuf_len(buf));
+
+ /* TODO: Force use of OFDM */
+ return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
+ wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
+ unsigned int duration)
+{
+ return 0;
+}
+
+
+static void
+hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const u8 *ssi, size_t ssi_len,
+ int peer_publish_id, const u8 *peer_addr,
+ bool fsd, bool fsd_gas)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
+ "subscribe_id=%d publish_id=%d address=" MACSTR
+ " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+ subscribe_id, peer_publish_id, MAC2STR(peer_addr),
+ fsd, fsd_gas, srv_proto_type, ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+static void
+hostapd_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr,
+ int peer_subscribe_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const u8 *ssi, size_t ssi_len)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_REPLIED
+ "publish_id=%d address=" MACSTR
+ " subscribe_id=%d srv_proto_type=%u ssi=%s",
+ publish_id, MAC2STR(peer_addr), peer_subscribe_id,
+ srv_proto_type, ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+static const char * nan_reason_txt(enum nan_de_reason reason)
+{
+ switch (reason) {
+ case NAN_DE_REASON_TIMEOUT:
+ return "timeout";
+ case NAN_DE_REASON_USER_REQUEST:
+ return "user-request";
+ case NAN_DE_REASON_FAILURE:
+ return "failure";
+ }
+
+ return "unknown";
+}
+
+
+static void hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
+ "publish_id=%d reason=%s",
+ publish_id, nan_reason_txt(reason));
+}
+
+
+static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
+ "subscribe_id=%d reason=%s",
+ subscribe_id, nan_reason_txt(reason));
+}
+
+
+static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
+ const u8 *ssi, size_t ssi_len,
+ const u8 *peer_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_RECEIVE
+ "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
+ id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+int hostapd_nan_usd_init(struct hostapd_data *hapd)
+{
+ struct nan_callbacks cb;
+
+ os_memset(&cb, 0, sizeof(cb));
+ cb.ctx = hapd;
+ cb.tx = hostapd_nan_de_tx;
+ cb.listen = hostapd_nan_de_listen;
+ cb.discovery_result = hostapd_nan_de_discovery_result;
+ cb.replied = hostapd_nan_de_replied;
+ cb.publish_terminated = hostapd_nan_de_publish_terminated;
+ cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
+ cb.receive = hostapd_nan_de_receive;
+
+ hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb);
+ if (!hapd->nan_de)
+ return -1;
+ return 0;
+}
+
+
+void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
+{
+ nan_de_deinit(hapd->nan_de);
+ hapd->nan_de = NULL;
+}
+
+
+void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+ unsigned int freq, const u8 *buf, size_t len)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+}
+
+
+void hostapd_nan_usd_flush(struct hostapd_data *hapd)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_flush(hapd->nan_de);
+}
+
+
+int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_publish_params *params)
+{
+ int publish_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
+ ssi, elems, params);
+ wpabuf_free(elems);
+ return publish_id;
+}
+
+
+void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_publish(hapd->nan_de, publish_id);
+}
+
+
+int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
+ const struct wpabuf *ssi)
+{
+ int ret;
+
+ if (!hapd->nan_de)
+ return -1;
+ ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
+ return ret;
+}
+
+
+int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
+ const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_subscribe_params *params)
+{
+ int subscribe_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
+ srv_proto_type, ssi, elems, params);
+ wpabuf_free(elems);
+ return subscribe_id;
+}
+
+
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
+}
+
+
+int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ const u8 *peer_addr, u8 req_instance_id)
+{
+ if (!hapd->nan_de)
+ return -1;
+ return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
+ req_instance_id);
+}
diff --git a/src/ap/nan_usd_ap.h b/src/ap/nan_usd_ap.h
new file mode 100644
index 0000000..58ff5fc
--- /dev/null
+++ b/src/ap/nan_usd_ap.h
@@ -0,0 +1,46 @@
+/*
+ * NAN unsynchronized service discovery (USD)
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NAN_USD_AP_H
+#define NAN_USD_AP_H
+
+struct nan_subscribe_params;
+struct nan_publish_params;
+enum nan_service_protocol_type;
+
+int hostapd_nan_usd_init(struct hostapd_data *hapd);
+void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
+void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+ unsigned int freq, const u8 *buf, size_t len);
+void hostapd_nan_usd_flush(struct hostapd_data *hapd);
+int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_publish_params *params);
+void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id);
+int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
+ const struct wpabuf *ssi);
+int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
+ const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_subscribe_params *params);
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id);
+int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ const u8 *peer_addr, u8 req_instance_id);
+void hostapd_nan_usd_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int duration);
+void hostapd_nan_usd_cancel_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq);
+void hostapd_nan_usd_tx_wait_expire(struct hostapd_data *hapd);
+
+#endif /* NAN_USD_AP_H */
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 5b276e8..2a25ae2 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -24,7 +24,7 @@
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
list) {
- if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(bssid, nr->bssid) &&
(!ssid ||
(ssid->ssid_len == nr->ssid.ssid_len &&
os_memcmp(ssid->ssid, nr->ssid.ssid,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index ee4232f..2fce838 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -487,14 +487,14 @@
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
entry = entry->hnext) {
if ((spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ ether_addr_equal(entry->spa, spa)) &&
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
} else {
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+ ether_addr_equal(entry->spa, spa))
return entry;
}
}
@@ -521,7 +521,7 @@
u8 new_pmkid[PMKID_LEN];
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->spa, spa))
continue;
if (wpa_key_mgmt_sae(entry->akmp) ||
wpa_key_mgmt_fils(entry->akmp)) {
@@ -575,7 +575,7 @@
int match = 0;
if (attr->sta_addr) {
- if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(attr->sta_addr, entry->spa))
return 0;
match++;
}
@@ -717,7 +717,7 @@
* <BSSID> <PMKID> <PMK> <expiration in seconds>
*/
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ if (addr && !ether_addr_equal(entry->spa, addr))
continue;
ret = os_snprintf(pos, end - pos, MACSTR " ",
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
index 3284a10..cb225c6 100644
--- a/src/ap/preauth_auth.c
+++ b/src/ap/preauth_auth.c
@@ -58,7 +58,7 @@
ethhdr = (struct l2_ethhdr *) buf;
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(ethhdr->h_dest, hapd->own_addr)) {
wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
MACSTR, MAC2STR(ethhdr->h_dest));
return;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index a00f896..2178d65 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -92,7 +92,7 @@
if (p2p_dev_addr == NULL)
continue;
- if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(p2p_dev_addr, addr))
return sta;
}
@@ -140,7 +140,7 @@
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, sta->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
@@ -303,7 +303,7 @@
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
- if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+ if (!ap_sta_is_mld(hapd, sta) ||
hapd->mld_link_id == sta->mld_assoc_link_id)
wpa_auth_sta_deinit(sta->wpa_sm);
#else
@@ -350,6 +350,7 @@
#ifdef CONFIG_INTERWORKING
if (sta->gas_dialog) {
int i;
+
for (i = 0; i < GAS_DIALOG_MAX; i++)
gas_serv_dialog_clear(&sta->gas_dialog[i]);
os_free(sta->gas_dialog);
@@ -420,6 +421,10 @@
os_free(sta->ifname_wds);
+#ifdef CONFIG_IEEE80211BE
+ ap_sta_free_sta_profile(&sta->mld_info);
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_TESTING_OPTIONS
os_free(sta->sae_postponed_commit);
forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
@@ -846,32 +851,20 @@
}
-void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_disconnect_common(struct hostapd_data *hapd,
+ struct sta_info *sta, unsigned int timeout)
{
- wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
- hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
- /* Skip deauthentication in DMG/IEEE 802.11ad */
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
- WLAN_STA_ASSOC_REQ_OK);
- sta->timeout_next = STA_REMOVE;
- } else {
- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
- sta->timeout_next = STA_DEAUTH;
- }
+
ap_sta_set_authorized(hapd, sta, 0);
hostapd_set_sta_flags(hapd, sta);
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
- __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DISASSOC);
+
+ wpa_printf(MSG_DEBUG,
+ "reschedule ap_handle_timer timeout (%u sec) for " MACSTR,
+ MAC2STR(sta->addr), timeout);
+
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
- ap_handle_timer, hapd, sta);
+ eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
@@ -883,6 +876,27 @@
#endif /* CONFIG_IEEE80211BE */
sta->wpa_sm = NULL;
+}
+
+
+static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Skip deauthentication in DMG/IEEE 802.11ad */
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_REMOVE;
+ } else {
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_DEAUTH;
+ }
+
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
sta->disassoc_reason = reason;
sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -905,8 +919,8 @@
}
-void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
{
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
@@ -918,21 +932,11 @@
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
- ap_sta_set_authorized(hapd, sta, 0);
- hostapd_set_sta_flags(hapd, sta);
+
sta->timeout_next = STA_REMOVE;
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
- __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DEAUTH);
- eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
- ap_handle_timer, hapd, sta);
- accounting_sta_stop(hapd, sta);
- ieee802_1x_free_station(hapd, sta);
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
sta->deauth_reason = reason;
sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
@@ -943,6 +947,105 @@
}
+static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason,
+ bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+ unsigned int i, link_id;
+ struct hapd_interfaces *interfaces;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+
+ /*
+ * Get the station on which the association was performed, as it holds
+ * the information about all the other links.
+ */
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
+ interfaces = assoc_hapd->iface->interfaces;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
+ tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ /*
+ * Handle the station on which the association
+ * was done only after all other link station
+ * are removed. Since there is a only a single
+ * station per hapd with the same association
+ * link simply break;
+ */
+ if (tmp_sta == assoc_sta)
+ break;
+
+ if (tmp_sta->mld_assoc_link_id !=
+ assoc_sta->mld_assoc_link_id ||
+ tmp_sta->aid != assoc_sta->aid)
+ continue;
+
+ if (disassoc)
+ ap_sta_handle_disassociate(tmp_hapd,
+ tmp_sta,
+ reason);
+ else
+ ap_sta_handle_deauthenticate(tmp_hapd,
+ tmp_sta,
+ reason);
+
+ break;
+ }
+ }
+ }
+
+ /* Disconnect the station on which the association was performed. */
+ if (disassoc)
+ ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
+ else
+ ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+
+ return true;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+ return;
+
+ ap_sta_handle_disassociate(hapd, sta, reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+ return;
+
+ ap_sta_handle_deauthenticate(hapd, sta, reason);
+}
+
+
#ifdef CONFIG_WPS
int ap_sta_wps_cancel(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx)
@@ -1292,25 +1395,17 @@
}
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
- int authorized)
+bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
{
- const u8 *dev_addr = NULL;
- char buf[100];
-#ifdef CONFIG_P2P
- u8 addr[ETH_ALEN];
- u8 ip_addr_buf[4];
-#endif /* CONFIG_P2P */
- u8 *ip_ptr = NULL;
-
if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
- return;
+ return false;
if (authorized) {
int mld_assoc_link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
if (sta->mld_assoc_link_id == hapd->mld_link_id)
mld_assoc_link_id = sta->mld_assoc_link_id;
else
@@ -1325,6 +1420,21 @@
sta->flags &= ~WLAN_STA_AUTHORIZED;
}
+ return true;
+}
+
+
+void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ const u8 *dev_addr = NULL;
+ char buf[100];
+#ifdef CONFIG_P2P
+ u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
+#endif /* CONFIG_P2P */
+ u8 *ip_ptr = NULL;
+
#ifdef CONFIG_P2P
if (hapd->p2p_group == NULL) {
if (sta->p2p_ie != NULL &&
@@ -1413,6 +1523,15 @@
}
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
+ return;
+ ap_sta_set_authorized_event(hapd, sta, authorized);
+}
+
+
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason)
{
@@ -1604,6 +1723,34 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_data *tmp_hapd;
+ unsigned int i, j;
+
+ for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
+ hapd->conf->mld_id) {
+ struct sta_info *tmp_sta;
+
+ if (hapd == tmp_hapd)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ !ether_addr_equal(tmp_sta->addr, sta->addr))
+ continue;
+
+ ap_free_sta(tmp_hapd, tmp_sta);
+ break;
+ }
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
{
const u8 *mld_link_addr = NULL;
@@ -1618,11 +1765,17 @@
*/
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+
+ /*
+ * In case the AP is affiliated with an AP MLD, we need to
+ * remove the station from all relevant links/APs.
+ */
+ ap_sta_remove_link_sta(hapd, sta);
}
#endif /* CONFIG_IEEE80211BE */
@@ -1646,3 +1799,19 @@
sta->added_unassoc = 1;
return 0;
}
+
+
+#ifdef CONFIG_IEEE80211BE
+void ap_sta_free_sta_profile(struct mld_info *info)
+{
+ int i;
+
+ if (!info)
+ return;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(info->links[i].resp_sta_profile);
+ info->links[i].resp_sta_profile = NULL;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e2b9dde..b136ff7 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -17,6 +17,7 @@
#include "common/sae.h"
#include "crypto/sha384.h"
#include "pasn/pasn_common.h"
+#include "hostapd.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -81,18 +82,18 @@
} common_info;
struct mld_link_info {
- u8 valid;
+ u8 valid:1;
+ u8 nstr_bitmap_len:2;
u8 local_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
- size_t nstr_bitmap_len;
u8 nstr_bitmap[2];
u16 capability;
u16 status;
- size_t resp_sta_profile_len;
- u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN];
+ u16 resp_sta_profile_len;
+ u8 *resp_sta_profile;
const u8 *rsne, *rsnxe;
} links[MAX_NUM_MLD_LINKS];
@@ -393,6 +394,10 @@
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
+bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized);
+void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
void ap_sta_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
static inline int ap_sta_is_authorized(struct sta_info *sta)
@@ -415,4 +420,24 @@
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (sta)
+ sta->mld_info.mld_sta = mld;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+void ap_sta_free_sta_profile(struct mld_info *info);
+
#endif /* STA_INFO_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 9ebb01e..dad768e 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -20,13 +20,6 @@
#include "ap_drv_ops.h"
#include "wmm.h"
-#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-#ifndef MAX
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
{
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 153ee40..b77e21b 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -44,6 +44,20 @@
}
+static const u8 * wnm_ap_get_own_addr(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && (!sta || ap_sta_is_mld(hapd, sta)))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ return own_addr;
+}
+
+
/* MLME-SLEEPMODE.response */
static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
const u8 *addr, u8 dialog_token,
@@ -63,6 +77,7 @@
struct sta_info *sta;
enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+ const u8 *own_addr;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
@@ -143,9 +158,12 @@
res = -1;
goto fail;
}
+
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -366,6 +384,8 @@
u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr;
+ struct sta_info *sta;
size_t len;
u8 *pos;
int res;
@@ -373,9 +393,13 @@
mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -821,14 +845,15 @@
{
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -887,14 +912,15 @@
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -939,6 +965,7 @@
u8 *buf, *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR
@@ -952,8 +979,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
@@ -1002,6 +1029,26 @@
os_free(buf);
if (disassoc_timer) {
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ int i;
+ unsigned int links = 0;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (sta->mld_info.links[i].valid)
+ links++;
+ }
+
+ if (links > 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Only terminating one link - other links remains associated for "
+ MACSTR,
+ MAC2STR(sta->mld_info.common_info.mld_addr));
+ return 0;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
/* send disassociation frame after time-out */
set_disassoc_timer(hapd, sta, disassoc_timer);
}
@@ -1016,6 +1063,7 @@
u8 buf[100], *pos;
struct ieee80211_mgmt *mgmt;
u8 dialog_token = 1;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
if (auto_report > 3 || timeout > 63)
return -1;
@@ -1024,8 +1072,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.coloc_intf_req.action =
WNM_COLLOCATED_INTERFERENCE_REQ;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index a662201..3002d91 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -194,6 +194,9 @@
if (!wpa_auth->cb->get_seqnum)
return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+ os_memset(seq, 0, WPA_KEY_RSC_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
#ifdef CONFIG_TESTING_OPTIONS
if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
@@ -599,6 +602,15 @@
}
#endif /* CONFIG_P2P */
+ if (conf->tx_bss_auth && conf->beacon_prot) {
+ conf->tx_bss_auth->non_tx_beacon_prot = true;
+ if (!conf->tx_bss_auth->conf.beacon_prot)
+ conf->tx_bss_auth->conf.beacon_prot = true;
+ if (!conf->tx_bss_auth->conf.group_mgmt_cipher)
+ conf->tx_bss_auth->conf.group_mgmt_cipher =
+ conf->group_mgmt_cipher;
+ }
+
return wpa_auth;
}
@@ -618,6 +630,17 @@
}
+static void wpa_auth_free_conf(struct wpa_auth_config *conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->eapol_m1_elements);
+ conf->eapol_m1_elements = NULL;
+ wpabuf_free(conf->eapol_m3_elements);
+ conf->eapol_m3_elements = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -651,6 +674,7 @@
bin_clear_free(prev, sizeof(*prev));
}
+ wpa_auth_free_conf(&wpa_auth->conf);
os_free(wpa_auth);
}
@@ -668,6 +692,7 @@
if (!wpa_auth)
return 0;
+ wpa_auth_free_conf(&wpa_auth->conf);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -900,19 +925,70 @@
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
{
- struct wpa_ie_data ie;
+ struct wpa_ie_data ie, assoc_ie;
struct rsn_mdie *mdie;
+ unsigned int i, j;
+ bool found = false;
+
+ /* Verify that PMKR1Name from EAPOL-Key message 2/4 matches the value
+ * we derived. */
if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
- ie.num_pmkid != 1 || !ie.pmkid) {
+ ie.num_pmkid < 1 || !ie.pmkid) {
wpa_printf(MSG_DEBUG,
"FT: No PMKR1Name in FT 4-way handshake message 2/4");
return -1;
}
- os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, PMKID_LEN);
+ if (wpa_parse_wpa_ie_rsn(sm->wpa_ie, sm->wpa_ie_len, &assoc_ie) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Could not parse (Re)Association Request frame RSNE");
+ os_memset(&assoc_ie, 0, sizeof(assoc_ie));
+ /* Continue to allow PMKR1Name matching to be done to cover the
+ * case where it is the only listed PMKID. */
+ }
+
+ for (i = 0; i < ie.num_pmkid; i++) {
+ const u8 *pmkid = ie.pmkid + i * PMKID_LEN;
+
+ if (os_memcmp_const(pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant matches PMKR1Name",
+ i);
+ found = true;
+ } else {
+ for (j = 0; j < assoc_ie.num_pmkid; j++) {
+ if (os_memcmp(pmkid,
+ assoc_ie.pmkid + j * PMKID_LEN,
+ PMKID_LEN) == 0)
+ break;
+ }
+
+ if (j == assoc_ie.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is neither PMKR1Name nor included in AssocReq",
+ i);
+ found = false;
+ break;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is not PMKR1Name, but matches a PMKID in AssocReq",
+ i);
+ }
+ }
+
+ if (!found) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way handshake");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: PMKIDs/PMKR1Name from Supplicant",
+ ie.pmkid, ie.num_pmkid * PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return -1;
+ }
if (!kde->mdie || !kde->ftie) {
wpa_printf(MSG_DEBUG,
@@ -1076,28 +1152,166 @@
}
+enum eapol_key_msg { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST };
+
+static bool wpa_auth_valid_key_desc_ver(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, u16 ver)
+{
+ if (ver > WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_INFO, "RSN: " MACSTR
+ " used undefined Key Descriptor Version %d",
+ MAC2STR(wpa_auth_get_spa(sm)), ver);
+ return false;
+ }
+
+ if (!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "advertised support for AES-128-CMAC, but did not use it");
+ return false;
+ }
+
+ if (sm->pairwise != WPA_CIPHER_TKIP &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ !wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use HMAC-SHA1-AES with CCMP/GCMP");
+ return false;
+ }
+
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_request_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *replay_counter)
+{
+
+ if (sm->req_replay_counter_used &&
+ os_memcmp(replay_counter, sm->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "received EAPOL-Key request with replayed counter");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const struct wpa_eapol_key *key,
+ enum eapol_key_msg msg,
+ const char *msgtxt)
+{
+ int i;
+
+ if (msg == REQUEST)
+ return wpa_auth_valid_request_counter(wpa_auth, sm,
+ key->replay_counter);
+
+ if (wpa_replay_counter_valid(sm->key_replay, key->replay_counter))
+ return true;
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ /*
+ * Some supplicant implementations (e.g., Windows XP
+ * WZC) update SNonce for each EAPOL-Key 2/4. This
+ * breaks the workaround on accepting any of the
+ * pending requests, so allow the SNonce to be updated
+ * even if we have already sent out EAPOL-Key 3/4.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
+ sm->update_snonce = 1;
+ os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+ sm->alt_snonce_valid = true;
+ os_memcpy(sm->alt_replay_counter,
+ sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ return true;
+ }
+
+ if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(key->replay_counter, sm->alt_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) == 0) {
+ /*
+ * Supplicant may still be using the old SNonce since
+ * there was two EAPOL-Key 2/4 messages and they had
+ * different SNonce values.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+ return true;
+ }
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "ignore retransmitted EAPOL-Key %s - SNonce did not change",
+ msgtxt);
+ } else {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "received EAPOL-Key %s with unexpected replay counter",
+ msgtxt);
+ }
+ 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 false;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- u16 key_info, key_data_length;
- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
- char *msgtxt;
- struct wpa_eapol_ie_parse kde;
+ u16 key_info, ver, key_data_length;
+ enum eapol_key_msg msg;
+ const char *msgtxt;
const u8 *key_data;
size_t keyhdrlen, mic_len;
u8 *mic;
- bool is_mld = false;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return;
-#ifdef CONFIG_IEEE80211BE
- is_mld = sm->mld_assoc_link_id >= 0;
-#endif /* CONFIG_IEEE80211BE */
-
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
@@ -1167,11 +1381,31 @@
return;
}
- /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
- * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
- * MLD mechanism here does not work without the somewhat undesired check
- * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be
- * able to know which message this is in MLO cases.. */
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (!wpa_auth_valid_key_desc_ver(wpa_auth, sm, ver))
+ goto out;
+ if (mic_len > 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ sm->PTK_valid &&
+ (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) &&
+ key_data_length >= 8 && key_data_length % 8 == 0) {
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ key_data_buf_len = key_data_length;
+ if (aes_unwrap(sm->PTK.kek, sm->PTK.kek_len,
+ key_data_length / 8, key_data, key_data_buf)) {
+ wpa_printf(MSG_INFO,
+ "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+ goto out;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
@@ -1179,10 +1413,13 @@
msg = GROUP_2;
msgtxt = "2/2 Group";
} else if (key_data_length == 0 ||
+ (sm->wpa == WPA_VERSION_WPA2 &&
+ (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ||
+ key_data_buf) &&
+ (key_info & WPA_KEY_INFO_SECURE) &&
+ !get_ie(key_data, key_data_length, WLAN_EID_RSN)) ||
(mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
- key_data_length == AES_BLOCK_SIZE) ||
- (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
+ key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -1190,127 +1427,15 @@
msgtxt = "2/4 Pairwise";
}
- if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
- msg == GROUP_2) {
- u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
- if (sm->pairwise == WPA_CIPHER_CCMP ||
- sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "advertised support for AES-128-CMAC, but did not use it");
- return;
- }
+ if (!wpa_auth_valid_counter(wpa_auth, sm, key, msg, msgtxt))
+ goto out;
- if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use HMAC-SHA1-AES with CCMP/GCMP");
- return;
- }
- }
-
- if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
- return;
- }
- }
-
- if (key_info & WPA_KEY_INFO_REQUEST) {
- if (sm->req_replay_counter_used &&
- os_memcmp(key->replay_counter, sm->req_replay_counter,
- WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "received EAPOL-Key request with replayed counter");
- return;
- }
- }
-
- if (!(key_info & WPA_KEY_INFO_REQUEST) &&
- !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
- int i;
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
- {
- /*
- * Some supplicant implementations (e.g., Windows XP
- * WZC) update SNonce for each EAPOL-Key 2/4. This
- * breaks the workaround on accepting any of the
- * pending requests, so allow the SNonce to be updated
- * even if we have already sent out EAPOL-Key 3/4.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
- sm->update_snonce = 1;
- os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
- sm->alt_snonce_valid = true;
- os_memcpy(sm->alt_replay_counter,
- sm->key_replay[0].counter,
- WPA_REPLAY_COUNTER_LEN);
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(key->replay_counter, sm->alt_replay_counter,
- WPA_REPLAY_COUNTER_LEN) == 0) {
- /*
- * Supplicant may still be using the old SNonce since
- * there was two EAPOL-Key 2/4 messages and they had
- * different SNonce values.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "ignore retransmitted EAPOL-Key %s - SNonce did not change",
- msgtxt);
- } else {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "received EAPOL-Key %s with unexpected replay counter",
- msgtxt);
- }
- 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;
- }
-
-continue_processing:
#ifdef CONFIG_FILS
if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1324,7 +1449,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
if (sm->group->reject_4way_hs_for_entropy) {
@@ -1342,7 +1467,7 @@
random_mark_pool_ready();
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
- return;
+ goto out;
}
break;
case PAIRWISE_4:
@@ -1352,7 +1477,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
break;
case GROUP_2:
@@ -1362,10 +1487,20 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
sm->wpa_ptk_group_state);
- return;
+ goto out;
}
break;
case REQUEST:
+ if (sm->wpa_ptk_state == WPA_PTK_PTKSTART ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING2 ||
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request in invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ goto out;
+ }
break;
}
@@ -1375,14 +1510,14 @@
if (key_info & WPA_KEY_INFO_ACK) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key Ack set");
- return;
+ goto out;
}
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
!(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
- return;
+ goto out;
}
#ifdef CONFIG_FILS
@@ -1390,7 +1525,7 @@
(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC set");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1409,7 +1544,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#ifdef CONFIG_FILS
if (!mic_len &&
@@ -1423,7 +1558,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
#ifdef TEST_FUZZ
@@ -1435,6 +1570,12 @@
}
if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (!(key_info & WPA_KEY_INFO_SECURE)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key request without Secure=1");
+ goto out;
+ }
if (sm->MICVerified) {
sm->req_replay_counter_used = 1;
os_memcpy(sm->req_replay_counter, key->replay_counter,
@@ -1443,28 +1584,19 @@
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key request with invalid MIC");
- return;
+ goto out;
}
- /*
- * TODO: should decrypt key data field if encryption was used;
- * even though MAC address KDE is not normally encrypted,
- * supplicant is allowed to encrypt it.
- */
if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
- return; /* STA entry was removed */
+ goto out; /* STA entry was removed */
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for new 4-Way Handshake");
wpa_request_new_ptk(sm);
- } else if (key_data_length > 0 &&
- wpa_parse_kde_ies(key_data, key_data_length,
- &kde) == 0 &&
- kde.mac_addr) {
} else {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
@@ -1506,7 +1638,7 @@
os_free(sm->last_rx_eapol_key);
sm->last_rx_eapol_key = os_memdup(data, data_len);
if (!sm->last_rx_eapol_key)
- return;
+ goto out;
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1515,6 +1647,9 @@
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
wpa_sm_step(sm);
+
+out:
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -2332,10 +2467,14 @@
SM_STATE(WPA_PTK, PTKSTART)
{
- u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+ u8 *buf;
+ size_t buf_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
u8 *pmkid = NULL;
size_t kde_len = 0;
u16 key_info;
+#ifdef CONFIG_TESTING_OPTIONS
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#endif /* CONFIG_TESTING_OPTIONS */
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = false;
@@ -2350,6 +2489,19 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ buf_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements)
+ buf_len += wpabuf_len(conf->eapol_m1_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ buf = os_zalloc(buf_len);
+ if (!buf)
+ return;
+
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
@@ -2453,11 +2605,20 @@
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements) {
+ os_memcpy(buf + kde_len, wpabuf_head(conf->eapol_m1_elements),
+ wpabuf_len(conf->eapol_m1_elements));
+ kde_len += wpabuf_len(conf->eapol_m1_elements);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
key_info |= WPA_KEY_INFO_SECURE;
wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
+ os_free(buf);
}
@@ -3178,7 +3339,7 @@
/* MLD MAC address must be the same */
if (!kde->mac_addr ||
- os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde->mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
return -1;
}
@@ -3205,8 +3366,8 @@
return -1;
}
- if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sm->mld_links[i].peer_addr,
+ kde->mlo_link[i] + 1)) {
wpa_printf(MSG_DEBUG,
"RSN: MLD: invalid MAC address=" MACSTR
" expected " MACSTR " (link ID %u)",
@@ -3240,7 +3401,7 @@
size_t pmk_len;
int ft;
const u8 *eapol_key_ie, *key_data, *mic;
- u16 key_data_length;
+ u16 key_info, ver, key_data_length;
size_t mic_len, eapol_key_ie_len;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
@@ -3250,6 +3411,8 @@
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN_MAX];
size_t key_len;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = false;
@@ -3357,12 +3520,46 @@
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key) - mic_len - 2)
goto out;
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ !wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO,
+ "Unsupported EAPOL-Key Key Data field encryption");
+ goto out;
+ }
+
+ if (key_data_length < 8 || key_data_length % 8) {
+ wpa_printf(MSG_INFO,
+ "RSN: Unsupported AES-WRAP len %u",
+ key_data_length);
+ goto out;
+ }
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ key_data_buf_len = key_data_length;
+ if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
+ key_data, key_data_buf)) {
+ bin_clear_free(key_data_buf, key_data_buf_len);
+ wpa_printf(MSG_INFO,
+ "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+ goto out;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
@@ -3507,27 +3704,6 @@
return;
}
-#ifdef CONFIG_IEEE80211R_AP
- if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
- /*
- * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
- * with the value we derived.
- */
- if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
- WPA_PMK_NAME_LEN) != 0) {
- wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "PMKR1Name mismatch in FT 4-way handshake");
- wpa_hexdump(MSG_DEBUG,
- "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
- sm->pmk_r1_name, WPA_PMK_NAME_LEN);
- goto out;
- }
- }
-#endif /* CONFIG_IEEE80211R_AP */
-
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
@@ -3562,6 +3738,7 @@
out:
forced_memzero(pmk_r0, sizeof(pmk_r0));
forced_memzero(pmk_r1, sizeof(pmk_r1));
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -3575,14 +3752,18 @@
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
size_t len = 0;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
if (sm->mgmt_frame_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
+
+ if (wpa_auth->conf.tx_bss_auth)
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
return len;
@@ -3595,7 +3776,8 @@
struct wpa_bigtk_kde bigtk;
struct wpa_group *gsm = sm->group;
u8 rsc[WPA_KEY_RSC_LEN];
- struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
@@ -3627,7 +3809,14 @@
NULL, 0);
forced_memzero(&igtk, sizeof(igtk));
- if (!conf->beacon_prot)
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ conf = &wpa_auth->conf;
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ gsm = wpa_auth->group;
+ }
+
+ if (!sm->wpa_auth->conf.beacon_prot)
return pos;
bigtk.keyid[0] = gsm->GN_bigtk;
@@ -3785,6 +3974,11 @@
if (!beacon_prot)
return;
+ if (a->conf.tx_bss_auth) {
+ a = a->conf.tx_bss_auth;
+ gsm = a->group;
+ }
+
info->bigtkidx = gsm->GN_bigtk;
info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3807,6 +4001,7 @@
static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_group *gsm = sm->group;
size_t gtk_len = gsm->GTK_len;
size_t igtk_len;
@@ -3825,10 +4020,15 @@
return kde_len;
/* MLO IGTK KDE for each link */
- igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
- if (!sm->wpa_auth->conf.beacon_prot)
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+ }
+
+ if (!wpa_auth->conf.beacon_prot)
return kde_len;
/* MLO BIGTK KDE for each link */
@@ -3865,7 +4065,8 @@
/* Add MLO GTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].gtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
@@ -3897,7 +4098,8 @@
/* Add MLO IGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
@@ -3936,7 +4138,9 @@
/* Add MLO BIGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].bigtk ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
@@ -4233,6 +4437,11 @@
kde_len += wpa_auth_ml_kdes_len(sm);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements)
+ kde_len += wpabuf_len(conf->eapol_m3_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
@@ -4248,7 +4457,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
@@ -4347,6 +4556,17 @@
pos = wpa_auth_ml_kdes(sm, pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements) {
+ os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
+ wpabuf_len(conf->eapol_m3_elements));
+ pos += wpabuf_len(conf->eapol_m3_elements);
+ }
+
+ if (conf->eapol_m3_no_encrypt)
+ encr = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
@@ -4398,7 +4618,7 @@
/* MLD MAC address must be the same */
if (!kde.mac_addr ||
- os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde.mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
return -1;
@@ -4732,7 +4952,8 @@
return;
kde = pos = kde_buf;
- wpa_auth_ml_group_kdes(sm, pos);
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+ kde_len = pos - kde_buf;
}
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -4912,19 +5133,30 @@
group->IGTK[group->GN_igtk - 4], len);
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- conf->beacon_prot) {
- len = wpa_cipher_key_len(conf->group_mgmt_cipher);
- os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
- inc_byte_array(group->Counter, WPA_NONCE_LEN);
- if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
- wpa_auth->addr, group->GNonce,
- group->BIGTK[group->GN_bigtk - 6], len) < 0)
- ret = -1;
- wpa_hexdump_key(MSG_DEBUG, "BIGTK",
- group->BIGTK[group->GN_bigtk - 6], len);
+ if (!wpa_auth->non_tx_beacon_prot &&
+ conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ return ret;
+ if (!conf->beacon_prot)
+ return ret;
+
+ if (wpa_auth->conf.tx_bss_auth) {
+ group = wpa_auth->conf.tx_bss_auth->group;
+ if (group->bigtk_set)
+ return ret;
+ wpa_printf(MSG_DEBUG, "Set up BIGTK for TX BSS");
}
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->BIGTK[group->GN_bigtk - 6], len) < 0)
+ return -1;
+ group->bigtk_set = true;
+ wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+ group->BIGTK[group->GN_bigtk - 6], len);
+
return ret;
}
@@ -5085,9 +5317,10 @@
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
u8 *start = pos;
- size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ size_t len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/*
* BIGTK subelement:
@@ -5097,7 +5330,7 @@
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+ if (wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
return 0;
pos += 6;
@@ -5187,12 +5420,21 @@
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
- if (ret == 0 && conf->beacon_prot &&
- wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ if (ret || !conf->beacon_prot)
+ return ret;
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ group = wpa_auth->group;
+ if (!group->bigtk_set || group->bigtk_configured)
+ return ret;
+ }
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
broadcast_ether_addr, group->GN_bigtk,
group->BIGTK[group->GN_bigtk - 6], len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
+ else
+ group->bigtk_configured = true;
}
return ret;
@@ -5337,9 +5579,11 @@
tmp = group->GM_igtk;
group->GM_igtk = group->GN_igtk;
group->GN_igtk = tmp;
- tmp = group->GM_bigtk;
- group->GM_bigtk = group->GN_bigtk;
- group->GN_bigtk = tmp;
+ if (!wpa_auth->conf.tx_bss_auth) {
+ tmp = group->GM_bigtk;
+ group->GM_bigtk = group->GN_bigtk;
+ group->GN_bigtk = tmp;
+ }
wpa_gtk_update(wpa_auth, group);
wpa_group_config_group_keys(wpa_auth, group);
}
@@ -5689,28 +5933,11 @@
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp)
-{
- if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
- NULL, 0, wpa_auth->addr, addr, session_timeout,
- NULL, akmp))
- return 0;
-
- return -1;
-}
-
-
-int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp, const u8 *dpp_pkhash)
{
struct rsn_pmksa_cache_entry *entry;
- if (wpa_auth->conf.disable_pmksa_caching)
+ if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
@@ -5834,13 +6061,14 @@
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
- u8 *pmkid, u8 *pmk)
+ u8 *pmkid, u8 *pmk, size_t *pmk_len)
{
if (!sm)
return;
sm->pmksa = pmksa;
- os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+ os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len);
+ *pmk_len = pmksa->pmk_len;
os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
}
@@ -6358,7 +6586,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 57fda8a..4f6bb76 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -244,6 +244,9 @@
unsigned int skip_send_eapol:1;
unsigned int enable_eapol_large_timeout:1;
bool delay_eapol_tx;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -279,6 +282,11 @@
bool force_kdk_derivation;
bool radius_psk;
+
+ /* Pointer to Multi-BSSID transmitted BSS authenticator instance.
+ * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
+ * and in BSSs that are not part of a Multi-BSSID set. */
+ struct wpa_authenticator *tx_bss_auth;
};
typedef enum {
@@ -479,9 +487,6 @@
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp);
-int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp, const u8 *dpp_pkhash);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
@@ -507,7 +512,7 @@
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
- u8 *pmkid, u8 *pmk);
+ u8 *pmkid, u8 *pmk, size_t *pmk_len);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 4b16f62..7744ed6 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1427,7 +1427,7 @@
os_get_reltime(&now);
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r0->spa, spa) &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
*r0_out = r0;
@@ -1522,7 +1522,7 @@
os_get_reltime(&now);
dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
- if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r1->spa, spa) &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
@@ -2024,7 +2024,7 @@
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
- if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0kh->addr, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG,
"FT: R0KH-ID points to self - no matching key available");
return -1;
@@ -2366,7 +2366,8 @@
static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
size_t subelem_len;
const u8 *kek, *bigtk;
size_t kek_len;
@@ -2381,7 +2382,7 @@
kek_len = sm->PTK.kek_len;
}
- bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ bigtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
* Key[16+8] */
@@ -2395,7 +2396,7 @@
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
+ wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3765,7 +3766,7 @@
" Target AP=" MACSTR " Action=%d)",
MAC2STR(sta_addr), MAC2STR(target_ap), action);
- if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta_addr, sm->addr)) {
wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
"STA=" MACSTR " STA-Address=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sta_addr));
@@ -3778,7 +3779,7 @@
* APs in the MD (if such a list were configured).
*/
if ((target_ap[0] & 0x01) ||
- os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(target_ap, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
"frame");
return -1;
@@ -4036,7 +4037,7 @@
seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype, no_defer);
if (!no_defer && r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4203,7 +4204,7 @@
cb ? 0 : 1);
}
if (cb && r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4357,7 +4358,7 @@
struct ft_get_sta_ctx *info = ctx;
if ((info->s1kh_id &&
- os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ !ether_addr_equal(info->s1kh_id, sm->addr)) ||
os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
FT_RRB_NONCE_LEN) != 0 ||
sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
@@ -4482,7 +4483,7 @@
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh, &r0kh_wildcard);
if (!r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
goto out;
@@ -4500,7 +4501,7 @@
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
&r1kh_wildcard);
if (!r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
f_r1kh_id, FT_R1KH_ID_LEN);
goto out;
@@ -4806,7 +4807,7 @@
return -1;
}
- if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(target_ap_addr, wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
"RRB Request does not match with own "
"address");
@@ -4969,7 +4970,7 @@
return;
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0->spa, addr)) {
r0found = r0;
break;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 82d79f2..b286a77 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -183,8 +183,15 @@
wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
wconf->oci_freq_override_fils_assoc =
conf->oci_freq_override_fils_assoc;
+
wconf->skip_send_eapol = iconf->skip_send_eapol;
wconf->enable_eapol_large_timeout = iconf->enable_eapol_large_timeout;
+
+ if (conf->eapol_m1_elements)
+ wconf->eapol_m1_elements = wpabuf_dup(conf->eapol_m1_elements);
+ if (conf->eapol_m3_elements)
+ wconf->eapol_m3_elements = wpabuf_dup(conf->eapol_m3_elements);
+ wconf->eapol_m3_no_encrypt = conf->eapol_m3_no_encrypt;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -556,7 +563,8 @@
if (sta) {
flags = hostapd_sta_flags_to_drv(sta->flags);
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+ if (ap_sta_is_mld(hapd, sta) &&
+ (sta->flags & WLAN_STA_AUTHORIZED))
link_id = -1;
#endif /* CONFIG_IEEE80211BE */
}
@@ -669,7 +677,7 @@
hapd = iface->bss[j];
if (hapd == idata->src_hapd ||
!hapd->wpa_auth ||
- os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst))
continue;
wpa_printf(MSG_DEBUG,
@@ -859,7 +867,7 @@
MOBILITY_DOMAIN_ID_LEN) != 0)
continue; /* no matching FT SSID/mobility domain */
if (!is_multicast_ether_addr(idata->dst_addr) &&
- os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst_addr))
continue; /* destination address does not match */
/* defer eth_p_oui_deliver until next eloop step as this is
@@ -1157,17 +1165,25 @@
if (!sta || !sta->wpa_sm)
return -1;
- if (vlan->notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from FT",
- vlan->untagged, vlan->tagged[0] ? "+" : "");
- return -1;
- }
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ?
+ "+" : "");
+ return -1;
+ }
- if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
- return -1;
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+
+ } else {
+ if (vlan->notempty)
+ sta->vlan_id = vlan->untagged;
+ }
/* Configure wpa_group for GTK but ignore error due to driver not
* knowing this STA. */
ap_sta_bind_vlan(hapd, sta);
@@ -1190,10 +1206,15 @@
if (!sta)
return -1;
- if (sta->vlan_desc)
+ if (sta->vlan_desc) {
*vlan = *sta->vlan_desc;
- else
+ } else if ((hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+ sta->vlan_id) {
+ vlan->notempty = 1;
+ vlan->untagged = sta->vlan_id;
+ } else {
os_memset(vlan, 0, sizeof(*vlan));
+ }
return 0;
}
@@ -1396,7 +1417,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
if (!is_multicast_ether_addr(ethhdr->h_dest) &&
- os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, ethhdr->h_dest))
return;
wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
len - sizeof(*ethhdr));
@@ -1412,7 +1433,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
if (!is_multicast_ether_addr(dst_addr) &&
- os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, dst_addr))
return;
wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
len);
@@ -1540,7 +1561,8 @@
if (!iface->bss[0]->conf->mld_ap ||
hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
+ link_id != iface->bss[0]->mld_link_id ||
+ !iface->bss[0]->wpa_auth)
continue;
wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
@@ -1582,7 +1604,8 @@
if (!iface->bss[0]->conf->mld_ap ||
hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
+ link_id != iface->bss[0]->mld_link_id ||
+ !iface->bss[0]->wpa_auth)
continue;
wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
@@ -1675,9 +1698,13 @@
};
const u8 *wpa_ie;
size_t wpa_ie_len;
+ struct hostapd_data *tx_bss;
hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
_conf.msg_ctx = hapd->msg_ctx;
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ if (tx_bss != hapd)
+ _conf.tx_bss_auth = tx_bss->wpa_auth;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 74ae5ad..9ba8304 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -134,8 +134,6 @@
* Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
size_t r0kh_id_len;
- u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
- * message 2/4 */
u8 *assoc_resp_ftie;
void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
@@ -222,6 +220,8 @@
u8 BIGTK[2][WPA_IGTK_MAX_LEN];
int GN_igtk, GM_igtk;
int GN_bigtk, GM_bigtk;
+ bool bigtk_set;
+ bool bigtk_configured;
/* Number of references except those in struct wpa_group->next */
unsigned int references;
unsigned int num_setup_iface;
@@ -257,6 +257,8 @@
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+ bool non_tx_beacon_prot;
+
#ifdef CONFIG_P2P
struct bitfield *ip_pool;
#endif /* CONFIG_P2P */
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index aacfa33..82d4d5f 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -288,7 +288,7 @@
any_psk = wpa_psk->psk;
if (mac_addr && !dev_psk &&
- os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mac_addr, wpa_psk->addr)) {
dev_psk = wpa_psk->psk;
break;
}