Snap for 11237490 from 2a14cf101bb80f154b2b3bcdc60ce6976672f180 to 24Q2-release
Change-Id: Iab7f1aaac7eafdaca480d189f09b95884d45ee75
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 580e41c..bfb152e 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -667,6 +667,10 @@
val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SHA384
+ else if (os_strcmp(start, "WPA-EAP-SHA384") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
val |= WPA_KEY_MGMT_PSK_SHA256;
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
@@ -2603,6 +2607,8 @@
} else if (os_strcmp(buf, "imsi_privacy_key") == 0) {
os_free(bss->imsi_privacy_key);
bss->imsi_privacy_key = os_strdup(pos);
+ } else if (os_strcmp(buf, "eap_sim_aka_fast_reauth_limit") == 0) {
+ bss->eap_sim_aka_fast_reauth_limit = atoi(pos);
#endif /* EAP_SERVER_SIM */
#ifdef EAP_SERVER_TNC
} else if (os_strcmp(buf, "tnc") == 0) {
@@ -4776,6 +4782,16 @@
return 1;
}
conf->punct_acs_threshold = val;
+ } else if (os_strcmp(buf, "mld_ap") == 0) {
+ bss->mld_ap = !!atoi(pos);
+ } else if (os_strcmp(buf, "mld_id") == 0) {
+ bss->mld_id = atoi(pos);
+ } else if (os_strcmp(buf, "mld_addr") == 0) {
+ if (hwaddr_aton(pos, bss->mld_addr)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+ line);
+ return 1;
+ }
#endif /* CONFIG_IEEE80211BE */
} else {
wpa_printf(MSG_ERROR,
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index b46d921..57c9997 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -956,6 +956,14 @@
pos += ret;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_SHA384
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA384 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
if (pos > buf && *(pos - 1) == ' ') {
*(pos - 1) = '\0';
@@ -1840,6 +1848,7 @@
int enabled = atoi(cmd);
char *pos;
const char *ifname;
+ const u8 *addr = hapd->own_addr;
if (!enabled) {
if (hapd->l2_test) {
@@ -1860,7 +1869,11 @@
else
ifname = hapd->conf->iface;
- hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+ hapd->l2_test = l2_packet_init(ifname, addr,
ETHERTYPE_IP, hostapd_data_test_rx,
hapd, 1);
if (hapd->l2_test == NULL)
@@ -2714,6 +2727,155 @@
}
+static u8 hostapd_maxnss(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u8 *mcs_set = NULL;
+ u16 mcs_map;
+ u8 ht_rx_nss = 0;
+ u8 vht_rx_nss = 1;
+ u8 mcs;
+ bool ht_supported = false;
+ bool vht_supported = false;
+ int i;
+
+ if (sta->ht_capabilities && (sta->flags & WLAN_STA_HT)) {
+ mcs_set = sta->ht_capabilities->supported_mcs_set;
+ ht_supported = true;
+ }
+
+ if (sta->vht_capabilities && (sta->flags & WLAN_STA_VHT)) {
+ mcs_map = le_to_host16(
+ sta->vht_capabilities->vht_supported_mcs_set.rx_map);
+ vht_supported = true;
+ }
+
+ if (ht_supported && mcs_set) {
+ if (mcs_set[0])
+ ht_rx_nss++;
+ if (mcs_set[1])
+ ht_rx_nss++;
+ if (mcs_set[2])
+ ht_rx_nss++;
+ if (mcs_set[3])
+ ht_rx_nss++;
+ }
+ if (vht_supported) {
+ for (i = 7; i >= 0; i--) {
+ mcs = (mcs_map >> (2 * i)) & 0x03;
+ if (mcs != 0x03) {
+ vht_rx_nss = i + 1;
+ break;
+ }
+ }
+ }
+
+ return ht_rx_nss > vht_rx_nss ? ht_rx_nss : vht_rx_nss;
+}
+
+
+static char hostapd_ctrl_iface_notify_cw_htaction(struct hostapd_data *hapd,
+ const u8 *addr, u8 width)
+{
+ u8 buf[3];
+ char ret;
+
+ width = width >= 1 ? 1 : 0;
+
+ buf[0] = WLAN_ACTION_HT;
+ buf[1] = WLAN_HT_ACTION_NOTIFY_CHANWIDTH;
+ buf[2] = width;
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ buf, sizeof(buf));
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Notify Channel Width frame to "
+ MACSTR, MAC2STR(addr));
+
+ return ret;
+}
+
+
+static char hostapd_ctrl_iface_notify_cw_vhtaction(struct hostapd_data *hapd,
+ const u8 *addr, u8 width)
+{
+ u8 buf[3];
+ char ret;
+
+ buf[0] = WLAN_ACTION_VHT;
+ buf[1] = WLAN_VHT_ACTION_OPMODE_NOTIF;
+ buf[2] = width;
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ buf, sizeof(buf));
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Opeating Mode Notification frame to "
+ MACSTR, MAC2STR(addr));
+
+ return ret;
+}
+
+
+static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 cw, operating_mode = 0, nss;
+ struct sta_info *sta;
+ enum hostapd_hw_mode hw_mode;
+
+ if (is_6ghz_freq(hapd->iface->freq)) {
+ wpa_printf(MSG_ERROR, "20/40 BSS coex not supported in 6 GHz");
+ return -1;
+ }
+
+ cw = atoi(cmd);
+ hw_mode = hapd->iface->current_mode->mode;
+ if ((hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B) &&
+ !(cw == 0 || cw == 1)) {
+ wpa_printf(MSG_ERROR,
+ "Channel width should be either 20 MHz or 40 MHz for 2.4 GHz band");
+ return -1;
+ }
+
+ switch (cw) {
+ case 0:
+ operating_mode = 0;
+ break;
+ case 1:
+ operating_mode = VHT_OPMODE_CHANNEL_40MHZ;
+ break;
+ case 2:
+ operating_mode = VHT_OPMODE_CHANNEL_80MHZ;
+ break;
+ case 3:
+ operating_mode = VHT_OPMODE_CHANNEL_160MHZ;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "Channel width should be between 0 to 3");
+ return -1;
+ }
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ nss = hostapd_maxnss(hapd, sta) - 1;
+ hostapd_ctrl_iface_notify_cw_vhtaction(hapd, sta->addr,
+ operating_mode |
+ (u8) (nss << 4));
+ continue;
+ }
+
+ if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) ==
+ WLAN_STA_HT && sta->ht_capabilities)
+ hostapd_ctrl_iface_notify_cw_htaction(hapd, sta->addr,
+ cw);
+ }
+
+ return 0;
+}
+
+
static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
int reply_size, const char *param)
{
@@ -3560,6 +3722,9 @@
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
reply_len = -1;
+ } else if (os_strncmp(buf, "NOTIFY_CW_CHANGE ", 17) == 0) {
+ if (hostapd_ctrl_iface_notify_cw_change(hapd, buf + 17))
+ reply_len = -1;
} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
reply_size);
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 6a41dcf..0a5ceee 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -415,7 +415,6 @@
# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol
# design is still subject to change. As such, this should not yet be enabled in
# production use.
-# This requires CONFIG_IEEE80211W=y to be enabled, too.
#CONFIG_PASN=y
# Device Provisioning Protocol (DPP) (also known as Wi-Fi Easy Connect)
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 5868bfd..f02cd92 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1040,6 +1040,20 @@
# Default is 0, indicates that ACS algorithm should not puncture any channel.
#punct_acs_threshold=75
+# AP MLD - Whether this AP is a part of an AP MLD
+# 0 = no (no MLO)
+# 1 = yes (MLO)
+#mld_ap=0
+
+# MLD ID - Affiliated MLD ID
+#mld_id=1
+
+# AP MLD MAC address
+# The configured address will be set as the interface hardware address and used
+# as the AP MLD MAC address. If not set, the current interface hardware address
+# will be used as the AP MLD MAC address.
+#mld_addr=02:03:04:05:06:07
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
@@ -1451,12 +1465,25 @@
# 1 = use pseudonyms, but not fast reauthentication
# 2 = do not use pseudonyms, but use fast reauthentication
# 3 = use pseudonyms and use fast reauthentication (default)
+# 4 = do not use pseudonyms or fast reauthentication and allow
+# EAP-Response/Identity to be used without method specific identity exchange
+# 5 = use pseudonyms, but not fast reauthentication and allow
+# EAP-Response/Identity to be used without method specific identity exchange
+# 6 = do not use pseudonyms, but use fast reauthentication and allow
+# EAP-Response/Identity to be used without method specific identity exchange
+# 7 = use pseudonyms and use fast reauthentication and allow
+# EAP-Response/Identity to be used without method specific identity exchange
#eap_sim_id=3
# IMSI privacy key (PEM encoded RSA 2048-bit private key) for decrypting
# permanent identity when using EAP-SIM/AKA/AKA'.
#imsi_privacy_key=imsi-privacy-key.pem
+# EAP-SIM and EAP-AKA fast re-authentication limit
+# Maximum number of fast re-authentications allowed after each full
+# authentication.
+#eap_sim_aka_fast_reauth_limit=1000
+
# Trusted Network Connect (TNC)
# If enabled, TNC validation will be required before the peer is allowed to
# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 646dfc5..45497cd 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1207,6 +1207,13 @@
}
+static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "NOTIFY_CW_CHANGE", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1386,6 +1393,13 @@
}
+static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DRIVER_FLAGS2");
+}
+
+
#ifdef CONFIG_DPP
static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
@@ -1679,6 +1693,8 @@
"<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
" [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
" = initiate channel switch announcement" },
+ { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
+ "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
"<addr> <url>\n"
" = send WNM-Notification Subscription Remediation Request" },
@@ -1719,6 +1735,8 @@
" = send FTM range request"},
{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
" = show supported driver flags"},
+ { "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL,
+ " = show supported driver flags2"},
#ifdef CONFIG_DPP
{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
"report a scanned DPP URI from a QR Code" },
diff --git a/hostapd/main.c b/hostapd/main.c
index 18b2dd9..615dc2f 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -166,8 +166,61 @@
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
+ struct hostapd_iface *h = iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (h == iface) {
+ wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
+ continue;
+ }
+
+ if (!hconf->mld_ap || hconf->mld_id != conf->mld_id) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip non matching mld_id");
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
+ if (!h_hapd->drv_priv) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Matching MLD BSS not initialized yet");
+ continue;
+ }
+
+ hapd->drv_priv = h_hapd->drv_priv;
+
+ /*
+ * All interfaces participating in the AP MLD would have
+ * the same MLD address, which is the interface hardware
+ * address, while the interface address would be
+ * derived from the original interface address if BSSID
+ * is not configured, and otherwise it would be the
+ * configured BSSID.
+ */
+ os_memcpy(hapd->mld_addr, h_hapd->mld_addr, ETH_ALEN);
+ if (is_zero_ether_addr(b)) {
+ os_memcpy(hapd->own_addr, h_hapd->mld_addr, ETH_ALEN);
+ random_mac_addr_keep_oui(hapd->own_addr);
+ } else {
+ os_memcpy(hapd->own_addr, b, ETH_ALEN);
+ }
+
+ /*
+ * Mark the interface as a secondary interface, as this
+ * is needed for the de-initialization flow
+ */
+ hapd->mld_first_bss = h_hapd;
+ hapd->mld_link_id = hapd->mld_first_bss->mld_next_link_id++;
+
+ goto setup_mld;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
/* Initialize the driver interface */
- if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
+ if (is_zero_ether_addr(b))
b = NULL;
os_memset(¶ms, 0, sizeof(params));
@@ -191,6 +244,15 @@
break;
}
params.bssid = b;
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Use the configured MLD MAC address as the interface hardware address
+ * if this AP is a part of an AP MLD.
+ */
+ if (!is_zero_ether_addr(hapd->conf->mld_addr) && hapd->conf->mld_ap)
+ params.bssid = hapd->conf->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
params.ifname = hapd->conf->iface;
params.driver_params = hapd->iconf->driver_params;
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
@@ -216,6 +278,26 @@
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * This is the first interface added to the AP MLD, so have the
+ * interface hardware address be the MLD address, while the link address
+ * would be derived from the original interface address if BSSID is not
+ * configured, and otherwise it would be the configured BSSID.
+ */
+ if (hapd->conf->mld_ap) {
+ os_memcpy(hapd->mld_addr, hapd->own_addr, ETH_ALEN);
+ hapd->mld_next_link_id = 0;
+ hapd->mld_link_id = hapd->mld_next_link_id++;
+ if (!b)
+ random_mac_addr_keep_oui(hapd->own_addr);
+ else
+ os_memcpy(hapd->own_addr, b, ETH_ALEN);
+ }
+
+setup_mld:
+#endif /* CONFIG_IEEE80211BE */
+
if (hapd->driver->get_capa &&
hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
struct wowlan_triggers *triggs;
@@ -237,6 +319,8 @@
*/
hostapd_get_ext_capa(iface);
+ hostapd_get_mld_capa(iface);
+
triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
if (triggs && hapd->driver->set_wowlan) {
if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
@@ -248,6 +332,25 @@
iface->ema_max_periodicity = capa.ema_max_periodicity;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) {
+ wpa_printf(MSG_INFO,
+ "MLD: Not supported by the driver");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set link_id=%u, mld_addr=" MACSTR
+ ", own_addr=" MACSTR,
+ hapd->mld_link_id, MAC2STR(hapd->mld_addr),
+ MAC2STR(hapd->own_addr));
+
+ hostapd_drv_link_add(hapd, hapd->mld_link_id,
+ hapd->own_addr);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return 0;
}
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 1181c7d..e3cfe1d 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -256,7 +256,7 @@
static const struct bw_item bw_40[] = {
{ 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
{ 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
- { 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+ { 5580, 5600, 118 }, { 5620, 5640, 126 }, { 5660, 5680, 134 },
{ 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
{ 5825, 5845, 167 }, { 5865, 5885, 175 },
{ 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
@@ -1076,12 +1076,6 @@
return ideal_chan;
}
-#ifdef CONFIG_IEEE80211BE
- if (iface->conf->punct_acs_threshold)
- wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
- ideal_chan->punct_bitmap);
-#endif /* CONFIG_IEEE80211BE */
-
return rand_chan;
}
@@ -1353,12 +1347,20 @@
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
+ int err;
+
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
- if (hostapd_drv_do_acs(iface->bss[0]))
+
+ err = hostapd_drv_do_acs(iface->bss[0]);
+ if (err) {
+ if (err == 1)
+ return HOSTAPD_CHAN_INVALID_NO_IR;
return HOSTAPD_CHAN_INVALID;
+ }
+
return HOSTAPD_CHAN_ACS;
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index c3ee506..60d3566 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -90,6 +90,7 @@
bss->radius_server_auth_port = 1812;
bss->eap_sim_db_timeout = 1;
bss->eap_sim_id = 3;
+ bss->eap_sim_aka_fast_reauth_limit = 1000;
bss->ap_max_inactivity = AP_MAX_INACTIVITY;
bss->eapol_version = EAPOL_VERSION;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index def5fd5..99a6d18 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -448,6 +448,7 @@
int eap_sim_aka_result_ind;
int eap_sim_id;
char *imsi_privacy_key;
+ int eap_sim_aka_fast_reauth_limit;
int tnc;
int fragment_size;
u16 pwd_group;
@@ -937,6 +938,17 @@
u8 rnr;
char *config_id;
bool xrates_supported;
+
+#ifdef CONFIG_IEEE80211BE
+ /* The AP is part of an AP MLD */
+ u8 mld_ap;
+
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+
+ /* The AP's MLD MAC address within the AP MLD */
+ u8 mld_addr[ETH_ALEN];
+#endif /* CONFIG_IEEE80211BE */
};
/**
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index aa4dbe9..8f9cc5b 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -430,7 +430,7 @@
size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
- int set)
+ int set, const u8 *link_addr, bool mld_link_sta)
{
struct hostapd_sta_add_params params;
@@ -460,6 +460,19 @@
params.support_p2p_ps = supp_p2p_ps;
params.set = set;
params.mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * An AP MLD needs to always specify to what link the station needs
+ * to be added.
+ */
+ if (hapd->conf->mld_ap) {
+ params.mld_link_id = hapd->mld_link_id;
+ params.mld_link_addr = link_addr;
+ params.mld_link_sta = mld_link_sta;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
}
@@ -540,12 +553,12 @@
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int idx, u8 *seq)
+ const u8 *addr, int idx, int link_id, u8 *seq)
{
if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
return 0;
return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
- seq);
+ link_id, seq);
}
@@ -584,6 +597,17 @@
return 0;
if (hapd->driver->set_freq == NULL)
return 0;
+
+ data.link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ data.link_id = hapd->mld_link_id;
+ wpa_printf(MSG_DEBUG,
+ "hostapd_set_freq: link_id=%d", data.link_id);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->set_freq(hapd->drv_priv, &data);
}
@@ -635,10 +659,19 @@
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
+ int link_id = -1;
+
if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
return 0;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
- cw_min, cw_max, burst_time);
+ cw_min, cw_max, burst_time,
+ link_id);
}
@@ -646,8 +679,8 @@
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags, u8 *dfs_domain)
{
- if (hapd->driver == NULL ||
- hapd->driver->get_hw_feature_data == NULL)
+ if (!hapd->driver || !hapd->driver->get_hw_feature_data ||
+ !hapd->drv_priv)
return NULL;
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
flags, dfs_domain);
@@ -727,6 +760,11 @@
params.key_flag = key_flag;
params.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && !(key_flag & KEY_FLAG_PAIRWISE))
+ params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->set_key(hapd->drv_priv, ¶ms);
}
@@ -736,20 +774,35 @@
const u16 *csa_offs, size_t csa_offs_len,
int no_encrypt)
{
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
return 0;
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
- csa_offs, csa_offs_len, no_encrypt, 0);
+ csa_offs, csa_offs_len, no_encrypt, 0,
+ link_id);
}
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
const u8 *addr, int reason)
{
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#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,
- reason);
+ reason, link_id);
}
@@ -889,6 +942,7 @@
int **freq_list)
{
int i;
+ bool is_no_ir = false;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
@@ -917,7 +971,12 @@
(chan->flag & HOSTAPD_CHAN_RADAR)) &&
!(chan->max_tx_power < hapd->iface->conf->min_tx_power))
int_array_add_unique(freq_list, chan->freq);
+ else if ((chan->flag & HOSTAPD_CHAN_NO_IR) &&
+ is_6ghz_freq(chan->freq))
+ is_no_ir = true;
}
+
+ hapd->iface->is_no_ir = is_no_ir;
}
@@ -935,6 +994,24 @@
}
+void hostapd_get_mld_capa(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+
+ if (!hapd->driver || !hapd->driver->get_mld_capab)
+ return;
+
+ hapd->driver->get_mld_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+ &iface->mld_eml_capa,
+ &iface->mld_mld_capa);
+}
+
+
+/**
+ * hostapd_drv_do_acs - Start automatic channel selection
+ * @hapd: BSS data for the device initiating ACS
+ * Returns: 0 on success, -1 on failure, 1 on failure due to NO_IR (AFC)
+ */
int hostapd_drv_do_acs(struct hostapd_data *hapd)
{
struct drv_acs_params params;
@@ -972,6 +1049,12 @@
false, &freq_list);
}
+ if (!freq_list && hapd->iface->is_no_ir) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Interface freq_list is empty. Failing do_acs.");
+ return 1;
+ }
+
params.freq_list = freq_list;
params.edmg_enabled = hapd->iface->conf->enable_edmg;
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 023cbf1..331b0ea 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -47,7 +47,7 @@
size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
- int set);
+ int set, const u8 *link_addr, bool mld_link_sta);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
@@ -62,7 +62,7 @@
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
struct wpa_bss_params *params);
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int idx, u8 *seq);
+ const u8 *addr, int idx, int link_id, u8 *seq);
int hostapd_flush(struct hostapd_data *hapd);
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int freq, int channel, int edmg, u8 edmg_channel,
@@ -155,6 +155,7 @@
u8 qos_map_set_len);
void hostapd_get_ext_capa(struct hostapd_iface *iface);
+void hostapd_get_mld_capa(struct hostapd_iface *iface);
void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
struct hostapd_hw_modes *mode,
@@ -172,12 +173,13 @@
static inline int hostapd_drv_set_sta_vlan(const char *ifname,
struct hostapd_data *hapd,
- const u8 *addr, int vlan_id)
+ const u8 *addr, int vlan_id,
+ int link_id)
{
if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
return 0;
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
- vlan_id);
+ vlan_id, link_id);
}
static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
@@ -199,13 +201,13 @@
static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- u32 flags)
+ u32 flags, int link_id)
{
if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
return 0;
return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
data_len, encrypt,
- hapd->own_addr, flags);
+ hapd->own_addr, flags, link_id);
}
static inline int hostapd_drv_read_sta_data(
@@ -440,4 +442,16 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_IEEE80211BE
+static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
+ u8 link_id, const u8 *addr)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_add)
+ return -1;
+
+ return hapd->driver->link_add(hapd->drv_priv, link_id, addr);
+
+}
+#endif /* CONFIG_IEEE80211BE */
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 4ab2a4a..1488dcc 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -106,6 +106,15 @@
{
struct radius_server_conf srv;
struct hostapd_bss_config *conf = hapd->conf;
+
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using RADIUS server of the first BSS");
+
+ hapd->radius_srv = hapd->mld_first_bss->radius_srv;
+ return 0;
+ }
+
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
srv.auth_port = conf->radius_server_auth_port;
@@ -215,6 +224,8 @@
cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
cfg->eap_sim_id = hapd->conf->eap_sim_id;
cfg->imsi_privacy_key = hapd->imsi_privacy_key;
+ cfg->eap_sim_aka_fast_reauth_limit =
+ hapd->conf->eap_sim_aka_fast_reauth_limit;
cfg->tnc = hapd->conf->tnc;
cfg->wps = hapd->wps;
cfg->fragment_size = hapd->conf->fragment_size;
@@ -238,6 +249,19 @@
int authsrv_init(struct hostapd_data *hapd)
{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
+
+#ifdef EAP_TLS_FUNCS
+ hapd->ssl_ctx = hapd->mld_first_bss->ssl_ctx;
+#endif /* EAP_TLS_FUNCS */
+ hapd->eap_cfg = hapd->mld_first_bss->eap_cfg;
+#ifdef EAP_SIM_DB
+ hapd->eap_sim_db_priv = hapd->mld_first_bss->eap_sim_db_priv;
+#endif /* EAP_SIM_DB */
+ return 0;
+ }
+
#ifdef EAP_TLS_FUNCS
if (hapd->conf->eap_server &&
(hapd->conf->ca_cert || hapd->conf->server_cert ||
@@ -352,6 +376,21 @@
void authsrv_deinit(struct hostapd_data *hapd)
{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Deinit auth_serv of a non-first BSS");
+
+ hapd->radius_srv = NULL;
+ hapd->eap_cfg = NULL;
+#ifdef EAP_SIM_DB
+ hapd->eap_sim_db_priv = NULL;
+#endif /* EAP_SIM_DB */
+#ifdef EAP_TLS_FUNCS
+ hapd->ssl_ctx = NULL;
+#endif /* EAP_TLS_FUNCS */
+ return;
+ }
+
#ifdef RADIUS_SERVER
radius_server_deinit(hapd->radius_srv);
hapd->radius_srv = NULL;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index de944fe..1b5cea9 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -17,6 +17,7 @@
#include "common/ieee802_11_common.h"
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
+#include "crypto/sha1.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -87,6 +88,12 @@
static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
{
+ enum hostapd_hw_mode hw_mode = hapd->iconf->hw_mode;
+
+ if (hw_mode != HOSTAPD_MODE_IEEE80211G &&
+ hw_mode != HOSTAPD_MODE_IEEE80211B)
+ return eid;
+
*eid++ = WLAN_EID_DS_PARAMS;
*eid++ = 1;
*eid++ = hapd->iconf->channel;
@@ -471,6 +478,7 @@
size_t len, rnr_len = 0;
u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
+ size_t i;
if (!iface->mbssid_max_interfaces ||
iface->num_bss > iface->mbssid_max_interfaces ||
@@ -478,6 +486,14 @@
!iface->ema_max_periodicity))
goto fail;
+ /* Make sure bss->xrates_supported is set for all BSSs to know whether
+ * it need to be non-inherited. */
+ for (i = 0; i < iface->num_bss; i++) {
+ u8 buf[100];
+
+ hostapd_eid_ext_supp_rates(iface->bss[i], buf);
+ }
+
tx_bss = hostapd_mbssid_get_tx_bss(hapd);
len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
NULL, 0, &rnr_len);
@@ -605,6 +621,14 @@
buflen += 3 + sizeof(struct ieee80211_eht_operation);
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;
}
#endif /* CONFIG_IEEE80211BE */
@@ -755,6 +779,8 @@
#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);
pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_eht_operation(hapd, pos);
}
@@ -1365,10 +1391,128 @@
#ifdef CONFIG_FILS
+static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be)
+ return FD_CAP_PHY_INDEX_EHT;
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
+ return FD_CAP_PHY_INDEX_HE;
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
+ return FD_CAP_PHY_INDEX_VHT;
+#endif /* CONFIG_IEEE80211AC */
+
+ if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
+ return FD_CAP_PHY_INDEX_HT;
+
+ return 0;
+}
+
+
+static u16 hostapd_gen_fils_discovery_nss(struct hostapd_hw_modes *mode,
+ u16 phy_index, u8 he_mcs_nss_size)
+{
+ u16 nss = 0;
+
+ if (!mode)
+ return 0;
+
+ if (phy_index == FD_CAP_PHY_INDEX_HE) {
+ const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
+ int i;
+ u16 mcs[6];
+
+ os_memset(mcs, 0xff, 6 * sizeof(u16));
+
+ if (he_mcs_nss_size == 4) {
+ mcs[0] = WPA_GET_LE16(&he_mcs[0]);
+ mcs[1] = WPA_GET_LE16(&he_mcs[2]);
+ }
+
+ if (he_mcs_nss_size == 8) {
+ mcs[2] = WPA_GET_LE16(&he_mcs[4]);
+ mcs[3] = WPA_GET_LE16(&he_mcs[6]);
+ }
+
+ if (he_mcs_nss_size == 12) {
+ mcs[4] = WPA_GET_LE16(&he_mcs[8]);
+ mcs[5] = WPA_GET_LE16(&he_mcs[10]);
+ }
+
+ for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
+ u16 nss_mask = 0x3 << (i * 2);
+
+ /*
+ * If Tx and/or Rx indicate support for a given NSS,
+ * count it towards the maximum NSS.
+ */
+ if (he_mcs_nss_size == 4 &&
+ (((mcs[0] & nss_mask) != nss_mask) ||
+ ((mcs[1] & nss_mask) != nss_mask))) {
+ nss++;
+ continue;
+ }
+
+ if (he_mcs_nss_size == 8 &&
+ (((mcs[2] & nss_mask) != nss_mask) ||
+ ((mcs[3] & nss_mask) != nss_mask))) {
+ nss++;
+ continue;
+ }
+
+ if (he_mcs_nss_size == 12 &&
+ (((mcs[4] & nss_mask) != nss_mask) ||
+ ((mcs[5] & nss_mask) != nss_mask))) {
+ nss++;
+ continue;
+ }
+ }
+ } else if (phy_index == FD_CAP_PHY_INDEX_EHT) {
+ u8 rx_nss, tx_nss, max_nss = 0, i;
+ u8 *mcs = mode->eht_capab[IEEE80211_MODE_AP].mcs;
+
+ /*
+ * The Supported EHT-MCS And NSS Set field for the AP contains
+ * one to three EHT-MCS Map fields based on the supported
+ * bandwidth. Check the first byte (max NSS for Rx/Tx that
+ * supports EHT-MCS 0-9) for each bandwidth (<= 80,
+ * 160, 320) to find the maximum NSS. This assumes that
+ * the lowest MCS rates support the largest number of spatial
+ * streams. If values are different between Tx, Rx or the
+ * bandwidths, choose the highest value.
+ */
+ for (i = 0; i < 3; i++) {
+ rx_nss = mcs[3 * i] & 0x0F;
+ if (rx_nss > max_nss)
+ max_nss = rx_nss;
+
+ tx_nss = (mcs[3 * i] & 0xF0) >> 4;
+ if (tx_nss > max_nss)
+ max_nss = tx_nss;
+ }
+
+ nss = max_nss;
+ }
+
+ if (nss > 4)
+ return FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
+ if (nss)
+ return (nss - 1) << FD_CAP_NSS_SHIFT;
+
+ return 0;
+}
+
+
static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
{
- u16 cap_info, phy_index = 0;
- u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4;
+ u16 cap_info, phy_index;
+ u8 chwidth = FD_CAP_BSS_CHWIDTH_20, he_mcs_nss_size = 4;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
cap_info = FD_CAP_ESS;
@@ -1376,17 +1520,15 @@
cap_info |= FD_CAP_PRIVACY;
if (is_6ghz_op_class(hapd->iconf->op_class)) {
- phy_index = FD_CAP_PHY_INDEX_HE;
-
switch (hapd->iconf->op_class) {
case 137:
chwidth = FD_CAP_BSS_CHWIDTH_320;
break;
case 135:
- mcs_nss_size += 4;
+ he_mcs_nss_size += 4;
/* fallthrough */
case 134:
- mcs_nss_size += 4;
+ he_mcs_nss_size += 4;
chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
break;
case 133:
@@ -1399,10 +1541,10 @@
} else {
switch (hostapd_get_oper_chwidth(hapd->iconf)) {
case CONF_OPER_CHWIDTH_80P80MHZ:
- mcs_nss_size += 4;
+ he_mcs_nss_size += 4;
/* fallthrough */
case CONF_OPER_CHWIDTH_160MHZ:
- mcs_nss_size += 4;
+ he_mcs_nss_size += 4;
chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
break;
case CONF_OPER_CHWIDTH_80MHZ:
@@ -1417,79 +1559,13 @@
default:
break;
}
-
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
- phy_index = FD_CAP_PHY_INDEX_HE;
-#endif /* CONFIG_IEEE80211AX */
-#ifdef CONFIG_IEEE80211AC
- if (!phy_index &&
- hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
- phy_index = FD_CAP_PHY_INDEX_VHT;
-#endif /* CONFIG_IEEE80211AC */
- if (!phy_index &&
- hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
- phy_index = FD_CAP_PHY_INDEX_HT;
}
+ phy_index = hostapd_gen_fils_discovery_phy_index(hapd);
cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
-
- if (mode && phy_index == FD_CAP_PHY_INDEX_HE) {
- const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
- int i;
- u16 nss = 0, mcs[6];
-
- os_memset(mcs, 0xffff, 6 * sizeof(u16));
-
- if (mcs_nss_size == 4) {
- mcs[0] = WPA_GET_LE16(&he_mcs[0]);
- mcs[1] = WPA_GET_LE16(&he_mcs[2]);
- }
-
- if (mcs_nss_size == 8) {
- mcs[2] = WPA_GET_LE16(&he_mcs[4]);
- mcs[3] = WPA_GET_LE16(&he_mcs[6]);
- }
-
- if (mcs_nss_size == 12) {
- mcs[4] = WPA_GET_LE16(&he_mcs[8]);
- mcs[5] = WPA_GET_LE16(&he_mcs[10]);
- }
-
- for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
- u16 nss_mask = 0x3 << (i * 2);
-
- /*
- * If NSS values supported by RX and TX are different
- * then choose the smaller of the two as the maximum
- * supported NSS as that is the value supported by
- * both RX and TX.
- */
- if (mcs_nss_size == 4 &&
- (((mcs[0] & nss_mask) == nss_mask) ||
- ((mcs[1] & nss_mask) == nss_mask)))
- continue;
-
- if (mcs_nss_size == 8 &&
- (((mcs[2] & nss_mask) == nss_mask) ||
- ((mcs[3] & nss_mask) == nss_mask)))
- continue;
-
- if (mcs_nss_size == 12 &&
- (((mcs[4] & nss_mask) == nss_mask) ||
- ((mcs[5] & nss_mask) == nss_mask)))
- continue;
-
- nss++;
- }
-
- if (nss > 4)
- cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
- else if (nss)
- cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT;
- }
-
+ cap_info |= hostapd_gen_fils_discovery_nss(mode, phy_index,
+ he_mcs_nss_size);
return cap_info;
}
@@ -1711,6 +1787,14 @@
tail_len += 3 + sizeof(struct ieee80211_eht_operation);
if (hapd->iconf->punct_bitmap)
tail_len += 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)
+ tail_len += 256;
}
#endif /* CONFIG_IEEE80211BE */
@@ -1881,6 +1965,9 @@
#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_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_eht_operation(hapd, tailpos);
@@ -1940,6 +2027,50 @@
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
+ /* If key management offload is enabled, configure PSK to the driver. */
+ if (wpa_key_mgmt_wpa_psk_no_sae(hapd->conf->wpa_key_mgmt) &&
+ (hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
+ if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) {
+ os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk,
+ PMK_LEN);
+ params->psk_len = PMK_LEN;
+ } else if (hapd->conf->ssid.wpa_passphrase &&
+ pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase,
+ hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len, 4096,
+ params->psk, PMK_LEN) == 0) {
+ params->psk_len = PMK_LEN;
+ }
+ }
+
+#ifdef CONFIG_SAE
+ /* If SAE offload is enabled, provide password to lower layer for
+ * SAE authentication and PMK generation.
+ */
+ if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
+ if (hostapd_sae_pk_in_use(hapd->conf)) {
+ wpa_printf(MSG_ERROR,
+ "SAE PK not supported with SAE offload");
+ return -1;
+ }
+
+ if (hostapd_sae_pw_id_in_use(hapd->conf)) {
+ wpa_printf(MSG_ERROR,
+ "SAE Password Identifiers not supported with SAE offload");
+ return -1;
+ }
+
+ params->sae_password = sae_get_password(hapd, NULL, NULL, NULL,
+ NULL, NULL);
+ if (!params->sae_password) {
+ wpa_printf(MSG_ERROR, "SAE password not configured for offload");
+ return -1;
+ }
+ }
+#endif /* CONFIG_SAE */
+
params->head = (u8 *) head;
params->head_len = head_len;
params->tail = tail;
@@ -2030,6 +2161,14 @@
}
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
+ !hapd->conf->disable_11be) {
+ params->mld_ap = true;
+ params->mld_link_id = hapd->mld_link_id;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return 0;
}
@@ -2172,6 +2311,12 @@
}
+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
+{
+ __ieee802_11_set_beacon(hapd);
+}
+
+
int ieee802_11_set_beacon(struct hostapd_data *hapd)
{
struct hostapd_iface *iface = hapd->iface;
@@ -2186,21 +2331,29 @@
if (!iface->interfaces || iface->interfaces->count <= 1)
return 0;
- /* Update Beacon frames in case of 6 GHz colocation */
+ /* Update Beacon frames in case of 6 GHz colocation or AP MLD */
is_6g = is_6ghz_op_class(iface->conf->op_class);
for (j = 0; j < iface->interfaces->count; j++) {
- struct hostapd_iface *colocated;
+ struct hostapd_iface *other;
+ bool mld_ap = false;
- colocated = iface->interfaces->iface[j];
- if (colocated == iface || !colocated || !colocated->conf)
+ other = iface->interfaces->iface[j];
+ if (other == iface || !other || !other->conf)
continue;
- if (is_6g == is_6ghz_op_class(colocated->conf->op_class))
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == other->bss[0]->conf->mld_id)
+ mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+ !mld_ap)
continue;
- for (i = 0; i < colocated->num_bss; i++) {
- if (colocated->bss[i] && colocated->bss[i]->started)
- __ieee802_11_set_beacon(colocated->bss[i]);
+ for (i = 0; i < other->num_bss; i++) {
+ if (other->bss[i] && other->bss[i]->started)
+ __ieee802_11_set_beacon(other->bss[i]);
}
}
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index c320825..b32b2a7 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -15,6 +15,7 @@
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal);
+void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
int ieee802_11_set_beacon(struct hostapd_data *hapd);
int ieee802_11_set_beacons(struct hostapd_iface *iface);
int ieee802_11_update_beacons(struct hostapd_iface *iface);
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
index 725d3cd..e9baafc 100644
--- a/src/ap/bss_load.c
+++ b/src/ap/bss_load.c
@@ -55,7 +55,7 @@
return;
}
- ieee802_11_set_beacon(hapd);
+ ieee802_11_set_beacon_per_bss_only(hapd);
if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
return;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 6934a73..a6fcb7e 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -845,6 +845,16 @@
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+
+ if (!iconf->he_op.he_bss_color_disabled &&
+ iconf->he_op.he_bss_color) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "he_bss_color=%d\n",
+ iconf->he_op.he_bss_color);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
}
#endif /* CONFIG_IEEE80211AX */
@@ -938,6 +948,21 @@
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+
+#ifdef CONFIG_IEEE80211BE
+ if (bss->conf->mld_ap) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "mld_addr[%d]=" MACSTR "\n"
+ "mld_id[%d]=%d\n"
+ "mld_link_id[%d]=%d\n",
+ (int) i, MAC2STR(bss->mld_addr),
+ (int) i, bss->conf->mld_id,
+ (int) i, bss->mld_link_id);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
if (hapd->conf->chan_util_avg_period) {
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index e8c5ec9..9a5d3c8 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -34,7 +34,7 @@
static bool dfs_use_radar_background(struct hostapd_iface *iface)
{
- return (iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) &&
+ return (iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) &&
iface->conf->enable_background_radar;
}
@@ -362,8 +362,9 @@
if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
channel_no -= 4;
- /* VHT/HE */
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ /* VHT/HE/EHT */
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
break;
@@ -381,9 +382,13 @@
chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
iface->conf) - 6;
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+ iface->conf) - 30;
+ break;
default:
wpa_printf(MSG_INFO,
- "DFS only VHT20/40/80/160/80+80 is supported now");
+ "DFS only EHT20/40/80/160/80+80/320 is supported now");
channel_no = -1;
break;
}
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 70dd18e..7a8ea4e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -2406,7 +2406,9 @@
char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
char cmd[300];
const char *password = NULL;
+#ifdef CONFIG_SAE
struct sae_password_entry *e;
+#endif /* CONFIG_SAE */
int conf_id = -1;
bool sae = false, psk = false;
size_t len;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 510a06c..98794c2 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -59,9 +59,10 @@
if (!sta->fils_pending_assoc_req)
return;
- ieee802_11_parse_elems(sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len, &elems, 0);
- if (!elems.fils_session) {
+ if (ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+ sta->fils_pending_assoc_req_len, &elems,
+ 0) == ParseFailed ||
+ !elems.fils_session) {
wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
__func__);
return;
@@ -131,8 +132,122 @@
}
+#ifdef CONFIG_IEEE80211BE
+static int hostapd_update_sta_links_status(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *resp_ies,
+ size_t resp_ies_len)
+{
+ struct mld_info *info = &sta->mld_info;
+ struct wpabuf *mlebuf;
+ const u8 *mle, *pos;
+ struct ieee802_11_elems elems;
+ size_t mle_len, rem_len;
+ int ret = 0;
+
+ if (!resp_ies) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: (Re)Association Response frame elements not available");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 0) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: Failed to parse (Re)Association Response frame elements");
+ return -1;
+ }
+
+ mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlebuf) {
+ wpa_printf(MSG_ERROR,
+ "MLO: Basic Multi-Link element not found in (Re)Association Response frame");
+ return -1;
+ }
+
+ mle = wpabuf_head(mlebuf);
+ mle_len = wpabuf_len(mlebuf);
+ if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+ mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN]) {
+ wpa_printf(MSG_ERROR,
+ "MLO: Invalid Multi-Link element in (Re)Association Response frame");
+ ret = -1;
+ goto out;
+ }
+
+ /* Skip Common Info */
+ pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+ rem_len = mle_len -
+ (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+ /* Parse Subelements */
+ while (rem_len > 2) {
+ size_t ie_len = 2 + pos[1];
+
+ if (rem_len < ie_len)
+ break;
+
+ if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ u8 link_id;
+ const u8 *sta_profile;
+ size_t sta_profile_len;
+ u16 sta_ctrl;
+
+ if (pos[1] < BASIC_MLE_STA_CTRL_LEN + 1) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: Invalid per-STA profile IE");
+ goto next_subelem;
+ }
+
+ sta_profile_len = pos[1];
+ sta_profile = &pos[2];
+ sta_ctrl = WPA_GET_LE16(sta_profile);
+ link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: Invalid link ID in per-STA profile IE");
+ goto next_subelem;
+ }
+
+ /* Skip STA Control and STA Info */
+ if (sta_profile_len - BASIC_MLE_STA_CTRL_LEN <
+ sta_profile[BASIC_MLE_STA_CTRL_LEN]) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: Invalid STA info in per-STA profile IE");
+ goto next_subelem;
+ }
+
+ sta_profile_len = sta_profile_len -
+ (BASIC_MLE_STA_CTRL_LEN +
+ sta_profile[BASIC_MLE_STA_CTRL_LEN]);
+ sta_profile = sta_profile + BASIC_MLE_STA_CTRL_LEN +
+ sta_profile[BASIC_MLE_STA_CTRL_LEN];
+
+ /* Skip Capabilities Information field */
+ if (sta_profile_len < 2)
+ goto next_subelem;
+ sta_profile_len -= 2;
+ sta_profile += 2;
+
+ /* Get status of the link */
+ info->links[link_id].status = WPA_GET_LE16(sta_profile);
+ }
+next_subelem:
+ pos += ie_len;
+ rem_len -= ie_len;
+ }
+
+out:
+ wpabuf_free(mlebuf);
+ return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *req_ies, size_t req_ies_len, int reassoc)
+ const u8 *req_ies, size_t req_ies_len,
+ const u8 *resp_ies, size_t resp_ies_len,
+ const u8 *link_addr, int reassoc)
{
struct sta_info *sta;
int new_assoc;
@@ -145,6 +260,9 @@
u16 reason = WLAN_REASON_UNSPECIFIED;
int status = WLAN_STATUS_SUCCESS;
const u8 *p2p_dev_addr = NULL;
+#ifdef CONFIG_OWE
+ struct hostapd_iface *iface = hapd->iface;
+#endif /* CONFIG_OWE */
if (addr == NULL) {
/*
@@ -176,7 +294,12 @@
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "associated");
- ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
+ if (ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG, "%s: Could not parse elements", __func__);
+ return -1;
+ }
+
if (elems.wps_ie) {
ie = elems.wps_ie - 2;
ielen = elems.wps_ie_len + 2;
@@ -220,6 +343,53 @@
return -1;
}
}
+
+ if (hapd->conf->wpa && check_sa_query_need(hapd, sta)) {
+ status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+ p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+ return 0;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (link_addr) {
+ struct mld_info *info = &sta->mld_info;
+ int i, num_valid_links = 0;
+ u8 link_id = hapd->mld_link_id;
+
+ info->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;
+ os_memcpy(info->links[link_id].peer_addr, link_addr, ETH_ALEN);
+ os_memcpy(info->links[link_id].local_addr, hapd->own_addr,
+ ETH_ALEN);
+
+ if (!elems.basic_mle ||
+ hostapd_process_ml_assoc_req(hapd, &elems, sta) !=
+ WLAN_STATUS_SUCCESS) {
+ reason = WLAN_REASON_UNSPECIFIED;
+ wpa_printf(MSG_DEBUG,
+ "Failed to get STA non-assoc links info");
+ goto fail;
+ }
+
+ for (i = 0 ; i < MAX_NUM_MLD_LINKS; i++) {
+ if (info->links[i].valid)
+ num_valid_links++;
+ }
+ if (num_valid_links > 1 &&
+ hostapd_update_sta_links_status(hapd, sta, resp_ies,
+ resp_ies_len)) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to get STA non-assoc links status info");
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
/*
@@ -314,17 +484,6 @@
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
struct wpabuf *wps;
- if (check_sa_query_need(hapd, sta)) {
- status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-
- p = hostapd_eid_assoc_comeback_time(hapd, sta,
- p);
-
- hostapd_sta_assoc(hapd, addr, reassoc, status,
- buf, p - buf);
- return 0;
- }
-
sta->flags |= WLAN_STA_WPS;
wps = ieee802_11_vendor_ie_concat(ie, ielen,
WPS_IE_VENDOR_TYPE);
@@ -340,16 +499,6 @@
}
#endif /* CONFIG_WPS */
- if (check_sa_query_need(hapd, sta)) {
- status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-
- p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
-
- hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
- p - buf);
- return 0;
- }
-
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
@@ -359,6 +508,15 @@
"Failed to initialize WPA state machine");
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set ML info in RSN Authenticator");
+ wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ sta->mld_assoc_link_id,
+ &sta->mld_info);
+ }
+#endif /* CONFIG_IEEE80211BE */
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
ie, ielen,
@@ -617,6 +775,7 @@
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ !(iface->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_AP) &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
elems.owe_dh) {
u8 *npos;
@@ -863,7 +1022,7 @@
{
#ifdef NEED_AP_MLME
int channel, chwidth, is_dfs0, is_dfs;
- u8 seg0_idx = 0, seg1_idx = 0;
+ u8 seg0_idx = 0, seg1_idx = 0, op_class, chan_no;
size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
@@ -917,6 +1076,12 @@
break;
}
+ /* The operating channel changed when CSA finished, so need to update
+ * hw_mode for all following operations to cover the cases where the
+ * driver changed the operating band. */
+ if (finished && hostapd_csa_update_hwmode(hapd->iface))
+ return;
+
switch (hapd->iface->current_mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
if (cf1 == 5935)
@@ -941,9 +1106,9 @@
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
- if (!ht) {
+ if (!ht)
hapd->iconf->ieee80211ac = 0;
- } else if (hapd->iconf->ch_switch_vht_config) {
+ if (hapd->iconf->ch_switch_vht_config) {
/* CHAN_SWITCH VHT config */
if (hapd->iconf->ch_switch_vht_config &
CH_SWITCH_VHT_ENABLED)
@@ -951,28 +1116,35 @@
else if (hapd->iconf->ch_switch_vht_config &
CH_SWITCH_VHT_DISABLED)
hapd->iconf->ieee80211ac = 0;
- } else if (hapd->iconf->ch_switch_he_config) {
+ }
+ if (hapd->iconf->ch_switch_he_config) {
/* CHAN_SWITCH HE config */
if (hapd->iconf->ch_switch_he_config &
- CH_SWITCH_HE_ENABLED)
+ CH_SWITCH_HE_ENABLED) {
hapd->iconf->ieee80211ax = 1;
+ if (hapd->iface->freq > 4000 &&
+ hapd->iface->freq < 5895)
+ hapd->iconf->ieee80211ac = 1;
+ }
else if (hapd->iconf->ch_switch_he_config &
CH_SWITCH_HE_DISABLED)
hapd->iconf->ieee80211ax = 0;
+ }
#ifdef CONFIG_IEEE80211BE
- } else if (hapd->iconf->ch_switch_eht_config) {
+ if (hapd->iconf->ch_switch_eht_config) {
/* CHAN_SWITCH EHT config */
if (hapd->iconf->ch_switch_eht_config &
CH_SWITCH_EHT_ENABLED) {
hapd->iconf->ieee80211be = 1;
hapd->iconf->ieee80211ax = 1;
- if (!is_6ghz_freq(hapd->iface->freq))
+ if (!is_6ghz_freq(hapd->iface->freq) &&
+ hapd->iface->freq > 4000)
hapd->iconf->ieee80211ac = 1;
} else if (hapd->iconf->ch_switch_eht_config &
CH_SWITCH_EHT_DISABLED)
hapd->iconf->ieee80211be = 0;
-#endif /* CONFIG_IEEE80211BE */
}
+#endif /* CONFIG_IEEE80211BE */
hapd->iconf->ch_switch_vht_config = 0;
hapd->iconf->ch_switch_he_config = 0;
hapd->iconf->ch_switch_eht_config = 0;
@@ -985,6 +1157,10 @@
hapd->iconf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
hapd->iconf->secondary_channel = offset;
+ if (ieee80211_freq_to_channel_ext(freq, offset, chwidth,
+ &op_class, &chan_no) !=
+ NUM_HOSTAPD_MODES)
+ hapd->iconf->op_class = op_class;
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);
@@ -1202,6 +1378,9 @@
hapd->iconf, acs_res->vht_seg1_center_ch);
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
}
+
+ if (hapd->iface->conf->ieee80211be && acs_res->puncture_bitmap)
+ hapd->iconf->punct_bitmap = acs_res->puncture_bitmap;
#endif /* CONFIG_IEEE80211BE */
out:
@@ -1418,6 +1597,23 @@
#ifdef NEED_AP_MLME
+static struct hostapd_data *
+switch_link_hapd(struct hostapd_data *hapd, int link_id)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && link_id >= 0) {
+ struct hostapd_data *link_bss;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (link_bss)
+ return link_bss;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd;
+}
+
+
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
@@ -1454,11 +1650,15 @@
static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
{
- struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_iface *iface;
const struct ieee80211_hdr *hdr;
const u8 *bssid;
struct hostapd_frame_info fi;
int ret;
+ bool is_mld = false;
+
+ hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
+ iface = hapd->iface;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_mgmt_frame_handling) {
@@ -1480,8 +1680,16 @@
if (bssid == NULL)
return 0;
- hapd = get_hapd_bssid(iface, bssid);
- if (hapd == NULL) {
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
+ is_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!is_mld)
+ hapd = get_hapd_bssid(iface, bssid);
+
+ if (!hapd) {
u16 fc = le_to_host16(hdr->frame_control);
/*
@@ -1526,15 +1734,34 @@
static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
- size_t len, u16 stype, int ok)
+ size_t len, u16 stype, int ok, int link_id)
{
struct ieee80211_hdr *hdr;
- struct hostapd_data *orig_hapd = hapd;
+ struct hostapd_data *orig_hapd, *tmp_hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && link_id != -1) {
+ tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (tmp_hapd)
+ hapd = tmp_hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ orig_hapd = hapd;
hdr = (struct ieee80211_hdr *) buf;
- hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
- if (!hapd)
+ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ if (tmp_hapd) {
+ 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) {
+ /* AP MLD address match - use hapd pointer as-is */
+#endif /* CONFIG_IEEE80211BE */
+ } else {
return;
+ }
+
if (hapd == HAPD_BROADCAST) {
if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
buf[24] != WLAN_ACTION_PUBLIC)
@@ -1575,20 +1802,75 @@
}
-static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
- const u8 *data, size_t data_len,
- enum frame_encryption encrypted)
+static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+ const u8 *src)
{
- struct hostapd_iface *iface = hapd->iface;
struct sta_info *sta;
- size_t j;
+ 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) {
- hapd = iface->bss[j];
- break;
+ if (sta && sta->flags & WLAN_STA_ASSOC)
+ return iface->bss[j];
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ const u8 *data, size_t data_len,
+ enum frame_encryption encrypted,
+ int link_id)
+{
+ struct hostapd_data *orig_hapd = hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (link_id != -1) {
+ struct hostapd_data *h_hapd;
+
+ hapd = switch_link_hapd(hapd, link_id);
+ h_hapd = hostapd_find_by_sta(hapd->iface, src);
+ if (!h_hapd)
+ h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
+ 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;
+ }
}
+ } else {
+ hapd = hostapd_find_by_sta(hapd->iface, src);
+ }
+#else /* CONFIG_IEEE80211BE */
+ hapd = hostapd_find_by_sta(hapd->iface, src);
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd) {
+ /* WLAN cases need to have an existing association, but non-WLAN
+ * cases (mainly, wired IEEE 802.1X) need to be able to process
+ * EAPOL frames from new devices that do not yet have a STA
+ * entry and as such, do not get a match in
+ * hostapd_find_by_sta(). */
+ wpa_printf(MSG_DEBUG,
+ "No STA-specific hostapd instance for EAPOL RX found - fall back to initial context");
+ hapd = orig_hapd;
}
ieee802_1x_receive(hapd, src, data, data_len, encrypted);
@@ -1827,7 +2109,7 @@
#ifdef CONFIG_OWE
static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
const u8 *peer, const u8 *ie,
- size_t ie_len)
+ size_t ie_len, const u8 *link_addr)
{
u16 status;
struct sta_info *sta;
@@ -1877,15 +2159,31 @@
}
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+#ifdef CONFIG_IEEE80211BE
+ if (link_addr) {
+ 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;;
+ 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,
+ ETH_ALEN);
+ os_memcpy(info->links[link_id].peer_addr, link_addr, ETH_ALEN);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
elems.rsn_ie_len, elems.owe_dh,
- elems.owe_dh_len);
+ elems.owe_dh_len, link_addr);
if (status != WLAN_STATUS_SUCCESS)
ap_free_sta(hapd, sta);
return 0;
err:
- hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
+ hostapd_drv_update_dh_ie(hapd, link_addr ? link_addr : peer, status,
+ NULL, 0);
return 0;
}
#endif /* CONFIG_OWE */
@@ -1895,6 +2193,7 @@
union wpa_event_data *data)
{
struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
#ifndef CONFIG_NO_STDOUT_DEBUG
int level = MSG_DEBUG;
@@ -1935,7 +2234,8 @@
hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
data->tx_status.data_len,
data->tx_status.stype,
- data->tx_status.ack);
+ data->tx_status.ack,
+ data->tx_status.link_id);
break;
case WLAN_FC_TYPE_DATA:
hostapd_tx_status(hapd, data->tx_status.dst,
@@ -1946,6 +2246,7 @@
}
break;
case EVENT_EAPOL_TX_STATUS:
+ hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
data->eapol_tx_status.data,
data->eapol_tx_status.data_len,
@@ -1987,23 +2288,60 @@
hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
data->eapol_rx.data,
data->eapol_rx.data_len,
- data->eapol_rx.encrypted);
+ data->eapol_rx.encrypted,
+ data->eapol_rx.link_id);
break;
case EVENT_ASSOC:
if (!data)
return;
+#ifdef CONFIG_IEEE80211BE
+ if (data->assoc_info.assoc_link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(
+ hapd, data->assoc_info.assoc_link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for EVENT_ASSOC");
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
hostapd_notif_assoc(hapd, data->assoc_info.addr,
data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
+ data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len,
+ data->assoc_info.link_addr,
data->assoc_info.reassoc);
break;
+ case EVENT_PORT_AUTHORIZED:
+ /* Port authorized event for an associated STA */
+ sta = ap_get_sta(hapd, data->port_authorized.sta_addr);
+ if (sta)
+ ap_sta_set_authorized(hapd, sta, 1);
+ else
+ wpa_printf(MSG_DEBUG,
+ "No STA info matching port authorized event found");
+ break;
#ifdef CONFIG_OWE
case EVENT_UPDATE_DH:
if (!data)
return;
+#ifdef CONFIG_IEEE80211BE
+ if (data->update_dh.assoc_link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(
+ hapd, data->update_dh.assoc_link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for EVENT_UPDATE_DH assoc_link_id=%d",
+ data->update_dh.assoc_link_id);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
data->update_dh.ie,
- data->update_dh.ie_len);
+ data->update_dh.ie_len,
+ data->update_dh.link_addr);
break;
#endif /* CONFIG_OWE */
case EVENT_DISASSOC:
@@ -2154,7 +2492,8 @@
case EVENT_CCA_NOTIFY:
wpa_printf(MSG_DEBUG, "CCA finished on on %s",
hapd->conf->iface);
- hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ if (hapd->cca_color)
+ hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
hostapd_cleanup_cca_params(hapd);
break;
#endif /* CONFIG_IEEE80211AX */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 112e6fa..236381f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -55,6 +55,7 @@
#include "hs20.h"
#include "airtime_policy.h"
#include "wpa_auth_kay.h"
+#include "hw_features.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -393,6 +394,25 @@
#endif /* CONFIG_WEP */
+static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
+{
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
+
+ if (hapd->iface == iface || !iface)
+ continue;
+
+ if (iface->bss && iface->bss[0] &&
+ iface->bss[0]->mld_first_bss == hapd)
+ iface->bss[0]->drv_priv = NULL;
+ }
+
+ hapd->drv_priv = NULL;
+}
+
+
void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
os_free(hapd->probereq_cb);
@@ -420,9 +440,11 @@
vlan_deinit(hapd);
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
- radius_client_deinit(hapd->radius);
+ if (!hapd->mld_first_bss) {
+ radius_client_deinit(hapd->radius);
+ radius_das_deinit(hapd->radius_das);
+ }
hapd->radius = NULL;
- radius_das_deinit(hapd->radius_das);
hapd->radius_das = NULL;
#endif /* CONFIG_NO_RADIUS */
@@ -449,7 +471,7 @@
* driver wrapper may have removed its internal instance
* and hapd->drv_priv is not valid anymore.
*/
- hapd->drv_priv = NULL;
+ hostapd_clear_drv_priv(hapd);
}
}
@@ -1196,6 +1218,10 @@
u8 if_addr[ETH_ALEN];
int flush_old_stations = 1;
+ if (hapd->mld_first_bss)
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s: Setting non-first BSS", __func__);
+
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
__func__, hapd, conf->iface, first);
@@ -1354,34 +1380,43 @@
}
#endif /* CONFIG_SQLITE */
- hapd->radius = radius_client_init(hapd, conf->radius);
- if (hapd->radius == NULL) {
- wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
- return -1;
- }
-
- if (conf->radius_das_port) {
- struct radius_das_conf das_conf;
- os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = conf->radius_das_port;
- das_conf.shared_secret = conf->radius_das_shared_secret;
- das_conf.shared_secret_len =
- conf->radius_das_shared_secret_len;
- das_conf.client_addr = &conf->radius_das_client_addr;
- das_conf.time_window = conf->radius_das_time_window;
- das_conf.require_event_timestamp =
- conf->radius_das_require_event_timestamp;
- das_conf.require_message_authenticator =
- conf->radius_das_require_message_authenticator;
- das_conf.ctx = hapd;
- das_conf.disconnect = hostapd_das_disconnect;
- das_conf.coa = hostapd_das_coa;
- hapd->radius_das = radius_das_init(&das_conf);
- if (hapd->radius_das == NULL) {
- wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
- "failed.");
+ if (!hapd->mld_first_bss) {
+ hapd->radius = radius_client_init(hapd, conf->radius);
+ if (!hapd->radius) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS client initialization failed.");
return -1;
}
+
+ if (conf->radius_das_port) {
+ struct radius_das_conf das_conf;
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &conf->radius_das_client_addr;
+ das_conf.time_window = conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ conf->radius_das_require_event_timestamp;
+ das_conf.require_message_authenticator =
+ conf->radius_das_require_message_authenticator;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (!hapd->radius_das) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS DAS initialization failed.");
+ return -1;
+ }
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using RADIUS client of the first BSS");
+ hapd->radius = hapd->mld_first_bss->radius;
+ hapd->radius_das = hapd->mld_first_bss->radius_das;
}
#endif /* CONFIG_NO_RADIUS */
@@ -1607,6 +1642,125 @@
}
+/* When NO_IR flag is set and AP is stopped, clean up BSS parameters without
+ * deinitializing the driver and the control interfaces. A subsequent
+ * REG_CHANGE event can bring the AP back up.
+ */
+static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
+{
+ hostapd_bss_deinit_no_free(bss);
+ hostapd_free_hapd_data(bss);
+ hostapd_cleanup_iface_partial(bss->iface);
+}
+
+
+static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
+ void *ctx)
+{
+ bool all_no_ir, is_6ghz;
+ int i, j;
+ struct hostapd_hw_modes *mode = NULL;
+
+ if (hostapd_get_hw_features(iface))
+ return 0;
+
+ all_no_ir = true;
+ is_6ghz = false;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+
+ if (mode->mode == iface->conf->hw_mode) {
+ if (iface->freq > 0 &&
+ !hw_mode_get_channel(mode, iface->freq, NULL)) {
+ mode = NULL;
+ continue;
+ }
+
+ for (j = 0; j < mode->num_channels; j++) {
+ if (!(mode->channels[j].flag &
+ HOSTAPD_CHAN_NO_IR))
+ all_no_ir = false;
+
+ if (is_6ghz_freq(mode->channels[j].freq))
+ is_6ghz = true;
+ }
+ break;
+ }
+ }
+
+ if (!mode || !is_6ghz)
+ return 0;
+ iface->current_mode = mode;
+
+ if (iface->state == HAPD_IFACE_ENABLED) {
+ if (!all_no_ir) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+
+ if (!chan) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not derive chan from freq");
+ return 0;
+ }
+
+ if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
+ return 0;
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: The current channel has NO_IR flag now, stop AP.");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: All chan in new chanlist are NO_IR, stop AP.");
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ iface->is_no_ir = true;
+ hostapd_drv_stop_ap(iface->bss[0]);
+ hostapd_no_ir_cleanup(iface->bss[0]);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ } else if (iface->state == HAPD_IFACE_NO_IR) {
+ if (all_no_ir) {
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
+ return 0;
+ }
+
+ if (!iface->conf->acs) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+ if (!chan) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not derive chan from freq");
+ return 0;
+ }
+
+ /* If the last operating channel is NO_IR, trigger ACS.
+ */
+ if (chan->flag & HOSTAPD_CHAN_NO_IR) {
+ iface->freq = 0;
+ iface->conf->channel = 0;
+ if (acs_init(iface) != HOSTAPD_CHAN_ACS)
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not start ACS");
+ return 0;
+ }
+ }
+
+ setup_interface2(iface);
+ }
+
+ return 0;
+}
+
+
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
@@ -1627,6 +1781,13 @@
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
{
+ if (initiator == REGDOM_SET_BY_DRIVER) {
+ hostapd_for_each_interface(iface->interfaces,
+ hostapd_no_ir_channel_list_updated,
+ NULL);
+ return;
+ }
+
if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
return;
@@ -1776,6 +1937,7 @@
static int setup_interface2(struct hostapd_iface *iface)
{
iface->wait_channel_update = 0;
+ iface->is_no_ir = false;
if (hostapd_get_hw_features(iface)) {
/* Not all drivers support this yet, so continue without hw
@@ -1831,6 +1993,14 @@
return hostapd_setup_interface_complete(iface, 0);
fail:
+ if (iface->is_no_ir) {
+ /* If AP is in NO_IR state, it can be reenabled by the driver
+ * regulatory update and EVENT_CHANNEL_LIST_CHANGED. */
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
if (iface->interfaces && iface->interfaces->terminate_on_error)
@@ -2317,10 +2487,20 @@
for (j = 0; j < iface->num_bss; j++)
hostapd_neighbor_set_own_report(iface->bss[j]);
+ if (iface->interfaces && iface->interfaces->count > 1)
+ ieee802_11_set_beacons(iface);
+
return 0;
fail:
wpa_printf(MSG_ERROR, "Interface initialization failed");
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
#ifdef CONFIG_FST
@@ -2366,8 +2546,15 @@
if (err) {
wpa_printf(MSG_ERROR, "Interface initialization failed");
- hostapd_set_state(iface, HAPD_IFACE_DISABLED);
iface->need_to_start_in_sync = 0;
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
if (interfaces && interfaces->terminate_on_error)
eloop_terminate();
@@ -2536,6 +2723,7 @@
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
iface->wait_channel_update = 0;
+ iface->is_no_ir = false;
#ifdef CONFIG_FST
if (iface->fst) {
@@ -2800,8 +2988,9 @@
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
if (driver && driver->hapd_deinit && drv_priv) {
- driver->hapd_deinit(drv_priv);
- iface->bss[0]->drv_priv = NULL;
+ if (!iface->bss[0]->mld_first_bss)
+ driver->hapd_deinit(drv_priv);
+ hostapd_clear_drv_priv(iface->bss[0]);
}
hostapd_interface_free(iface);
}
@@ -2816,13 +3005,14 @@
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
if (driver && driver->hapd_deinit && drv_priv) {
- driver->hapd_deinit(drv_priv);
+ if (!hapd_iface->bss[0]->mld_first_bss)
+ driver->hapd_deinit(drv_priv);
for (j = 0; j < hapd_iface->num_bss; j++) {
wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
__func__, (int) j,
hapd_iface->bss[j]->drv_priv);
if (hapd_iface->bss[j]->drv_priv == drv_priv) {
- hapd_iface->bss[j]->drv_priv = NULL;
+ hostapd_clear_drv_priv(hapd_iface->bss[j]);
hapd_iface->extended_capa = NULL;
hapd_iface->extended_capa_mask = NULL;
hapd_iface->extended_capa_len = 0;
@@ -3163,8 +3353,14 @@
conf_file = ptr + 7;
for (i = 0; i < interfaces->count; i++) {
+ bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+ mld_ap = interfaces->iface[i]->conf->bss[0]->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
- buf)) {
+ buf) && !mld_ap) {
wpa_printf(MSG_INFO, "Cannot add interface - it "
"already exists");
return -1;
@@ -3339,6 +3535,12 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return;
+#endif /* CONFIG_IEEE80211BE */
+
ap_sta_clear_disconnect_timeouts(hapd, sta);
sta->post_csa_sa_query = 0;
@@ -3371,8 +3573,12 @@
sta->auth_alg != WLAN_AUTH_FILS_PK &&
!(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
- } else
+ } else if (!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
+ /* The 4-way handshake offloaded case will have this handled
+ * based on the port authorized event. */
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+ }
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
@@ -3421,6 +3627,8 @@
return "DFS";
case HAPD_IFACE_ENABLED:
return "ENABLED";
+ case HAPD_IFACE_NO_IR:
+ return "NO_IR";
}
return "UNKNOWN";
@@ -3572,6 +3780,7 @@
if (!channel)
return -1;
+ hostapd_determine_mode(hapd->iface);
mode = hapd->iface->current_mode;
/* if a pointer to old_params is provided we save previous state */
@@ -4063,3 +4272,26 @@
}
}
#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_IEEE80211BE
+struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ u8 link_id)
+{
+ unsigned int i;
+
+ 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;
+
+ if (h_hapd->mld_link_id == link_id)
+ return h_hapd;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index b9a67b9..7f703be 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -99,6 +99,7 @@
HOSTAPD_CHAN_VALID = 0, /* channel is ready */
HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+ HOSTAPD_CHAN_INVALID_NO_IR = 3, /* channel invalid due to AFC NO IR */
};
struct hostapd_probereq_cb {
@@ -174,6 +175,12 @@
unsigned int reenable_beacon:1;
u8 own_addr[ETH_ALEN];
+ u8 mld_addr[ETH_ALEN];
+ u8 mld_link_id;
+ /* Used for mld_link_id assignment - valid on the first MLD BSS only */
+ u8 mld_next_link_id;
+
+ struct hostapd_data *mld_first_bss;
int num_sta; /* number of entries in sta_list */
struct sta_info *sta_list; /* STA info list head */
@@ -482,6 +489,7 @@
HAPD_IFACE_ACS,
HAPD_IFACE_HT_SCAN,
HAPD_IFACE_DFS,
+ HAPD_IFACE_NO_IR,
HAPD_IFACE_ENABLED
};
@@ -495,7 +503,8 @@
struct hostapd_config *conf;
char phy[16]; /* Name of the PHY (radio) */
- enum hostapd_iface_state state;
+ enum hostapd_iface_state state;
+
#ifdef CONFIG_MESH
struct mesh_conf *mconf;
#endif /* CONFIG_MESH */
@@ -542,6 +551,8 @@
const u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
+ u16 mld_eml_capa, mld_mld_capa;
+
unsigned int drv_max_acl_mac_addrs;
struct hostapd_hw_modes *hw_features;
@@ -652,6 +663,9 @@
int (*enable_iface_cb)(struct hostapd_iface *iface);
int (*disable_iface_cb)(struct hostapd_iface *iface);
+
+ /* Configured freq of interface is NO_IR */
+ bool is_no_ir;
};
/* hostapd.c */
@@ -712,13 +726,15 @@
const u8 *ie, size_t ie_len,
int ssi_signal),
void *ctx);
-void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+ int mld_assoc_link_id);
/* drv_callbacks.c (TODO: move to somewhere else?) */
void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta);
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *ie, size_t ielen, int reassoc);
+ const u8 *req_ie, size_t req_ielen, const u8 *resp_ie,
+ size_t resp_ielen, const u8 *link_addr, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
@@ -753,5 +769,7 @@
int hostapd_set_acl(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
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);
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index f836be4..9edbb5a 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -79,6 +79,9 @@
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
u8 dfs_domain;
+ enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY;
+ bool is_6ghz = false;
+ bool orig_mode_valid = false;
if (hostapd_drv_none(hapd))
return -1;
@@ -95,6 +98,20 @@
iface->hw_flags = flags;
iface->dfs_domain = dfs_domain;
+ if (iface->current_mode) {
+ /*
+ * Received driver event CHANNEL_LIST_CHANGED when the current
+ * hw mode is valid. Clear iface->current_mode temporarily as
+ * the mode instance will be replaced with a new instance and
+ * the current pointer would be pointing to freed memory.
+ */
+ orig_mode_valid = true;
+ mode = iface->current_mode->mode;
+ is_6ghz = mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->current_mode->num_channels > 0 &&
+ is_6ghz_freq(iface->current_mode->channels[0].freq);
+ iface->current_mode = NULL;
+ }
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = modes;
iface->num_hw_features = num_modes;
@@ -104,6 +121,12 @@
int dfs_enabled = hapd->iconf->ieee80211h &&
(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+ /* Restore orignal mode if possible */
+ if (orig_mode_valid && feature->mode == mode &&
+ feature->num_channels > 0 &&
+ is_6ghz == is_6ghz_freq(feature->channels[0].freq))
+ iface->current_mode = feature;
+
/* set flag for channels we can use in current regulatory
* domain */
for (j = 0; j < feature->num_channels; j++) {
@@ -141,6 +164,12 @@
}
}
+ if (orig_mode_valid && !iface->current_mode) {
+ wpa_printf(MSG_ERROR,
+ "%s: Could not update iface->current_mode",
+ __func__);
+ }
+
return 0;
}
@@ -794,6 +823,11 @@
}
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
int frequency, int primary)
{
@@ -817,6 +851,10 @@
chan->flag,
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+
+ if (is_6ghz_freq(chan->freq) && (chan->flag & HOSTAPD_CHAN_NO_IR))
+ return -1;
+
return 0;
}
@@ -826,6 +864,7 @@
int i, contiguous = 0;
int num_of_enabled = 0;
int max_contiguous = 0;
+ int err;
struct ieee80211_edmg_config edmg;
struct hostapd_channel_data *pri_chan;
@@ -865,8 +904,9 @@
if (num_of_enabled > 4)
return 0;
- if (!hostapd_is_usable_chan(iface, freq, 1))
- return 0;
+ err = hostapd_is_usable_chan(iface, freq, 1);
+ if (err <= 0)
+ return err;
if (contiguous > max_contiguous)
max_contiguous = contiguous;
@@ -897,7 +937,8 @@
{
#ifdef CONFIG_IEEE80211BE
struct hostapd_config *conf = iface->conf;
- u8 bw, start_chan;
+ u16 bw;
+ u8 start_chan;
if (!conf->punct_bitmap)
return true;
@@ -914,21 +955,30 @@
return false;
}
- switch (conf->eht_oper_chwidth) {
- case 0:
- wpa_printf(MSG_ERROR,
- "RU puncturing is supported only in 80 MHz and 160 MHz");
- return false;
- case 1:
- bw = 80;
- start_chan = conf->eht_oper_centr_freq_seg0_idx - 6;
- break;
- case 2:
- bw = 160;
- start_chan = conf->eht_oper_centr_freq_seg0_idx - 14;
- break;
- default:
- return false;
+ /*
+ * In the 6 GHz band, eht_oper_chwidth is ignored. Use operating class
+ * to determine channel width.
+ */
+ if (conf->op_class == 137) {
+ bw = 320;
+ start_chan = conf->eht_oper_centr_freq_seg0_idx - 30;
+ } else {
+ switch (conf->eht_oper_chwidth) {
+ case 0:
+ wpa_printf(MSG_ERROR,
+ "RU puncturing is supported only in 80 MHz and 160 MHz");
+ return false;
+ case 1:
+ bw = 80;
+ start_chan = conf->eht_oper_centr_freq_seg0_idx - 6;
+ break;
+ case 2:
+ bw = 160;
+ start_chan = conf->eht_oper_centr_freq_seg0_idx - 14;
+ break;
+ default:
+ return false;
+ }
}
if (!is_punct_bitmap_valid(bw, (conf->channel - start_chan) / 4,
@@ -942,10 +992,16 @@
}
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
+ int err;
if (!iface->current_mode)
return 0;
@@ -957,12 +1013,15 @@
wpa_printf(MSG_ERROR, "Primary frequency not present");
return 0;
}
- if (!hostapd_is_usable_chan(iface, pri_chan->freq, 1)) {
+
+ err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
+ if (err <= 0) {
wpa_printf(MSG_ERROR, "Primary frequency not allowed");
- return 0;
+ return err;
}
- if (!hostapd_is_usable_edmg(iface))
- return 0;
+ err = hostapd_is_usable_edmg(iface);
+ if (err <= 0)
+ return err;
if (!hostapd_is_usable_punct_bitmap(iface))
return 0;
@@ -970,8 +1029,9 @@
if (!iface->conf->secondary_channel)
return 1;
- if (hostapd_is_usable_chan(iface, iface->freq +
- iface->conf->secondary_channel * 20, 0)) {
+ err = hostapd_is_usable_chan(iface, iface->freq +
+ iface->conf->secondary_channel * 20, 0);
+ if (err > 0) {
if (iface->conf->secondary_channel == 1 &&
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))
return 1;
@@ -980,24 +1040,24 @@
return 1;
}
if (!iface->conf->ht40_plus_minus_allowed)
- return 0;
+ return err;
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_freq = iface->freq + 20;
- if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
- (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+ err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_freq = iface->freq - 20;
- if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
- (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+ err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
- return 0;
+ return err;
}
@@ -1019,14 +1079,14 @@
}
-static void hostapd_determine_mode(struct hostapd_iface *iface)
+int hostapd_determine_mode(struct hostapd_iface *iface)
{
int i;
enum hostapd_hw_mode target_mode;
if (iface->current_mode ||
iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
- return;
+ return 0;
if (iface->freq < 4000)
target_mode = HOSTAPD_MODE_IEEE80211G;
@@ -1049,8 +1109,11 @@
}
}
- if (!iface->current_mode)
- wpa_printf(MSG_ERROR, "ACS: Cannot decide mode");
+ if (!iface->current_mode) {
+ wpa_printf(MSG_ERROR, "ACS/CSA: Cannot decide mode");
+ return -1;
+ }
+ return 0;
}
@@ -1058,11 +1121,17 @@
hostapd_check_chans(struct hostapd_iface *iface)
{
if (iface->freq) {
+ int err;
+
hostapd_determine_mode(iface);
- if (hostapd_is_usable_chans(iface))
- return HOSTAPD_CHAN_VALID;
- else
- return HOSTAPD_CHAN_INVALID;
+
+ err = hostapd_is_usable_chans(iface);
+ if (err <= 0) {
+ if (!err)
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_INVALID_NO_IR;
+ }
+ return HOSTAPD_CHAN_VALID;
}
/*
@@ -1073,6 +1142,8 @@
switch (acs_init(iface)) {
case HOSTAPD_CHAN_ACS:
return HOSTAPD_CHAN_ACS;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ return HOSTAPD_CHAN_INVALID_NO_IR;
case HOSTAPD_CHAN_VALID:
case HOSTAPD_CHAN_INVALID:
default:
@@ -1112,6 +1183,7 @@
switch (hostapd_check_chans(iface)) {
case HOSTAPD_CHAN_VALID:
+ iface->is_no_ir = false;
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
ACS_EVENT_COMPLETED "freq=%d channel=%d",
iface->freq, iface->conf->channel);
@@ -1121,6 +1193,9 @@
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
hostapd_notify_bad_chans(iface);
goto out;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ iface->is_no_ir = true;
+ /* fall through */
case HOSTAPD_CHAN_INVALID:
default:
wpa_printf(MSG_ERROR, "ACS picked unusable channels");
@@ -1144,6 +1219,25 @@
/**
+ * hostapd_csa_update_hwmode - Update hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, < 0 on failure
+ *
+ * Update hardware mode when the operating channel changed because of CSA.
+ */
+int hostapd_csa_update_hwmode(struct hostapd_iface *iface)
+{
+ if (!iface || !iface->conf)
+ return -1;
+
+ iface->current_mode = NULL;
+ iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
+
+ return hostapd_determine_mode(iface);
+}
+
+
+/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
* Returns: 0 on success, < 0 on failure
@@ -1206,9 +1300,13 @@
switch (hostapd_check_chans(iface)) {
case HOSTAPD_CHAN_VALID:
+ iface->is_no_ir = false;
return 0;
case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
return 1;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ iface->is_no_ir = true;
+ /* fall through */
case HOSTAPD_CHAN_INVALID:
default:
hostapd_notify_bad_chans(iface);
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index ad0ddf7..c682c6d 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -15,6 +15,7 @@
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features);
int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_csa_update_hwmode(struct hostapd_iface *iface);
int hostapd_acs_completed(struct hostapd_iface *iface, int err);
int hostapd_select_hw_mode(struct hostapd_iface *iface);
const char * hostapd_hw_mode_txt(int mode);
@@ -28,6 +29,7 @@
void hostapd_stop_setup_timers(struct hostapd_iface *iface);
int hostapd_hw_skip_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode);
+int hostapd_determine_mode(struct hostapd_iface *iface);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -40,6 +42,11 @@
return -1;
}
+static inline int hostapd_csa_update_hwmode(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
{
return -1;
@@ -91,6 +98,11 @@
return 0;
}
+static inline int hostapd_determine_mode(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 93a6b4f..1f39107 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -83,6 +83,8 @@
static void handle_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int rssi, int from_queue);
+static int add_associated_sta(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc);
u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
@@ -396,17 +398,38 @@
u8 *buf;
size_t rlen;
int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *sa = hapd->own_addr;
+ struct wpabuf *ml_resp = NULL;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once a non-AP MLD is added to the driver, the addressing should use
+ * 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) {
+ sa = hapd->mld_addr;
+
+ ml_resp = hostapd_ml_auth_resp(hapd);
+ if (!ml_resp)
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+ if (ml_resp)
+ rlen += wpabuf_len(ml_resp);
buf = os_zalloc(rlen);
- if (buf == NULL)
+ if (!buf) {
+ wpabuf_free(ml_resp);
return -1;
+ }
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_AUTH);
os_memcpy(reply->da, dst, ETH_ALEN);
- os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
os_memcpy(reply->bssid, bssid, ETH_ALEN);
reply->u.auth.auth_alg = host_to_le16(auth_alg);
@@ -416,6 +439,14 @@
if (ies && ies_len)
os_memcpy(reply->u.auth.variable, ies, ies_len);
+#ifdef CONFIG_IEEE80211BE
+ if (ml_resp)
+ os_memcpy(reply->u.auth.variable + ies_len,
+ wpabuf_head(ml_resp), wpabuf_len(ml_resp));
+
+ wpabuf_free(ml_resp);
+#endif /* CONFIG_IEEE80211BE */
+
wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
" auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
MAC2STR(dst), auth_alg, auth_transaction,
@@ -509,12 +540,12 @@
}
-static const char * sae_get_password(struct hostapd_data *hapd,
- 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)
+const char * sae_get_password(struct hostapd_data *hapd,
+ 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)
{
const char *password = NULL;
struct sae_password_entry *pw;
@@ -524,7 +555,8 @@
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
- os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+ (!sta ||
+ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0))
continue;
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
@@ -542,7 +574,7 @@
pt = hapd->conf->ssid.pt;
}
- if (!password) {
+ if (!password && sta) {
for (psk = sta->psk; psk; psk = psk->next) {
if (psk->is_passphrase) {
password = psk->passphrase;
@@ -573,12 +605,18 @@
int use_pt = 0;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
use_pt = sta->sae->h2e;
#ifdef CONFIG_SAE_PK
- os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
+ os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
#endif /* CONFIG_SAE_PK */
}
@@ -598,12 +636,12 @@
}
if (update && use_pt &&
- sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
+ sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
NULL, pk) < 0)
return NULL;
if (update && !use_pt &&
- sae_prepare_commit(hapd->own_addr, sta->addr,
+ sae_prepare_commit(own_addr, sta->addr,
(u8 *) password, os_strlen(password),
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
@@ -837,7 +875,15 @@
os_memset(¶ms, 0, sizeof(params));
params.status = status;
- params.bssid = sta->addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta)
+ params.bssid =
+ sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
+#endif /* CONFIG_IEEE80211BE */
+ if (!params.bssid)
+ params.bssid = sta->addr;
+
if (status == WLAN_STATUS_SUCCESS && sta->sae &&
!hapd->conf->disable_pmksa_caching)
params.pmkid = sta->sae->pmkid;
@@ -2748,6 +2794,8 @@
size_t resp_ies_len = 0;
u16 seq_ctrl;
struct radius_sta rad_info;
+ const u8 *dst, *sa, *bssid;
+ bool mld_sta = false;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -2765,6 +2813,20 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
+ sa = mgmt->sa;
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Handle MLO authentication before the station is added to hostapd and
+ * the driver so that the station MLD MAC address would be used in both
+ * hostapd and the driver.
+ */
+ sa = hostapd_process_ml_auth(hapd, mgmt, len);
+ if (sa)
+ mld_sta = true;
+ else
+ sa = mgmt->sa;
+#endif /* CONFIG_IEEE80211BE */
+
auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
@@ -2780,7 +2842,7 @@
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
"auth_transaction=%d status_code=%d wep=%d%s "
"seq_ctrl=0x%x%s%s",
- MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ MAC2STR(sa), auth_alg, auth_transaction,
status_code, !!(fc & WLAN_FC_ISWEP),
challenge ? " challenge" : "",
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
@@ -2846,7 +2908,17 @@
if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (mld_sta &&
+ (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
+ os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+ wpa_printf(MSG_INFO,
+ "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2854,7 +2926,7 @@
if (hapd->conf->no_auth_if_seen_on) {
struct hostapd_data *other;
- other = sta_track_seen_on(hapd->iface, mgmt->sa,
+ other = sta_track_seen_on(hapd->iface, sa,
hapd->conf->no_auth_if_seen_on);
if (other) {
u8 *pos;
@@ -2863,7 +2935,7 @@
wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
MACSTR " since STA has been seen on %s",
- hapd->conf->iface, MAC2STR(mgmt->sa),
+ hapd->conf->iface, MAC2STR(sa),
hapd->conf->no_auth_if_seen_on);
resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
@@ -2906,12 +2978,12 @@
}
}
- res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+ res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
&rad_info);
if (res == HOSTAPD_ACL_REJECT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Ignore Authentication frame from " MACSTR
- " due to ACL reject", MAC2STR(mgmt->sa));
+ " due to ACL reject", MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2921,7 +2993,7 @@
#ifdef CONFIG_SAE
if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
(auth_transaction == 1 ||
- (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
+ (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
/* Handle SAE Authentication commit message through a queue to
* provide more control for postponing the needed heavy
* processing under a possible DoS attack scenario. In addition,
@@ -2934,7 +3006,7 @@
}
#endif /* CONFIG_SAE */
- sta = ap_get_sta(hapd, mgmt->sa);
+ sta = ap_get_sta(hapd, sa);
if (sta) {
sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
sta->ft_over_ds = 0;
@@ -2954,7 +3026,7 @@
sta->plink_state == PLINK_BLOCKED) {
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" is blocked - drop Authentication frame",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
return;
}
#endif /* CONFIG_MESH */
@@ -2974,7 +3046,7 @@
*/
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" not yet known - drop Authentication frame",
- MAC2STR(mgmt->sa));
+ MAC2STR(sa));
/*
* Save a copy of the frame so that it can be processed
* if a new peer entry is added shortly after this.
@@ -2986,13 +3058,38 @@
}
#endif /* CONFIG_MESH */
- sta = ap_sta_add(hapd, mgmt->sa);
+ sta = ap_sta_add(hapd, sa);
if (!sta) {
wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
+
+#ifdef CONFIG_IEEE80211BE
+ if (auth_transaction == 1) {
+ 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;
+ sta->mld_assoc_link_id = link_id;
+
+ /*
+ * Set the MLD address as the station address and the
+ * station addresses.
+ */
+ os_memcpy(sta->mld_info.common_info.mld_addr, sa,
+ ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].peer_addr,
+ mgmt->sa, ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
#ifdef CONFIG_MBO
@@ -3130,7 +3227,22 @@
}
fail:
- reply_res = send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, auth_alg,
+ dst = mgmt->sa;
+ bssid = mgmt->bssid;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once a non-AP MLD is added to the driver, the addressing should use
+ * 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) {
+ dst = sta->addr;
+ bssid = hapd->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
auth_alg == WLAN_AUTH_SAE ?
auth_transaction : auth_transaction + 1,
resp, resp_ies, resp_ies_len,
@@ -3161,10 +3273,51 @@
}
+static u32 hostapd_get_aid_word(struct hostapd_data *hapd,
+ struct sta_info *sta, int i)
+{
+#ifdef CONFIG_IEEE80211BE
+ u32 aid_word = 0;
+
+ /* 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) {
+ int j;
+
+ for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
+ struct hostapd_data *link_bss;
+
+ if (!sta->mld_info.links[j].valid)
+ continue;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, j);
+ if (!link_bss) {
+ /* This shouldn't happen, just skip */
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for AID");
+ continue;
+ }
+
+ aid_word |= link_bss->sta_aid[i];
+ }
+
+ return aid_word;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->sta_aid[i];
+}
+
+
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
{
int i, j = 32, aid;
+ /* Transmitted and non-transmitted BSSIDs share the same AID pool, so
+ * use the shared storage in the transmitted BSS to find the next
+ * available value. */
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
/* get a unique AID */
if (sta->aid > 0) {
wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
@@ -3175,10 +3328,12 @@
return -1;
for (i = 0; i < AID_WORDS; i++) {
- if (hapd->sta_aid[i] == (u32) -1)
+ u32 aid_word = hostapd_get_aid_word(hapd, sta, i);
+
+ if (aid_word == (u32) -1)
continue;
for (j = 0; j < 32; j++) {
- if (!(hapd->sta_aid[i] & BIT(j)))
+ if (!(aid_word & BIT(j)))
break;
}
if (j < 32)
@@ -3548,7 +3703,8 @@
u16 owe_process_rsn_ie(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *rsn_ie, size_t rsn_ie_len,
- const u8 *owe_dh, size_t owe_dh_len)
+ const u8 *owe_dh, size_t owe_dh_len,
+ const u8 *link_addr)
{
u16 status;
u8 *owe_buf, ie[256 * 2];
@@ -3570,6 +3726,11 @@
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto end;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta)
+ wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ sta->mld_assoc_link_id, &sta->mld_info);
+#endif /* CONFIG_IEEE80211BE */
rsn_ie -= 2;
rsn_ie_len += 2;
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
@@ -3614,8 +3775,9 @@
end:
wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
MACSTR, status, (unsigned int) ie_len,
- MAC2STR(sta->addr));
- hostapd_drv_update_dh_ie(hapd, sta->addr, status,
+ MAC2STR(link_addr ? link_addr : sta->addr));
+ hostapd_drv_update_dh_ie(hapd, link_addr ? link_addr : sta->addr,
+ status,
status == WLAN_STATUS_SUCCESS ? ie : NULL,
ie_len);
@@ -3655,7 +3817,8 @@
static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len,
- struct ieee802_11_elems *elems, int reassoc)
+ struct ieee802_11_elems *elems, int reassoc,
+ bool link)
{
int resp;
const u8 *wpa_ie;
@@ -3757,6 +3920,12 @@
elems->eht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ if (!link) {
+ resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -3790,8 +3959,6 @@
if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
"Request - assume WPS is used");
- if (check_sa_query(hapd, sta, reassoc))
- return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
sta->flags |= WLAN_STA_WPS;
wpabuf_free(sta->wps_ie);
sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
@@ -3823,20 +3990,36 @@
if (hapd->conf->wpa && wpa_ie) {
enum wpa_validate_result res;
- if (check_sa_query(hapd, sta, reassoc))
- return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-
wpa_ie -= 2;
wpa_ie_len += 2;
- if (sta->wpa_sm == NULL)
+
+ if (!sta->wpa_sm) {
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info *info = &sta->mld_info;
+#endif /* CONFIG_IEEE80211BE */
+
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
p2p_dev_addr);
- if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_WARNING, "Failed to initialize WPA "
- "state machine");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (!sta->wpa_sm) {
+ wpa_printf(MSG_WARNING,
+ "Failed to initialize RSN state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (info->mld_sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set ML info in RSN Authenticator");
+ wpa_auth_set_ml_info(sta->wpa_sm,
+ hapd->mld_addr,
+ sta->mld_assoc_link_id,
+ info);
+ }
+#endif /* CONFIG_IEEE80211BE */
}
+
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
@@ -3873,6 +4056,8 @@
}
#endif /* CONFIG_IEEE80211R_AP */
+ if (link)
+ goto skip_sae_owe;
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
sta->sae->state == SAE_ACCEPTED)
@@ -3922,6 +4107,7 @@
return resp;
}
#endif /* CONFIG_OWE */
+ skip_sae_owe:
#ifdef CONFIG_DPP2
dpp_pfs_free(sta->dpp_pfs);
@@ -4116,7 +4302,256 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc);
+ return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
+ false);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ u16 status_code,
+ u8 *buf, size_t buflen)
+{
+ u8 *p = buf;
+
+ /* Capability Info */
+ WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
+ p += 2;
+
+ /* Status Code */
+ WPA_PUT_LE16(p, status_code);
+ p += 2;
+
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return p - buf;
+
+ /* AID is not included */
+ p = hostapd_eid_supp_rates(hapd, p);
+ p = hostapd_eid_ext_supp_rates(hapd, p);
+ p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+ p = hostapd_eid_ht_capabilities(hapd, p);
+ p = hostapd_eid_ht_operation(hapd, p);
+
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ p = hostapd_eid_vht_capabilities(hapd, p, 0);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
+
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_spatial_reuse(hapd, p);
+ p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_eht_operation(hapd, p);
+ }
+ }
+
+ p = hostapd_eid_ext_capab(hapd, p, false);
+ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+ p = hostapd_eid_wmm(hapd, p);
+
+ if (hapd->conf->assocresp_elements &&
+ (size_t) (buf + buflen - p) >=
+ wpabuf_len(hapd->conf->assocresp_elements)) {
+ os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+ wpabuf_len(hapd->conf->assocresp_elements));
+ p += wpabuf_len(hapd->conf->assocresp_elements);
+ }
+
+ return p - buf;
+}
+
+
+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)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *mlbuf = NULL;
+ struct sta_info *sta = NULL;
+ u16 status = WLAN_STATUS_SUCCESS;
+
+ wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
+ hapd->mld_link_id, MAC2STR(link->peer_addr));
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+ sta = ap_get_sta(hapd, origin_sta->addr);
+ if (sta) {
+ wpa_printf(MSG_INFO, "MLD: link: Station already exists");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta = NULL;
+ goto out;
+ }
+
+ sta = ap_sta_add(hapd, origin_sta->addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto out;
+ }
+
+ mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf)
+ goto out;
+
+ if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
+ hapd->mld_link_id, true) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: link: Failed to parse association request Multi-Link element");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+ goto out;
+ }
+
+ sta->mld_info.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));
+
+ /*
+ * 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);
+ sta->wpa_sm = NULL;
+
+ /*
+ * Do not initialize the EAPOL state machine.
+ * TODO: Maybe it is needed?
+ */
+ sta->eapol_sm = NULL;
+
+ wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
+ hapd->mld_link_id, sta->aid);
+
+ /*
+ * Get RSNE and RSNXE for the current BSS as they are required by the
+ * Authenticator.
+ */
+ link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
+
+ /* TODO: What other processing is required? */
+
+ if (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);
+
+ link->resp_sta_profile_len =
+ ieee80211_ml_build_assoc_resp(hapd, link->status,
+ link->resp_sta_profile,
+ sizeof(link->resp_sta_profile));
+}
+
+
+bool hostapd_is_mld_ap(struct hostapd_data *hapd)
+{
+ if (!hapd->conf->mld_ap)
+ return false;
+
+ if (!hapd->iface || !hapd->iface->interfaces ||
+ hapd->iface->interfaces->count <= 1)
+ return false;
+
+ return true;
+}
+
+#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)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, j;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ /*
+ * This is not really needed, but make the interaction with the RSN
+ * Authenticator more consistent
+ */
+ sta->mld_info.links[hapd->mld_link_id].rsne =
+ hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ sta->mld_info.links[hapd->mld_link_id].rsnxe =
+ hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ struct hostapd_iface *iface = NULL;
+ struct mld_link_info *link = &sta->mld_info.links[i];
+
+ if (!link->valid)
+ continue;
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ iface = hapd->iface->interfaces->iface[j];
+
+ if (hapd->iface == iface)
+ continue;
+
+ if (iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
+ i == iface->bss[0]->mld_link_id)
+ break;
+ }
+
+ if (!iface || j == hapd->iface->interfaces->count) {
+ 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));
+ } else {
+ ieee80211_ml_process_link(iface->bss[0], sta, link,
+ ies, ies_len, reassoc);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
}
@@ -4150,6 +4585,20 @@
struct ieee80211_he_capabilities he_cap;
struct ieee80211_eht_capabilities eht_cap;
int set = 1;
+ const u8 *mld_link_addr = NULL;
+ bool mld_link_sta = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_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;
+
+ if (hapd->mld_link_id != sta->mld_assoc_link_id)
+ set = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
/*
* Remove the STA entry to ensure the STA PS state gets cleared and
@@ -4178,7 +4627,7 @@
wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
- if (!sta->added_unassoc &&
+ if (!mld_link_sta && !sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
(reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
(!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
@@ -4228,7 +4677,7 @@
sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
- set)) {
+ set, mld_link_addr, mld_link_sta)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
"Could not %s STA to kernel driver",
@@ -4259,6 +4708,7 @@
struct ieee80211_mgmt *reply;
u8 *p;
u16 res = WLAN_STATUS_SUCCESS;
+ const u8 *sa = hapd->own_addr;
buflen = sizeof(struct ieee80211_mgmt) + 1024;
#ifdef CONFIG_FILS
@@ -4294,9 +4744,19 @@
IEEE80211_FC(WLAN_FC_TYPE_MGMT,
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * 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)
+ sa = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
os_memcpy(reply->da, addr, ETH_ALEN);
- os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
send_len = IEEE80211_HDRLEN;
send_len += sizeof(reply->u.assoc_resp);
@@ -4432,6 +4892,8 @@
#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_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_eht_operation(hapd, p);
}
@@ -4704,6 +5166,7 @@
int delay_assoc = 0;
#endif /* CONFIG_FILS */
int omit_rsnxe = 0;
+ bool set_beacon = false;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -4879,6 +5342,11 @@
}
#endif /* CONFIG_MBO */
+ if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) {
+ resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+ goto fail;
+ }
+
/*
* sta->capability is used in check_assoc_ies() for RRM enabled
* capability element.
@@ -4940,7 +5408,7 @@
sta->nonerp_set = 1;
hapd->iface->num_sta_non_erp++;
if (hapd->iface->num_sta_non_erp == 1)
- ieee802_11_set_beacons(hapd->iface);
+ set_beacon = true;
}
if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
@@ -4951,7 +5419,7 @@
hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 1)
- ieee802_11_set_beacons(hapd->iface);
+ set_beacon = true;
}
if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
@@ -4966,10 +5434,11 @@
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 1)
- ieee802_11_set_beacons(hapd->iface);
+ set_beacon = true;
}
- update_ht_state(hapd, sta);
+ if (update_ht_state(hapd, sta) > 0)
+ set_beacon = true;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -5008,6 +5477,9 @@
}
#endif /* CONFIG_FILS */
+ if (set_beacon)
+ ieee802_11_set_beacons(hapd->iface);
+
fail:
/*
@@ -5028,6 +5500,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 (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
@@ -5087,27 +5562,37 @@
}
-static void handle_disassoc(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+static void hostapd_deauth_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt)
{
- struct sta_info *sta;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "deauthentication: STA=" MACSTR " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
- wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
- (unsigned long) len);
- return;
- }
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ mlme_deauthenticate_indication(
+ hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
- wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
- MAC2STR(mgmt->sa),
- le_to_host16(mgmt->u.disassoc.reason_code));
- sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL) {
- wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
- MAC2STR(mgmt->sa));
- return;
- }
+static void hostapd_disassoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt)
+{
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "disassocation: STA=" MACSTR " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code));
ap_sta_set_authorized(hapd, sta, 0);
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
@@ -5152,45 +5637,173 @@
}
+#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,
+ bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+ unsigned int i, link_id;
+
+ 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);
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
+ tmp_hapd =
+ assoc_hapd->iface->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) {
+ /*
+ * Remove the station on which the association
+ * was done only after all other link stations
+ * are removed. Since there is only a single
+ * station per struct hostapd_hapd with the
+ * same association link simply break out from
+ * the loop.
+ */
+ 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)
+ hostapd_deauth_sta(tmp_hapd, tmp_sta,
+ mgmt);
+ else
+ hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+ mgmt);
+ break;
+ }
+ }
+ }
+
+ /* Remove the station on which the association was performed. */
+ if (!disassoc)
+ hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
+ else
+ hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
+
+ return true;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "handle_disassoc - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " trying to disassociate, but it is not associated",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
+ return;
+
+ hostapd_disassoc_sta(hapd, sta, mgmt);
+}
+
+
static void handle_deauth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
- "payload (len=%lu)", (unsigned long) len);
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "handle_deauth - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
- " reason_code=%d",
- MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
-
/* Clear the PTKSA cache entries for PASN */
ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
- "to deauthenticate, but it is not authenticated",
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " trying to deauthenticate, but it is not authenticated",
MAC2STR(mgmt->sa));
return;
}
- ap_sta_set_authorized(hapd, sta, 0);
- sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
- WLAN_STA_ASSOC_REQ_OK);
- hostapd_set_sta_flags(hapd, sta);
- wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "deauthenticated");
- mlme_deauthenticate_indication(
- hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
- sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
- ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
- ap_free_sta(hapd, sta);
+ if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
+ return;
+
+ hostapd_deauth_sta(hapd, sta, mgmt);
}
@@ -5495,6 +6108,10 @@
#ifdef CONFIG_MESH
!(hapd->conf->mesh & MESH_ENABLED) &&
#endif /* CONFIG_MESH */
+#ifdef CONFIG_IEEE80211BE
+ !(hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
MAC2STR(mgmt->bssid));
@@ -5514,6 +6131,10 @@
if ((!is_broadcast_ether_addr(mgmt->da) ||
stype != WLAN_FC_STYPE_ACTION) &&
+#ifdef CONFIG_IEEE80211BE
+ !(hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -5658,6 +6279,90 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void ieee80211_ml_link_sta_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct mld_link_info *link,
+ bool ok)
+{
+ if (!ok) {
+ hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+
+ /* The STA is added only in case of SUCCESS */
+ if (link->status == WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+
+ return;
+ }
+
+ if (link->status != WLAN_STATUS_SUCCESS)
+ return;
+
+ sta->flags |= WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+ ap_sta_set_authorized(hapd, sta, 1);
+
+ hostapd_set_sta_flags(hapd, sta);
+
+ /*
+ * TODOs:
+ * - IEEE 802.1X port enablement is not needed as done on the station
+ * doing the connection.
+ * - Not handling accounting
+ * - Need to handle VLAN configuration
+ */
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta, bool ok)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, link_id;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+ if (!link->valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd =
+ hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (tmp_hapd->conf->mld_ap ||
+ hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
+ continue;
+
+ ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
+ tmp_sta, link,
+ ok);
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
static void handle_assoc_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int reassoc, int ok)
@@ -5673,6 +6378,17 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_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,
+ "%s: MLD: ignore on link station (%d != %d)",
+ __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+ return;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
sizeof(mgmt->u.assoc_resp))) {
wpa_printf(MSG_INFO,
@@ -5696,11 +6412,11 @@
if (status == WLAN_STATUS_SUCCESS)
hostapd_drv_sta_remove(hapd, sta->addr);
- return;
+ goto handle_ml;
}
if (status != WLAN_STATUS_SUCCESS)
- return;
+ goto handle_ml;
/* Stop previous accounting session, if one is started, and allocate
* new session id for the new session. */
@@ -5742,11 +6458,11 @@
* interface selection is not going to change anymore.
*/
if (ap_sta_bind_vlan(hapd, sta) < 0)
- return;
+ goto handle_ml;
} else if (sta->vlan_id) {
/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
if (ap_sta_bind_vlan(hapd, sta) < 0)
- return;
+ goto handle_ml;
}
hostapd_set_sta_flags(hapd, sta);
@@ -5762,7 +6478,7 @@
/* WPS not supported on backhaul BSS. Disable 4addr mode on fronthaul */
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
- !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ (hapd->conf->multi_ap & BACKHAUL_BSS) &&
!(sta->flags & WLAN_STA_WPS))) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
@@ -5814,6 +6530,9 @@
os_free(sta->pending_eapol_rx);
sta->pending_eapol_rx = NULL;
}
+
+handle_ml:
+ hostapd_ml_handle_assoc_cb(hapd, sta, ok);
}
@@ -6343,7 +7062,7 @@
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
{
- u8 bw, chan1, chan2 = 0;
+ u8 bw, chan1 = 0, chan2 = 0;
int freq1;
if (!hapd->cs_freq_params.channel ||
@@ -6352,20 +7071,17 @@
!hapd->cs_freq_params.eht_enabled))
return eid;
- /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80, 4: 320 */
+ /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4: 320 as per
+ * IEEE P802.11-REVme/D4.0, 9.4.2.159 and Table 9-314. */
switch (hapd->cs_freq_params.bandwidth) {
case 40:
bw = 0;
break;
case 80:
- /* check if it's 80+80 */
- if (!hapd->cs_freq_params.center_freq2)
- bw = 1;
- else
- bw = 3;
+ bw = 1;
break;
case 160:
- bw = 2;
+ bw = 1;
break;
case 320:
bw = 4;
@@ -6392,6 +7108,21 @@
*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
*eid++ = bw; /* New Channel Width */
+ if (hapd->cs_freq_params.bandwidth == 160) {
+ /* Update the CCFS0 and CCFS1 values in the element based on
+ * IEEE P802.11-REVme/D4.0, Table 9-314 */
+
+ /* CCFS1 - The channel center frequency index of the 160 MHz
+ * channel. */
+ chan2 = chan1;
+
+ /* CCFS0 - The channel center frequency index of the 80 MHz
+ * channel segment that contains the primary channel. */
+ if (hapd->cs_freq_params.channel < chan1)
+ chan1 -= 8;
+ else
+ chan1 += 8;
+ }
*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
@@ -6443,12 +7174,19 @@
size_t total_len = 0, len = *current_len;
int tbtt_count = 0;
size_t i, start = 0;
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
while (start < hapd->iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
len = RNR_HEADER_LEN;
total_len += RNR_HEADER_LEN;
+ tbtt_count = 0;
}
len += RNR_TBTT_HEADER_LEN;
@@ -6472,8 +7210,13 @@
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
break;
- len += RNR_TBTT_INFO_LEN;
- total_len += RNR_TBTT_INFO_LEN;
+ if (!ap_mld) {
+ len += RNR_TBTT_INFO_LEN;
+ total_len += RNR_TBTT_INFO_LEN;
+ } else {
+ len += RNR_TBTT_INFO_MLD_LEN;
+ total_len += RNR_TBTT_INFO_MLD_LEN;
+ }
tbtt_count++;
}
start = i;
@@ -6528,8 +7271,8 @@
}
-static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
- size_t *current_len)
+static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t len = 0;
@@ -6540,9 +7283,16 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+ ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
if (iface == hapd->iface ||
- !is_6ghz_op_class(iface->conf->op_class))
+ !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
continue;
len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
@@ -6557,6 +7307,11 @@
{
size_t total_len = 0, current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
@@ -6565,9 +7320,10 @@
/* fallthrough */
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND)
- total_len += hostapd_eid_rnr_colocation_len(
- hapd, ¤t_len);
+ if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ total_len +=
+ hostapd_eid_rnr_multi_iface_len(hapd,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
@@ -6657,6 +7413,11 @@
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;
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
return eid;
@@ -6670,7 +7431,8 @@
while (start < iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
eid_start = eid;
*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
size_offset = eid++;
@@ -6679,7 +7441,7 @@
}
tbtt_count_pos = eid++;
- *eid++ = RNR_TBTT_INFO_LEN;
+ *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
*eid++ = op_class;
*eid++ = hapd->iconf->channel;
len += RNR_TBTT_HEADER_LEN;
@@ -6728,7 +7490,18 @@
*eid++ = bss_param;
*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
- len += RNR_TBTT_INFO_LEN;
+
+ 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;
}
@@ -6745,8 +7518,8 @@
}
-static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
- size_t *current_len)
+static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t i;
@@ -6756,9 +7529,16 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+ ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
if (iface == hapd->iface ||
- !is_6ghz_op_class(iface->conf->op_class))
+ !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
continue;
eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
@@ -6774,6 +7554,11 @@
u8 *eid_start = eid;
size_t current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
@@ -6782,9 +7567,9 @@
/* fallthrough */
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND)
- eid = hostapd_eid_rnr_colocation(hapd, eid,
- ¤t_len);
+ if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
@@ -6979,7 +7764,13 @@
(conf->dtim_period % elem_count))
conf->dtim_period = elem_count;
*eid++ = conf->dtim_period;
- *eid++ = 0xFF; /* DTIM Count */
+ /* The driver is expected to update the DTIM Count
+ * field for each BSS that corresponds to a
+ * nontransmitted BSSID. The value is initialized to
+ * 0 here so that the DTIM count would be somewhat
+ * functional even if the driver were not to update
+ * this. */
+ *eid++ = 0; /* DTIM Count */
} else {
/* Probe Request frame does not include DTIM Period and
* DTIM Count fields. */
@@ -7010,11 +7801,12 @@
non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
if (ie_count) {
*eid++ = WLAN_EID_EXTENSION;
- *eid++ = 2 + ie_count;
+ *eid++ = 2 + ie_count + 1;
*eid++ = WLAN_EID_EXT_NON_INHERITANCE;
*eid++ = ie_count;
os_memcpy(eid, non_inherit_ie, ie_count);
eid += ie_count;
+ *eid++ = 0; /* No Element ID Extension List */
}
*eid_len_pos = (eid - eid_len_pos) - 1;
@@ -7099,8 +7891,8 @@
if (hapd->conf->rnr)
rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
- rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
- &cur_len);
+ rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+ &cur_len);
}
return eid;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 1190a5e..4b58fee 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -19,6 +19,10 @@
struct radius_sta;
enum ieee80211_op_mode;
enum oper_chan_width;
+struct ieee802_11_elems;
+struct sae_pk;
+struct sae_pt;
+struct sae_password_entry;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -84,13 +88,22 @@
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);
+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,
+ size_t len);
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+ struct ieee802_11_elems *elems,
+ struct sta_info *sta);
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);
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ie, size_t len);
-void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+int update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
@@ -177,7 +190,8 @@
u8 *owe_buf, size_t owe_buf_len, u16 *status);
u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *rsn_ie, size_t rsn_ie_len,
- const u8 *owe_dh, size_t owe_dh_len);
+ const u8 *owe_dh, size_t owe_dh_len,
+ const u8 *link_addr);
u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
const u8 *rsn_ie, size_t rsn_ie_len,
const u8 *owe_dh, size_t owe_dh_len);
@@ -226,5 +240,10 @@
u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
enum oper_chan_width *width, u8 *seg0, u8 *seg1);
+bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+const char * sae_get_password(struct hostapd_data *hapd,
+ 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);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index 6ebe0f9..1d17518 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -8,6 +8,8 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_groups.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ieee802_11.h"
@@ -417,3 +419,732 @@
os_memset(dest, 0, sizeof(*dest));
os_memcpy(dest, src, len);
}
+
+
+u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
+ struct sta_info *info, bool include_mld_id)
+{
+ struct wpabuf *buf;
+ u16 control;
+ u8 *pos = eid;
+ const u8 *ptr;
+ size_t len, slice_len;
+ u8 link_id;
+ u8 common_info_len;
+
+ /*
+ * As the Multi-Link element can exceed the size of 255 bytes need to
+ * first build it and then handle fragmentation.
+ */
+ buf = wpabuf_alloc(1024);
+ if (!buf)
+ return pos;
+
+ /* Multi-Link Control field */
+ control = MULTI_LINK_CONTROL_TYPE_BASIC |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+
+ /*
+ * Set the basic Multi-Link common information. Hard code the common
+ * info length to 13 based on the length of the present fields:
+ * Length (1) + MLD address (6) + Link ID (1) +
+ * BSS Parameters Change Count (1) + EML Capabilities (2) +
+ * MLD Capabilities and Operations (2)
+ */
+ common_info_len = 13;
+
+ if (include_mld_id) {
+ /* AP MLD ID */
+ control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+ common_info_len++;
+ }
+
+ wpabuf_put_le16(buf, control);
+
+ wpabuf_put_u8(buf, common_info_len);
+
+ /* Own MLD MAC Address */
+ wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+ /* Own Link ID */
+ wpabuf_put_u8(buf, hapd->mld_link_id);
+
+ /* Currently hard code the BSS Parameters Change Count to 0x1 */
+ wpabuf_put_u8(buf, 0x1);
+
+ wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+ hapd->iface->mld_eml_capa);
+ wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
+
+ wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
+ hapd->iface->mld_mld_capa);
+ wpabuf_put_le16(buf, hapd->iface->mld_mld_capa);
+
+ if (include_mld_id) {
+ wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
+ hapd->conf->mld_id);
+ wpabuf_put_u8(buf, hapd->conf->mld_id);
+ }
+
+ if (!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 hostapd_data *link_bss;
+
+ /*
+ * control (2) + station info length (1) + MAC address (6) +
+ * 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;
+
+ /* Skip the local one */
+ if (link_id == hapd->mld_link_id || !link->valid)
+ continue;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!link_bss) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Couldn't find link BSS - skip it");
+ continue;
+ }
+
+ /* Per-STA Profile subelement */
+ wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
+
+ if (total_len <= 255)
+ wpabuf_put_u8(buf, total_len);
+ else
+ wpabuf_put_u8(buf, 255);
+
+ /* STA Control */
+ control = (link_id & 0xf) |
+ EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK |
+ EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
+ EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
+ EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+ EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
+ EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+ wpabuf_put_le16(buf, control);
+
+ /* STA Info */
+
+ /* STA Info Length */
+ wpabuf_put_u8(buf, fixed_len - 2);
+ wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
+ wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
+
+ /* TSF Offset */
+ /*
+ * TODO: Currently setting TSF offset to zero. However, this
+ * information needs to come from the driver.
+ */
+ wpabuf_put_le64(buf, 0);
+
+ /* DTIM Info */
+ 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);
+
+ /* Fragment the sub element if needed */
+ if (total_len <= 255) {
+ wpabuf_put_data(buf, link->resp_sta_profile,
+ link->resp_sta_profile_len);
+ } else {
+ ptr = link->resp_sta_profile;
+ len = link->resp_sta_profile_len;
+
+ slice_len = 255 - fixed_len;
+
+ wpabuf_put_data(buf, ptr, slice_len);
+ len -= slice_len;
+ ptr += slice_len;
+
+ while (len) {
+ if (len <= 255)
+ slice_len = len;
+ else
+ slice_len = 255;
+
+ wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT);
+ wpabuf_put_u8(buf, slice_len);
+ wpabuf_put_data(buf, ptr, slice_len);
+
+ len -= slice_len;
+ ptr += slice_len;
+ }
+ }
+ }
+
+out:
+ /* Fragment the Multi-Link element, if needed */
+ len = wpabuf_len(buf);
+ ptr = wpabuf_head(buf);
+
+ if (len <= 254)
+ slice_len = len;
+ else
+ slice_len = 254;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = slice_len + 1;
+ *pos++ = WLAN_EID_EXT_MULTI_LINK;
+ os_memcpy(pos, ptr, slice_len);
+
+ ptr += slice_len;
+ pos += slice_len;
+ len -= slice_len;
+
+ while (len) {
+ if (len <= 255)
+ slice_len = len;
+ else
+ slice_len = 255;
+
+ *pos++ = WLAN_EID_FRAGMENT;
+ *pos++ = slice_len;
+ os_memcpy(pos, ptr, slice_len);
+
+ ptr += slice_len;
+ pos += slice_len;
+ len -= slice_len;
+ }
+
+ wpabuf_free(buf);
+ return pos;
+}
+
+
+struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
+{
+ struct wpabuf *buf = wpabuf_alloc(12);
+
+ if (!buf)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 10);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
+ wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
+ wpabuf_put_u8(buf, ETH_ALEN + 1);
+ wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+ return buf;
+}
+
+
+#ifdef CONFIG_SAE
+
+static const u8 *
+sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
+ const u8 *pos, u16 status_code)
+{
+ u16 group;
+ size_t prime_len;
+ struct crypto_ec *ec;
+
+ if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ return pos;
+
+ /* SAE H2E commit message (group, scalar, FFE) */
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE Group is not present");
+ return NULL;
+ }
+
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* TODO: How to parse when the group is unknown? */
+ ec = crypto_ec_init(group);
+ if (!ec) {
+ const struct dh_group *dh = dh_groups_get(group);
+
+ if (!dh) {
+ wpa_printf(MSG_DEBUG, "EHT: Unknown SAE group %u",
+ group);
+ return NULL;
+ }
+
+ prime_len = dh->prime_len;
+ } else {
+ prime_len = crypto_ec_prime_len(ec);
+ }
+
+ wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
+
+ /* scalar */
+ pos += prime_len;
+
+ if (ec) {
+ pos += prime_len * 2;
+ crypto_ec_deinit(ec);
+ } else {
+ pos += prime_len;
+ }
+
+ if (pos - mgmt->u.auth.variable > (int) len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: Too short SAE commit Authentication frame");
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements",
+ pos, (int) len - (pos - mgmt->u.auth.variable));
+
+ return pos;
+}
+
+
+static const u8 *
+sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ const u8 *pos, u16 status_code)
+{
+ struct sta_info *sta;
+
+ if (status_code == WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
+ return pos;
+
+ /* send confirm integer */
+ pos += 2;
+
+ /*
+ * At this stage we should already have an MLD station and actually SA
+ * will be replaced with the MLD MAC address by the driver.
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
+ return NULL;
+ }
+
+ if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
+ if (sta->sae)
+ wpa_printf(MSG_DEBUG, "SAE: Invalid state=%u",
+ sta->sae->state);
+ else
+ wpa_printf(MSG_DEBUG, "SAE: No SAE context");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
+ sta->sae->tmp->kck_len);
+
+ pos += sta->sae->tmp->kck_len;
+
+ if (pos - mgmt->u.auth.variable > (int) len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: Too short SAE confirm Authentication frame");
+ return NULL;
+ }
+
+ return pos;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+#ifdef CONFIG_SAE
+ u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ u16 status_code = le_to_host16(mgmt->u.auth.status_code);
+#endif /* CONFIG_SAE */
+ const u8 *pos = mgmt->u.auth.variable;
+
+ /* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69
+ * (Presence of fields and elements in Authentications frames) */
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ return pos;
+#ifdef CONFIG_SAE
+ case WLAN_AUTH_SAE:
+ if (auth_transaction == 1) {
+ if (status_code == WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE H2E is mandatory for MLD");
+ goto out;
+ }
+
+ return sae_commit_skip_fixed_fields(mgmt, len, pos,
+ status_code);
+ } else if (auth_transaction == 2) {
+ return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
+ pos, status_code);
+ }
+
+ return pos;
+#endif /* CONFIG_SAE */
+ /* TODO: Support additional algorithms that can be used for MLO */
+ case WLAN_AUTH_FT:
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ case WLAN_AUTH_FILS_PK:
+ case WLAN_AUTH_PASN:
+ default:
+ break;
+ }
+
+#ifdef CONFIG_SAE
+out:
+#endif /* CONFIG_SAE */
+ wpa_printf(MSG_DEBUG,
+ "TODO: Authentication algorithm %u not supported with MLD",
+ auth_alg);
+ return NULL;
+}
+
+
+const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ pos = auth_skip_fixed_fields(hapd, mgmt, len);
+ if (!pos)
+ return NULL;
+
+ if (ieee802_11_parse_elems(pos,
+ (int)len - (pos - mgmt->u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed parsing Authentication frame");
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len)
+ return NULL;
+
+ return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+}
+
+
+static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ struct mld_info *info)
+{
+ u8 i, link_id;
+
+ if (!info->mld_sta) {
+ wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
+ return 0;
+ }
+
+ /*
+ * Iterate over the links negotiated in the (Re)Association Request
+ * frame and validate that they are indeed valid links in the local AP
+ * MLD.
+ *
+ * While at it, also update the local address for the links in the
+ * mld_info, so it could be easily available for later flows, e.g., for
+ * the RSN Authenticator, etc.
+ */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct hostapd_data *other_hapd;
+
+ if (!info->links[link_id].valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (hapd == other_hapd)
+ continue;
+
+ if (other_hapd->conf->mld_ap &&
+ other_hapd->conf->mld_id == hapd->conf->mld_id &&
+ link_id == other_hapd->mld_link_id)
+ break;
+ }
+
+ if (i == hapd->iface->interfaces->count &&
+ link_id != hapd->mld_link_id) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+ link_id);
+ return -1;
+ }
+
+ if (i < hapd->iface->interfaces->count)
+ os_memcpy(info->links[link_id].local_addr,
+ other_hapd->own_addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+ struct ieee802_11_elems *elems,
+ struct sta_info *sta)
+{
+ struct wpabuf *mlbuf;
+ const struct ieee80211_eht_ml *ml;
+ const struct eht_ml_basic_common_info *common_info;
+ size_t ml_len, common_info_len;
+ struct mld_link_info *link_info;
+ struct mld_info *info = &sta->mld_info;
+ const u8 *pos;
+ int ret = -1;
+ u16 ml_control;
+
+ mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf)
+ return WLAN_STATUS_SUCCESS;
+
+ ml = wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+
+ 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 params change not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ common_info_len += 2;
+ } else {
+ wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ common_info_len += 2;
+
+ } else {
+ wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
+ common_info_len);
+
+ if (sizeof(*ml) + common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+ goto out;
+ }
+
+ common_info = (const 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 (expected %zu)",
+ common_info->len, common_info_len);
+ goto out;
+ }
+
+ pos = common_info->variable;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ info->common_info.eml_capa = WPA_GET_LE16(pos);
+ pos += 2;
+ } else {
+ info->common_info.eml_capa = 0;
+ }
+
+ info->common_info.mld_capa = WPA_GET_LE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
+ MAC2STR(info->common_info.mld_addr),
+ 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) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: MLD address mismatch between authentication ("
+ MACSTR ") and association (" MACSTR ")",
+ MAC2STR(info->common_info.mld_addr),
+ MAC2STR(common_info->mld_addr));
+ goto out;
+ }
+
+ info->links[hapd->mld_link_id].valid = true;
+
+ /* Parse the link info field */
+ ml_len -= sizeof(*ml) + common_info_len;
+
+ while (ml_len > 2) {
+ size_t sub_elem_len = *(pos + 1);
+ size_t sta_info_len;
+ u16 control;
+
+ wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
+ sub_elem_len);
+
+ if (2 + sub_elem_len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid link info len: %zu %zu",
+ 2 + sub_elem_len, ml_len);
+ goto out;
+ }
+
+ if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip vendor specific subelement");
+
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
+ }
+
+ if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Multi-Link element subelement ID=%u",
+ *pos);
+ goto out;
+ }
+
+ /* Skip the subelement ID and the length */
+ pos += 2;
+ ml_len -= 2;
+
+ /* Get the station control field */
+ if (sub_elem_len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Too short Per-STA Profile subelement");
+ goto out;
+ }
+ control = WPA_GET_LE16(pos);
+ link_info = &info->links[control &
+ EHT_PER_STA_CTRL_LINK_ID_MSK];
+ pos += 2;
+ ml_len -= 2;
+ sub_elem_len -= 2;
+
+ if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per-STA complete profile expected");
+ goto out;
+ }
+
+ if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per-STA MAC address not present");
+ goto out;
+ }
+
+ if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+ EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Beacon/DTIM interval not expected");
+ goto out;
+ }
+
+ /* The length octet and the MAC address must be present */
+ sta_info_len = 1 + ETH_ALEN;
+
+ if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
+ if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
+ link_info->nstr_bitmap_len = 2;
+ else
+ link_info->nstr_bitmap_len = 1;
+ }
+
+ sta_info_len += link_info->nstr_bitmap_len;
+
+ if (sta_info_len > ml_len || sta_info_len != *pos ||
+ sta_info_len > sub_elem_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
+ goto out;
+ }
+
+ /* skip the length */
+ pos++;
+ ml_len--;
+
+ /* get the link address */
+ os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "MLD: assoc: link id=%u, addr=" MACSTR,
+ control & EHT_PER_STA_CTRL_LINK_ID_MSK,
+ MAC2STR(link_info->peer_addr));
+
+ pos += ETH_ALEN;
+ ml_len -= ETH_ALEN;
+
+ /* Get the NSTR bitmap */
+ if (link_info->nstr_bitmap_len) {
+ os_memcpy(link_info->nstr_bitmap, pos,
+ link_info->nstr_bitmap_len);
+ pos += link_info->nstr_bitmap_len;
+ ml_len -= link_info->nstr_bitmap_len;
+ }
+
+ sub_elem_len -= sta_info_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
+ if (sub_elem_len > ml_len)
+ goto out;
+
+ if (sub_elem_len > 2)
+ link_info->capability = WPA_GET_LE16(pos);
+
+ pos += sub_elem_len;
+ ml_len -= sub_elem_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
+ ", nstr bitmap len=%lu",
+ control, MAC2STR(link_info->peer_addr),
+ link_info->nstr_bitmap_len);
+
+ link_info->valid = true;
+ }
+
+ if (ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
+ ml_len);
+ goto out;
+ }
+
+ ret = hostapd_mld_validate_assoc_info(hapd, info);
+out:
+ wpabuf_free(mlbuf);
+ if (ret) {
+ os_memset(info, 0, sizeof(*info));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 59ecbdc..f90f125 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -479,15 +479,14 @@
}
-void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+int update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
{
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
update_sta_ht(hapd, sta);
else
update_sta_no_ht(hapd, sta);
- if (hostapd_ht_operation_update(hapd->iface) > 0)
- ieee802_11_set_beacons(hapd->iface);
+ return hostapd_ht_operation_update(hapd->iface);
}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 8b67669..052231e 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -95,39 +95,43 @@
if (sta->flags & WLAN_STA_PREAUTH) {
rsn_preauth_send(hapd, sta, buf, len);
} else {
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
hostapd_drv_hapd_send_eapol(
hapd, sta->addr, buf, len,
- encrypt, hostapd_sta_flags_to_drv(sta->flags));
+ encrypt, hostapd_sta_flags_to_drv(sta->flags), link_id);
}
os_free(buf);
}
-void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
- struct sta_info *sta, int authorized)
+static void ieee802_1x_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ bool authorized, bool mld)
{
int res;
if (sta->flags & WLAN_STA_PREAUTH)
return;
- if (authorized) {
- ap_sta_set_authorized(hapd, sta, 1);
- res = hostapd_set_authorized(hapd, sta, 1);
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
- HOSTAPD_LEVEL_DEBUG, "authorizing port");
- } else {
- ap_sta_set_authorized(hapd, sta, 0);
- res = hostapd_set_authorized(hapd, sta, 0);
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
- HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
- }
+ ap_sta_set_authorized(hapd, sta, authorized);
+ res = hostapd_set_authorized(hapd, sta, authorized);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
+ authorized ? "" : "un");
- if (res && errno != ENOENT) {
+ if (!mld && res && errno != ENOENT) {
wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
" flags for kernel driver (errno=%d).",
MAC2STR(sta->addr), errno);
+ } else if (mld && res) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Could not set station " MACSTR " flags",
+ MAC2STR(sta->addr));
}
if (authorized) {
@@ -137,6 +141,65 @@
}
+static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ bool authorized)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, link_id;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ /*
+ * Authorizing the station should be done only in the station
+ * performing the association
+ */
+ if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
+ return;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+ if (!link->valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd =
+ hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
+ continue;
+
+ ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+ authorized, true);
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ ieee802_1x_set_authorized(hapd, sta, authorized, false);
+ ieee802_1x_ml_set_sta_authorized(hapd, sta, !!authorized);
+}
+
+
#ifdef CONFIG_WEP
#ifndef CONFIG_FIPS
#ifndef CONFIG_NO_RC4
@@ -2474,6 +2537,14 @@
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using IEEE 802.1X state machine of the first BSS");
+
+ hapd->eapol_auth = hapd->mld_first_bss->eapol_auth;
+ return 0;
+ }
+
dl_list_init(&hapd->erp_keys);
os_memset(&conf, 0, sizeof(conf));
@@ -2558,6 +2629,14 @@
void ieee802_1x_deinit(struct hostapd_data *hapd)
{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
+
+ hapd->eapol_auth = NULL;
+ return;
+ }
+
#ifdef CONFIG_WEP
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
#endif /* CONFIG_WEP */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 32d291d..ee4232f 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -56,7 +56,9 @@
unsigned int hash;
pmksa->pmksa_count--;
- pmksa->free_cb(entry, pmksa->ctx);
+
+ if (pmksa->free_cb)
+ pmksa->free_cb(entry, pmksa->ctx);
/* unlink from hash list */
hash = PMKID_HASH(entry->pmkid);
@@ -332,6 +334,10 @@
return NULL;
os_memcpy(entry->pmk, pmk, pmk_len);
entry->pmk_len = pmk_len;
+ if (kck && kck_len && kck_len < WPA_KCK_MAX_LEN) {
+ os_memcpy(entry->kck, kck, kck_len);
+ entry->kck_len = kck_len;
+ }
if (pmkid)
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
@@ -523,8 +529,17 @@
return entry;
continue;
}
- rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
- entry->akmp);
+ if (entry->akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 &&
+ entry->kck_len > 0)
+ rsn_pmkid_suite_b_192(entry->kck, entry->kck_len,
+ aa, spa, new_pmkid);
+ else if (wpa_key_mgmt_suite_b(entry->akmp) &&
+ entry->kck_len > 0)
+ rsn_pmkid_suite_b(entry->kck, entry->kck_len, aa, spa,
+ new_pmkid);
+ else
+ rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa,
+ new_pmkid, entry->akmp);
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index e3cee4a..e38e7ec 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -19,6 +19,8 @@
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
+ u8 kck[WPA_KCK_MAX_LEN];
+ size_t kck_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 spa[ETH_ALEN];
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 07100f2..a00f896 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -199,7 +199,7 @@
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
- !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ (hapd->conf->multi_ap & BACKHAUL_BSS) &&
!(sta->flags & WLAN_STA_WPS)))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
@@ -290,7 +290,7 @@
#endif /* CONFIG_MESH */
if (set_beacon)
- ieee802_11_set_beacons(hapd->iface);
+ ieee802_11_update_beacons(hapd->iface);
wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
__func__, MAC2STR(sta->addr));
@@ -301,7 +301,15 @@
sae_clear_retransmit_timer(hapd, sta);
ieee802_1x_free_station(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+ hapd->mld_link_id == sta->mld_assoc_link_id)
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#else
wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
rsn_preauth_free_station(hapd, sta);
#ifndef CONFIG_NO_RADIUS
if (hapd->radius)
@@ -866,7 +874,14 @@
ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(hapd, sta);
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap ||
+ hapd->mld_link_id == sta->mld_assoc_link_id)
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#else
wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
sta->wpa_sm = NULL;
sta->disassoc_reason = reason;
@@ -1071,6 +1086,12 @@
struct hostapd_vlan *vlan = NULL;
int ret;
int old_vlanid = sta->vlan_id_bound;
+ int mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ mld_link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) {
wpa_printf(MSG_DEBUG,
@@ -1128,7 +1149,8 @@
if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
- ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+ ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id,
+ mld_link_id);
if (ret < 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
@@ -1285,7 +1307,19 @@
return;
if (authorized) {
- hostapd_prune_associations(hapd, sta->addr);
+ int mld_assoc_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ mld_assoc_link_id = sta->mld_assoc_link_id;
+ else
+ mld_assoc_link_id = -2;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ if (mld_assoc_link_id != -2)
+ hostapd_prune_associations(hapd, sta->addr,
+ mld_assoc_link_id);
sta->flags |= WLAN_STA_AUTHORIZED;
} else {
sta->flags &= ~WLAN_STA_AUTHORIZED;
@@ -1572,6 +1606,9 @@
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
{
+ const u8 *mld_link_addr = NULL;
+ bool mld_link_sta = false;
+
/*
* If a station that is already associated to the AP, is trying to
* authenticate again, remove the STA entry, in order to make sure the
@@ -1579,6 +1616,16 @@
* this, station's added_unassoc flag is cleared once the station has
* completed association.
*/
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_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;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
ap_sta_set_authorized(hapd, sta, 0);
hostapd_drv_sta_remove(hapd, sta->addr);
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
@@ -1587,7 +1634,8 @@
sta->supported_rates,
sta->supported_rates_len,
0, NULL, NULL, NULL, 0, NULL, 0, NULL,
- sta->flags, 0, 0, 0, 0)) {
+ sta->flags, 0, 0, 0, 0,
+ mld_link_addr, mld_link_sta)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 8433ff8..e2b9dde 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -69,6 +69,35 @@
enum frame_encryption encrypted;
};
+#define EHT_ML_MAX_STA_PROF_LEN 1024
+struct mld_info {
+ bool mld_sta;
+
+ struct ml_common_info {
+ u8 mld_addr[ETH_ALEN];
+ u16 medium_sync_delay;
+ u16 eml_capa;
+ u16 mld_capa;
+ } common_info;
+
+ struct mld_link_info {
+ u8 valid;
+ 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];
+
+ const u8 *rsne, *rsnxe;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
@@ -299,6 +328,11 @@
#ifdef CONFIG_PASN
struct pasn_data *pasn;
#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info mld_info;
+ u8 mld_assoc_link_id;
+#endif /* CONFIG_IEEE80211BE */
};
diff --git a/src/ap/utils.c b/src/ap/utils.c
index bedad6e..e93e531 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -43,6 +43,7 @@
struct prune_data {
struct hostapd_data *hapd;
const u8 *addr;
+ int mld_assoc_link_id;
};
static int prune_associations(struct hostapd_iface *iface, void *ctx)
@@ -72,6 +73,12 @@
if (!osta)
continue;
+#ifdef CONFIG_IEEE80211BE
+ if (data->mld_assoc_link_id >= 0 &&
+ osta->mld_assoc_link_id == data->mld_assoc_link_id)
+ continue;
+#endif /* CONFIG_IEEE80211BE */
+
wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
ohapd->conf->iface, MAC2STR(osta->addr));
ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
@@ -84,15 +91,20 @@
* hostapd_prune_associations - Remove extraneous associations
* @hapd: Pointer to BSS data for the most recent association
* @addr: Associated STA address
+ * @mld_assoc_link_id: MLD link id used for association or -1 for non MLO
*
* This function looks through all radios and BSS's for previous
* (stale) associations of STA. If any are found they are removed.
*/
-void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+ int mld_assoc_link_id)
{
struct prune_data data;
+
data.hapd = hapd;
data.addr = addr;
+ data.mld_assoc_link_id = mld_assoc_link_id;
+
if (hapd->iface->interfaces &&
hapd->iface->interfaces->for_each_interface)
hapd->iface->interfaces->for_each_interface(
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 635a74a..a662201 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -29,6 +29,7 @@
#include "drivers/driver.h"
#include "ap_config.h"
#include "ieee802_11.h"
+#include "sta_info.h"
#include "wpa_auth.h"
#include "pmksa_cache_auth.h"
#include "wpa_auth_i.h"
@@ -84,12 +85,20 @@
static const u8 * wpa_auth_get_aa(const struct wpa_state_machine *sm)
{
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return sm->own_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
return sm->wpa_auth->addr;
}
static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
{
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return sm->peer_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
return sm->addr;
}
@@ -699,6 +708,9 @@
sm->wpa_auth = wpa_auth;
sm->group = wpa_auth->group;
wpa_group_get(sm->wpa_auth, sm->group);
+#ifdef CONFIG_IEEE80211BE
+ sm->mld_assoc_link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
return sm;
}
@@ -1077,9 +1089,15 @@
const u8 *key_data;
size_t keyhdrlen, mic_len;
u8 *mic;
+ bool is_mld = false;
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);
@@ -1149,6 +1167,11 @@
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.. */
if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
@@ -1157,7 +1180,9 @@
msgtxt = "2/2 Group";
} else if (key_data_length == 0 ||
(mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
- key_data_length == AES_BLOCK_SIZE)) {
+ key_data_length == AES_BLOCK_SIZE) ||
+ (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -1779,6 +1804,15 @@
}
+static int wpa_auth_get_sta_count(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth->cb->get_sta_count)
+ return -1;
+
+ return wpa_auth->cb->get_sta_count(wpa_auth->cb_ctx);
+}
+
+
static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,
@@ -1811,11 +1845,16 @@
skip_tx:
#endif /* CONFIG_TESTING_OPTIONS */
- if (ctr == 1 && wpa_auth->conf.tx_status)
- timeout_ms = pairwise ? eapol_key_timeout_first :
- eapol_key_timeout_first_group;
- else
+ if (ctr == 1 && wpa_auth->conf.tx_status) {
+ if (pairwise)
+ timeout_ms = eapol_key_timeout_first;
+ else if (wpa_auth_get_sta_count(wpa_auth) > 100)
+ timeout_ms = eapol_key_timeout_first_group * 2;
+ else
+ timeout_ms = eapol_key_timeout_first_group;
+ } else {
timeout_ms = eapol_key_timeout_subseq;
+ }
if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
(!pairwise || (key_info & WPA_KEY_INFO_MIC)))
timeout_ms = eapol_key_timeout_no_retrans;
@@ -2293,8 +2332,9 @@
SM_STATE(WPA_PTK, PTKSTART)
{
- u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
- size_t pmkid_len = 0;
+ u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+ u8 *pmkid = NULL;
+ size_t kde_len = 0;
u16 key_info;
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
@@ -2332,7 +2372,7 @@
wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
- pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
@@ -2400,12 +2440,24 @@
}
}
if (!pmkid)
- pmkid_len = 0;
+ kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Add MAC Address KDE: kde_len=%zu",
+ kde_len);
+ wpa_add_kde(buf + kde_len, RSN_KEY_DATA_MAC_ADDR,
+ sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
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, pmkid, pmkid_len, 0, 0);
+ sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
}
@@ -3114,6 +3166,71 @@
#endif /* CONFIG_OCV */
+static int wpa_auth_validate_ml_kdes_m2(struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+{
+#ifdef CONFIG_IEEE80211BE
+ int i;
+ unsigned int n_links = 0;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /* MLD MAC address must be the same */
+ if (!kde->mac_addr ||
+ os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
+ return -1;
+ }
+
+ /* Find matching link ID and the MAC address for each link */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(kde->valid_mlo_links & BIT(i)))
+ continue;
+
+ /*
+ * Each entry should contain the link information and the MAC
+ * address.
+ */
+ if (kde->mlo_link_len[i] != 1 + ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Invalid MLO Link (ID %u) KDE len=%zu",
+ i, kde->mlo_link_len[i]);
+ return -1;
+ }
+
+ if (!sm->mld_links[i].valid || i == sm->mld_assoc_link_id) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Invalid link ID=%u", i);
+ return -1;
+ }
+
+ if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
+ ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: invalid MAC address=" MACSTR
+ " expected " MACSTR " (link ID %u)",
+ MAC2STR(kde->mlo_link[i] + 1),
+ MAC2STR(sm->mld_links[i].peer_addr), i);
+ return -1;
+ }
+
+ n_links++;
+ }
+
+ /* Must have the same number of MLO links (excluding the local one) */
+ if (n_links != sm->n_mld_affiliated_links) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Expecting %u MLD links in msg 2, but got %u",
+ sm->n_mld_affiliated_links, n_links);
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
@@ -3384,6 +3501,12 @@
}
#endif /* CONFIG_DPP2 */
+ if (wpa_auth_validate_ml_kdes_m2(sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
@@ -3478,6 +3601,11 @@
if (!sm->mgmt_frame_prot)
return pos;
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return pos; /* Use per-link MLO KDEs instead */
+#endif /* CONFIG_IEEE80211BE */
+
igtk.keyid[0] = gsm->GN_igtk;
igtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
@@ -3599,6 +3727,346 @@
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_rsn_info *info)
+{
+ info->rsn_ies = a->wpa_ie;
+ info->rsn_ies_len = a->wpa_ie_len;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLD: link_id=%u, rsn_ies_len=%zu",
+ info->link_id, info->rsn_ies_len);
+}
+
+
+static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_ml_rsn_info *info)
+{
+ if (!wpa_auth->cb->get_ml_rsn_info)
+ return;
+
+ wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, info);
+}
+
+
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot)
+{
+ struct wpa_group *gsm = a->group;
+ u8 rsc[WPA_KEY_RSC_LEN];
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u",
+ info->link_id, mgmt_frame_prot, beacon_prot);
+
+ info->gtkidx = gsm->GN & 0x03;
+ info->gtk = gsm->GTK[gsm->GN - 1];
+ info->gtk_len = gsm->GTK_len;
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0)
+ os_memset(info->pn, 0, sizeof(info->pn));
+ else
+ os_memcpy(info->pn, rsc, sizeof(info->pn));
+
+ if (!mgmt_frame_prot)
+ return;
+
+ info->igtkidx = gsm->GN_igtk;
+ info->igtk = gsm->IGTK[gsm->GN_igtk - 4];
+ info->igtk_len = wpa_cipher_key_len(a->conf.group_mgmt_cipher);
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0)
+ os_memset(info->ipn, 0, sizeof(info->ipn));
+ else
+ os_memcpy(info->ipn, rsc, sizeof(info->ipn));
+
+ if (!beacon_prot)
+ return;
+
+ info->bigtkidx = gsm->GN_bigtk;
+ info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0)
+ os_memset(info->bipn, 0, sizeof(info->bipn));
+ else
+ os_memcpy(info->bipn, rsc, sizeof(info->bipn));
+}
+
+
+static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_ml_key_info *info)
+{
+ if (!wpa_auth->cb->get_ml_key_info)
+ return;
+
+ wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+}
+
+
+static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+{
+ struct wpa_group *gsm = sm->group;
+ size_t gtk_len = gsm->GTK_len;
+ size_t igtk_len;
+ size_t kde_len;
+ unsigned int n_links;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ n_links = sm->n_mld_affiliated_links + 1;
+
+ /* MLO GTK KDE for each link */
+ kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+
+ if (!sm->mgmt_frame_prot)
+ return kde_len;
+
+ /* MLO IGTK KDE for each link */
+ igtk_len = wpa_cipher_key_len(sm->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)
+ return kde_len;
+
+ /* MLO BIGTK KDE for each link */
+ kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+
+ return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_auth_ml_key_info ml_key_info;
+ unsigned int i, link_id;
+
+ /* First fetch the key information from all the authenticators */
+ os_memset(&ml_key_info, 0, sizeof(ml_key_info));
+ ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
+
+ /*
+ * Assume that management frame protection and beacon protection are the
+ * same on all links.
+ */
+ ml_key_info.mgmt_frame_prot = sm->mgmt_frame_prot;
+ ml_key_info.beacon_prot = sm->wpa_auth->conf.beacon_prot;
+
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ ml_key_info.links[i++].link_id = link_id;
+ }
+
+ wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info);
+
+ /* 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)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO GTK",
+ ml_key_info.links[i].gtk,
+ ml_key_info.links[i].gtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1 + 6 +
+ ml_key_info.links[i].gtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_GTK);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = (ml_key_info.links[i].gtkidx & 0x3) | (link_id << 4);
+
+ os_memcpy(pos, ml_key_info.links[i].pn, 6);
+ pos += 6;
+
+ os_memcpy(pos, ml_key_info.links[i].gtk,
+ ml_key_info.links[i].gtk_len);
+ pos += ml_key_info.links[i].gtk_len;
+
+ i++;
+ }
+
+ if (!sm->mgmt_frame_prot)
+ return pos;
+
+ /* 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)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO IGTK",
+ ml_key_info.links[i].igtk,
+ ml_key_info.links[i].igtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+ sizeof(ml_key_info.links[i].ipn) +
+ ml_key_info.links[i].igtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_IGTK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Key ID */
+ *pos++ = ml_key_info.links[i].igtkidx;
+ *pos++ = 0;
+
+ /* Add the IPN */
+ os_memcpy(pos, ml_key_info.links[i].ipn,
+ sizeof(ml_key_info.links[i].ipn));
+ pos += sizeof(ml_key_info.links[i].ipn);
+
+ *pos++ = ml_key_info.links[i].link_id << 4;
+
+ os_memcpy(pos, ml_key_info.links[i].igtk,
+ ml_key_info.links[i].igtk_len);
+ pos += ml_key_info.links[i].igtk_len;
+
+ i++;
+ }
+
+ if (!sm->wpa_auth->conf.beacon_prot)
+ return pos;
+
+ /* 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)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO BIGTK",
+ ml_key_info.links[i].bigtk,
+ ml_key_info.links[i].igtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+ sizeof(ml_key_info.links[i].bipn) +
+ ml_key_info.links[i].igtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_BIGTK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Key ID */
+ *pos++ = ml_key_info.links[i].bigtkidx;
+ *pos++ = 0;
+
+ /* Add the BIPN */
+ os_memcpy(pos, ml_key_info.links[i].bipn,
+ sizeof(ml_key_info.links[i].bipn));
+ pos += sizeof(ml_key_info.links[i].bipn);
+
+ *pos++ = ml_key_info.links[i].link_id << 4;
+
+ os_memcpy(pos, ml_key_info.links[i].bigtk,
+ ml_key_info.links[i].igtk_len);
+ pos += ml_key_info.links[i].igtk_len;
+
+ i++;
+ }
+
+ return pos;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
+{
+ size_t kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ unsigned int link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /* For the MAC Address KDE */
+ kde_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ /* MLO Link KDE for each link */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len;
+ }
+
+ kde_len += wpa_auth_ml_group_kdes_len(sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+#ifdef CONFIG_IEEE80211BE
+ u8 link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return pos;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLD: Adding MAC Address KDE");
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR,
+ sm->own_mld_addr, ETH_ALEN, NULL, 0);
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLO Link: link=%u, len=%zu", link_id,
+ RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_LINK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Link Information */
+ *pos = link_id;
+ if (sm->mld_links[link_id].rsne_len)
+ *pos |= RSN_MLO_LINK_KDE_LI_RSNE_INFO;
+ if (sm->mld_links[link_id].rsnxe_len)
+ *pos |= RSN_MLO_LINK_KDE_LI_RSNXE_INFO;
+
+ pos++;
+ os_memcpy(pos, sm->mld_links[link_id].own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ if (sm->mld_links[link_id].rsne_len) {
+ os_memcpy(pos, sm->mld_links[link_id].rsne,
+ sm->mld_links[link_id].rsne_len);
+ pos += sm->mld_links[link_id].rsne_len;
+ }
+
+ if (sm->mld_links[link_id].rsnxe_len) {
+ os_memcpy(pos, sm->mld_links[link_id].rsnxe,
+ sm->mld_links[link_id].rsnxe_len);
+ pos += sm->mld_links[link_id].rsnxe_len;
+ }
+ }
+
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+#endif /* CONFIG_IEEE80211BE */
+
+ return pos;
+}
+
+
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
@@ -3609,6 +4077,11 @@
u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
u8 hdr[2];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#ifdef CONFIG_IEEE80211BE
+ bool is_mld = sm->mld_assoc_link_id >= 0;
+#else /* CONFIG_IEEE80211BE */
+ bool is_mld = false;
+#endif /* CONFIG_IEEE80211BE */
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = false;
@@ -3714,6 +4187,7 @@
secure = 0;
gtk = NULL;
gtk_len = 0;
+ gtkidx = 0;
_rsc = NULL;
if (sm->rx_eapol_key_secure) {
/*
@@ -3757,13 +4231,17 @@
kde_len += 2 + RSN_SELECTOR_LEN + 2;
#endif /* CONFIG_DPP2 */
+ kde_len += wpa_auth_ml_kdes_len(sm);
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
pos = kde;
- os_memcpy(pos, wpa_ie, wpa_ie_len);
- pos += wpa_ie_len;
+ if (!is_mld) {
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+ }
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
@@ -3787,7 +4265,7 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
}
- if (gtk) {
+ if (gtk && !is_mld) {
hdr[0] = gtkidx & 0x03;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
@@ -3867,6 +4345,8 @@
}
#endif /* CONFIG_DPP2 */
+ pos = wpa_auth_ml_kdes(sm, pos);
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
@@ -3881,10 +4361,68 @@
}
+static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ const struct ieee802_1x_hdr *hdr;
+ const struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ const u8 *key_data, *mic;
+ u16 key_data_length;
+ size_t mic_len;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (const struct wpa_eapol_key *) (hdr + 1);
+ mic = (const u8 *) (key + 1);
+ 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)
+ return -1;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key msg 4/4 with invalid Key Data contents");
+ return -1;
+ }
+
+ /* MLD MAC address must be the same */
+ if (!kde.mac_addr ||
+ os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: MLD address in EAPOL-Key msg 4/4: " MACSTR,
+ MAC2STR(kde.mac_addr));
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
SM_STATE(WPA_PTK, PTKINITDONE)
{
SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
sm->EAPOLKeyReceived = false;
+
+ if (wpa_auth_validate_ml_kdes_m4(sm) < 0) {
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
if (sm->Pair) {
enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
int klen = wpa_cipher_key_len(sm->pairwise);
@@ -4121,11 +4659,16 @@
{
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_group *gsm = sm->group;
- const u8 *kde;
+ const u8 *kde = NULL;
u8 *kde_buf = NULL, *pos, hdr[2];
size_t kde_len = 0;
u8 *gtk, stub_gtk[32];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ bool is_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ is_mld = sm->mld_assoc_link_id >= 0;
+#endif /* CONFIG_IEEE80211BE */
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
@@ -4160,7 +4703,8 @@
return;
gtk = stub_gtk;
}
- if (sm->wpa == WPA_VERSION_WPA2) {
+
+ if (sm->wpa == WPA_VERSION_WPA2 && !is_mld) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
@@ -4179,6 +4723,18 @@
return;
}
kde_len = pos - kde;
+#ifdef CONFIG_IEEE80211BE
+ } else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
+ kde_len = wpa_auth_ml_group_kdes_len(sm);
+ if (kde_len) {
+ kde_buf = os_malloc(kde_len);
+ if (!kde_buf)
+ return;
+
+ kde = pos = kde_buf;
+ wpa_auth_ml_group_kdes(sm, pos);
+ }
+#endif /* CONFIG_IEEE80211BE */
} else {
kde = gtk;
kde_len = gsm->GTK_len;
@@ -6050,3 +6606,81 @@
eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
}
+
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ u8 mld_assoc_link_id, struct mld_info *info)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct wpa_auth_ml_rsn_info ml_rsn_info;
+ unsigned int link_id, i;
+
+ if (!info)
+ return;
+
+ os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "MLD: Initialization");
+
+ os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
+ os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
+
+ sm->mld_assoc_link_id = mld_assoc_link_id;
+
+ os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
+
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->links[link_id];
+ struct mld_link *sm_link = &sm->mld_links[link_id];
+
+ sm_link->valid = link->valid;
+ if (!link->valid)
+ continue;
+
+ os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
+ os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+ link_id,
+ MAC2STR(sm_link->own_addr),
+ MAC2STR(sm_link->peer_addr));
+
+ if (link_id != mld_assoc_link_id)
+ sm->n_mld_affiliated_links++;
+
+ ml_rsn_info.links[i++].link_id = link_id;
+ }
+
+ ml_rsn_info.n_mld_links = i;
+
+ wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
+
+ for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
+ struct mld_link *sm_link;
+ const u8 *rsn_ies;
+ u8 rsn_ies_len;
+
+ sm_link = &sm->mld_links[ml_rsn_info.links[i].link_id];
+ rsn_ies = ml_rsn_info.links[i].rsn_ies;
+ rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
+
+ /* This should not really happen */
+ if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN ||
+ rsn_ies[1] + 2 > rsn_ies_len) {
+ wpa_printf(MSG_INFO, "WPA_AUTH: MLD: Invalid RSNE");
+ continue;
+ }
+
+ sm_link->rsne = rsn_ies;
+ sm_link->rsne_len = rsn_ies[1] + 2;
+
+ if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
+ rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
+ sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
+ sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 3b32fe3..57fda8a 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -15,6 +15,7 @@
#include "common/ieee802_11_defs.h"
struct vlan_description;
+struct mld_info;
#define MAX_OWN_IE_OVERRIDE 256
@@ -290,6 +291,40 @@
WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
} wpa_eapol_variable;
+struct wpa_auth_ml_rsn_info {
+ unsigned int n_mld_links;
+
+ struct wpa_auth_ml_link_rsn_info {
+ unsigned int link_id;
+ const u8 *rsn_ies;
+ size_t rsn_ies_len;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_ml_key_info {
+ unsigned int n_mld_links;
+ bool mgmt_frame_prot;
+ bool beacon_prot;
+
+ struct wpa_auth_ml_link_key_info {
+ u8 link_id;
+
+ u8 gtkidx;
+ u8 gtk_len;
+ u8 pn[6];
+ const u8 *gtk;
+
+ u8 igtkidx;
+ u8 igtk_len;
+ const u8 *igtk;
+ u8 ipn[6];
+
+ u8 bigtkidx;
+ const u8 *bigtk;
+ u8 bipn[6];
+ } links[MAX_NUM_MLD_LINKS];
+};
+
struct wpa_auth_callbacks {
void (*logger)(void *ctx, const u8 *addr, logger_level level,
const char *txt);
@@ -309,6 +344,7 @@
int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
size_t data_len, int encrypt);
+ int (*get_sta_count)(void *ctx);
int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
void *ctx), void *cb_ctx);
int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
@@ -357,6 +393,11 @@
int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
size_t ltf_keyseed_len);
#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+ int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
+ int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
+#endif /* CONFIG_IEEE80211BE */
+ int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
};
struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -599,4 +640,12 @@
void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ u8 mld_assoc_link_id, struct mld_info *info);
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_rsn_info *info);
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot);
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 2402ad9..4b16f62 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2398,7 +2398,7 @@
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
- bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
+ bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random BIGTK to each OSEN STA to prevent use
@@ -2805,7 +2805,7 @@
ric_start = pos;
if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
- sm->wpa_key_mgmt) == 0 && parse.ric) {
+ sm->wpa_key_mgmt, false) == 0 && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
@@ -2821,8 +2821,10 @@
} else {
res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe,
sizeof(rsnxe_buf));
- if (res < 0)
- return NULL;
+ if (res < 0) {
+ pos = NULL;
+ goto fail;
+ }
rsnxe_len = res;
}
#ifdef CONFIG_TESTING_OPTIONS
@@ -2851,17 +2853,23 @@
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
rsnxe_len ? rsnxe : NULL, rsnxe_len,
+ NULL,
fte_mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
- return NULL;
+ pos = NULL;
+ goto fail;
}
os_free(sm->assoc_resp_ftie);
sm->assoc_resp_ftie = os_malloc(ftie_len);
- if (!sm->assoc_resp_ftie)
- return NULL;
+ if (!sm->assoc_resp_ftie) {
+ pos = NULL;
+ goto fail;
+ }
os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+fail:
+ wpa_ft_parse_ies_free(&parse);
return pos;
}
@@ -3173,6 +3181,7 @@
const u8 *identity, *radius_cui;
size_t identity_len = 0, radius_cui_len = 0;
size_t pmk_r1_len, kdk_len, len;
+ int retval = WLAN_STATUS_UNSPECIFIED_FAILURE;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -3183,7 +3192,7 @@
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, 0, false)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -3194,17 +3203,20 @@
sm->wpa_auth->conf.mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- return WLAN_STATUS_INVALID_MDIE;
+ retval = WLAN_STATUS_INVALID_MDIE;
+ goto out;
}
if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
@@ -3214,11 +3226,12 @@
if (parse.rsn_pmkid == NULL) {
wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
- return WLAN_STATUS_INVALID_PMKID;
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
@@ -3228,12 +3241,14 @@
if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
sm->wpa_auth->conf.r1_key_holder,
sm->addr, pmk_r1_name, PMK_LEN) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
&vlan, &identity, &identity_len,
&radius_cui, &radius_cui_len,
- &session_timeout) < 0)
- return WLAN_STATUS_INVALID_PMKID;
+ &session_timeout) < 0) {
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
+ }
pmk_r1_len = PMK_LEN;
wpa_printf(MSG_DEBUG,
"FT: Generated PMK-R1 for FT-PSK locally");
@@ -3284,10 +3299,12 @@
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG,
"FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
- return WLAN_STATUS_INVALID_PMKID;
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
- return -1; /* Status pending */
+ retval = -1; /* Status pending */
+ goto out;
pmk_r1_derived:
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
@@ -3299,7 +3316,7 @@
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"ANonce");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
/* Now that we know the correct PMK-R1 length and as such, the length
@@ -3310,7 +3327,8 @@
ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
@@ -3320,7 +3338,8 @@
ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
@@ -3330,7 +3349,8 @@
ftie = (const struct rsn_ftie *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
@@ -3352,14 +3372,14 @@
sm->addr, sm->wpa_auth->addr, pmk_r1_name,
&sm->PTK, ptk_name, parse.key_mgmt,
pairwise, kdk_len) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
#ifdef CONFIG_PASN
if (sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
#endif /* CONFIG_PASN */
@@ -3370,14 +3390,14 @@
if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
identity, identity_len) < 0 ||
wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
radius_cui, radius_cui_len) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
@@ -3410,11 +3430,14 @@
*resp_ies_len = pos - *resp_ies;
- return WLAN_STATUS_SUCCESS;
+ retval = WLAN_STATUS_SUCCESS;
+ goto out;
fail:
os_free(*resp_ies);
*resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+out:
+ wpa_ft_parse_ies_free(&parse);
+ return retval;
}
@@ -3473,6 +3496,7 @@
const u8 *kck;
size_t kck_len;
struct wpa_auth_config *conf;
+ int retval = WLAN_STATUS_UNSPECIFIED_FAILURE;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -3481,26 +3505,29 @@
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt,
+ false) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (parse.rsn == NULL) {
wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (parse.rsn_pmkid == NULL) {
wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
- return WLAN_STATUS_INVALID_PMKID;
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
!= 0) {
wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
"with the PMKR1Name derived from auth request");
- return WLAN_STATUS_INVALID_PMKID;
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
mdie = (struct rsn_mdie *) parse.mdie;
@@ -3508,7 +3535,8 @@
os_memcmp(mdie->mobility_domain, conf->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- return WLAN_STATUS_INVALID_MDIE;
+ retval = WLAN_STATUS_INVALID_MDIE;
+ goto out;
}
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
@@ -3526,7 +3554,8 @@
wpa_printf(MSG_DEBUG,
"FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
parse.fte_mic_len, mic_len);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
@@ -3535,7 +3564,8 @@
parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
@@ -3544,12 +3574,14 @@
parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -3561,12 +3593,14 @@
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (os_memcmp_const(parse.r1kh_id, conf->r1_key_holder,
@@ -3577,7 +3611,8 @@
parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
conf->r1_key_holder, FT_R1KH_ID_LEN);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.rsn_pmkid == NULL ||
@@ -3585,7 +3620,8 @@
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
- return WLAN_STATUS_INVALID_PMKID;
+ retval = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
count = 3;
@@ -3597,7 +3633,7 @@
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
parse.fte_elem_count, count);
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
@@ -3615,9 +3651,10 @@
parse.ric, parse.ric_len,
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+ NULL,
mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
@@ -3636,7 +3673,8 @@
wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
if (parse.fte_rsnxe_used &&
@@ -3645,7 +3683,8 @@
!parse.rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
- return -1; /* discard request */
+ retval = -1; /* discard request */
+ goto out;
}
#ifdef CONFIG_OCV
@@ -3658,14 +3697,14 @@
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in (Re)Assoc Request");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (get_sta_tx_parameters(sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
@@ -3681,12 +3720,16 @@
OCV_FAILURE "addr=" MACSTR
" frame=ft-reassoc-req error=%s",
MAC2STR(sm->addr), ocv_errorstr);
- return WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTIE;
+ goto out;
}
}
#endif /* CONFIG_OCV */
- return WLAN_STATUS_SUCCESS;
+ retval = WLAN_STATUS_SUCCESS;
+out:
+ wpa_ft_parse_ies_free(&parse);
+ return retval;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9f6699b..82d79f2 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -513,7 +513,14 @@
u8 *seq)
{
struct hostapd_data *hapd = ctx;
- return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq);
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && idx)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+ return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, link_id,
+ seq);
}
@@ -524,6 +531,11 @@
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
u32 flags = 0;
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_eapol_frame_io) {
@@ -541,11 +553,24 @@
#endif /* CONFIG_TESTING_OPTIONS */
sta = ap_get_sta(hapd, addr);
- if (sta)
+ if (sta) {
flags = hostapd_sta_flags_to_drv(sta->flags);
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+ link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
+ }
return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
- encrypt, flags);
+ encrypt, flags, link_id);
+}
+
+
+static int hostapd_wpa_auth_get_sta_count(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hapd->num_sta;
}
@@ -1488,6 +1513,110 @@
#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+ struct wpa_auth_ml_rsn_info *info)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i, j;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
+ info->n_mld_links);
+
+ if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ unsigned int link_id = info->links[i].link_id;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
+ link_id);
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ struct hostapd_iface *iface =
+ hapd->iface->interfaces->iface[j];
+
+ 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)
+ continue;
+
+ wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
+ &info->links[i]);
+ break;
+ }
+
+ if (j == hapd->iface->interfaces->count)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ struct wpa_auth_ml_key_info *info)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i, j;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
+ info->n_mld_links);
+
+ if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ u8 link_id = info->links[i].link_id;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link info CB: link_id=%u",
+ link_id);
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ struct hostapd_iface *iface =
+ hapd->iface->interfaces->iface[j];
+
+ 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)
+ continue;
+
+ wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
+ break;
+ }
+
+ if (j == hapd->iface->interfaces->count)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static int hostapd_wpa_auth_get_drv_flags(void *ctx,
+ u64 *drv_flags, u64 *drv_flags2)
+{
+ struct hostapd_data *hapd = ctx;
+
+ if (drv_flags)
+ *drv_flags = hapd->iface->drv_flags;
+ if (drv_flags2)
+ *drv_flags2 = hapd->iface->drv_flags2;
+
+ return 0;
+}
+
+
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
@@ -1503,6 +1632,7 @@
.set_key = hostapd_wpa_auth_set_key,
.get_seqnum = hostapd_wpa_auth_get_seqnum,
.send_eapol = hostapd_wpa_auth_send_eapol,
+ .get_sta_count = hostapd_wpa_auth_get_sta_count,
.for_each_sta = hostapd_wpa_auth_for_each_sta,
.for_each_auth = hostapd_wpa_auth_for_each_auth,
.send_ether = hostapd_wpa_auth_send_ether,
@@ -1537,6 +1667,11 @@
#ifdef CONFIG_PASN
.set_ltf_keyseed = hostapd_set_ltf_keyseed,
#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+ .get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
+ .get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+#endif /* CONFIG_IEEE80211BE */
+ .get_drv_flags = hostapd_wpa_auth_get_drv_flags,
};
const u8 *wpa_ie;
size_t wpa_ie_len;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index d401550..74ae5ad 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -172,6 +172,24 @@
void *eapol_status_cb_ctx1;
void *eapol_status_cb_ctx2;
#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+ u8 own_mld_addr[ETH_ALEN];
+ u8 peer_mld_addr[ETH_ALEN];
+ s8 mld_assoc_link_id;
+ u8 n_mld_affiliated_links;
+
+ struct mld_link {
+ bool valid;
+ u8 peer_addr[ETH_ALEN];
+ u8 own_addr[ETH_ALEN];
+
+ const u8 *rsne;
+ size_t rsne_len;
+ const u8 *rsnxe;
+ size_t rsnxe_len;
+ } mld_links[MAX_NUM_MLD_LINKS];
+#endif /* CONFIG_IEEE80211BE */
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 43ccec9..a5f2861 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -10,6 +10,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -212,6 +213,13 @@
num_suites++;
}
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SHA384
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SHA384 */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
pos += RSN_SELECTOR_LEN;
@@ -705,6 +713,10 @@
else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
selector = RSN_AUTH_KEY_MGMT_OSEN;
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SHA384
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -787,6 +799,10 @@
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
@@ -998,11 +1014,24 @@
}
#ifdef CONFIG_SAE
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
- !sm->pmksa) {
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
- "No PMKSA cache entry found for SAE");
- return WPA_INVALID_PMKID;
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE_EXT_KEY) {
+ u64 drv_flags = 0;
+ u64 drv_flags2 = 0;
+ bool ap_sae_offload = false;
+
+ if (wpa_auth->cb->get_drv_flags &&
+ wpa_auth->cb->get_drv_flags(wpa_auth->cb_ctx, &drv_flags,
+ &drv_flags2) == 0)
+ ap_sae_offload =
+ !!(drv_flags2 &
+ WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP);
+
+ if (!ap_sae_offload && data.num_pmkid && !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for SAE");
+ return WPA_INVALID_PMKID;
+ }
}
#endif /* CONFIG_SAE */
diff --git a/src/common/defs.h b/src/common/defs.h
index aa3c5cf..48d5d3c 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -52,6 +52,7 @@
#define WPA_KEY_MGMT_PASN BIT(25)
#define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26)
#define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27)
+#define WPA_KEY_MGMT_IEEE8021X_SHA384 BIT(28)
#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
@@ -75,7 +76,8 @@
WPA_KEY_MGMT_FILS_SHA256 |
WPA_KEY_MGMT_FILS_SHA384 |
WPA_KEY_MGMT_FT_FILS_SHA256 |
- WPA_KEY_MGMT_FT_FILS_SHA384));
+ WPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_KEY_MGMT_IEEE8021X_SHA384));
}
static inline int wpa_key_mgmt_wpa_psk_no_sae(int akm)
@@ -153,7 +155,8 @@
return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_FILS_SHA384 |
- WPA_KEY_MGMT_FT_FILS_SHA384));
+ WPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_KEY_MGMT_IEEE8021X_SHA384));
}
static inline int wpa_key_mgmt_suite_b(int akm)
diff --git a/src/common/dragonfly.c b/src/common/dragonfly.c
index 1e84271..d039e5f 100644
--- a/src/common/dragonfly.c
+++ b/src/common/dragonfly.c
@@ -67,12 +67,15 @@
}
res = crypto_bignum_legendre(tmp, prime);
- if (res == 1 && !(*qr))
+ if (res == 1 && !(*qr)) {
*qr = tmp;
- else if (res == -1 && !(*qnr))
+ } else if (res == -1 && !(*qnr)) {
*qnr = tmp;
- else
+ } else {
crypto_bignum_deinit(tmp, 0);
+ if (res == -2)
+ break;
+ }
}
if (*qr && *qnr)
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 584c6d2..57b5a8e 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -183,8 +183,8 @@
*pri_chan = *sec_chan = 0;
- ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
- if (elems.ht_operation) {
+ if (ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0) !=
+ ParseFailed && elems.ht_operation) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->primary_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
@@ -273,7 +273,10 @@
if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
return 0;
- ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+ if (ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0) ==
+ ParseFailed)
+ return 0;
+
if (!elems.ht_capabilities) {
wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
@@ -357,9 +360,9 @@
}
}
- ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
- 0);
- if (elems.ht_capabilities) {
+ if (ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len,
+ &elems, 0) != ParseFailed &&
+ elems.ht_capabilities) {
struct ieee80211_ht_capabilities *ht_cap =
(struct ieee80211_ht_capabilities *)
elems.ht_capabilities;
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index dcadfbe..cc7af8c 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -429,6 +429,7 @@
for_each_element(elem, start, len) {
u8 id = elem->id, elen = elem->datalen;
const u8 *pos = elem->data;
+ size_t *total_len = NULL;
if (id == WLAN_EID_FRAGMENT && elems->num_frag_elems > 0) {
elems->num_frag_elems--;
@@ -512,6 +513,8 @@
break;
elems->ftie = pos;
elems->ftie_len = elen;
+ elems->fte_defrag_len = elen;
+ total_len = &elems->fte_defrag_len;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
if (elen != 5)
@@ -657,6 +660,12 @@
id, elen);
break;
}
+
+ if (elen == 255 && total_len)
+ *total_len += ieee802_11_fragments_length(
+ elems, pos + elen,
+ (start + len) - (pos + elen));
+
}
if (!for_each_element_completed(elem, start, len)) {
@@ -2498,6 +2507,35 @@
/**
+ * get_ie_nth - Fetch a specified information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @eid: Information element identifier (WLAN_EID_*)
+ * @nth: Return the nth element of the requested type (2 returns the second)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the nth matching information element in the IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth)
+{
+ const struct element *elem;
+ int sofar = 0;
+
+ if (!ies)
+ return NULL;
+
+ for_each_element_id(elem, eid, ies, len) {
+ sofar++;
+ if (sofar == nth)
+ return &elem->id;
+ }
+
+ return NULL;
+}
+
+
+/**
* get_ie_ext - Fetch a specified extended information element from IEs buffer
* @ies: Information elements buffer
* @len: Information elements buffer length
@@ -2964,7 +3002,7 @@
for (i = 0; i < flen; i++)
capabs |= rsnxe[i] << (8 * i);
- return capabs & BIT(capab);
+ return !!(capabs & BIT(capab));
}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index c7afd34..854857e 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -174,6 +174,8 @@
struct mb_ies_info mb_ies;
+ size_t fte_defrag_len;
+
/*
* The number of fragment elements to be skipped after a known
* fragmented element.
@@ -259,6 +261,7 @@
extern size_t global_op_class_size;
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth);
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index b9bb226..244b38e 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -501,6 +501,7 @@
#define WLAN_EID_EXT_EHT_CAPABILITIES 108
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
/* Extended Capabilities field */
@@ -677,6 +678,19 @@
#define WLAN_PA_FILS_DISCOVERY 34
#define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47
+/* HT Action field values (IEEE P802.11-REVme/D4.0, 9.6.11.1, Table 9-491) */
+#define WLAN_HT_ACTION_NOTIFY_CHANWIDTH 0
+#define WLAN_HT_ACTION_SMPS 1
+#define WLAN_HT_ACTION_CSI 4
+#define WLAN_HT_ACTION_NONCOMPRESSED_BF 5
+#define WLAN_HT_ACTION_COMPRESSED_BF 6
+#define WLAN_HT_ACTION_ASEL_IDX_FEEDBACK 7
+
+/* VHT Action field values (IEEE P802.11-REVme/D4.0, 9.6.22.1, Table 9-579) */
+#define WLAN_VHT_ACTION_COMPRESSED_BF 0
+#define WLAN_VHT_ACTION_GROUP_ID_MGMT 1
+#define WLAN_VHT_ACTION_OPMODE_NOTIF 2
+
/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
* Table 9-332) */
#define WLAN_PROT_DSE_ENABLEMENT 1
@@ -1190,9 +1204,6 @@
*/
} STRUCT_PACKED;
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
#define ERP_INFO_NON_ERP_PRESENT BIT(0)
#define ERP_INFO_USE_PROTECTION BIT(1)
@@ -1362,6 +1373,10 @@
#define VHT_RX_NSS_MAX_STREAMS 8
+#define VHT_OPMODE_CHANNEL_40MHZ ((u8) BIT(0))
+#define VHT_OPMODE_CHANNEL_80MHZ ((u8) BIT(1))
+#define VHT_OPMODE_CHANNEL_160MHZ ((u8) BIT(1) | BIT(2))
+
/* VHT operation information - channel widths */
#define CHANWIDTH_USE_HT 0
#define CHANWIDTH_80MHZ 1
@@ -2479,9 +2494,14 @@
*/
#define RNR_HEADER_LEN 2
#define RNR_TBTT_HEADER_LEN 4
+#define RNR_TBTT_INFO_HDR_TYPE_MSK 0x03
+#define RNR_TBTT_INFO_HDR_FILTERED_AP 0x04
+#define RNR_TBTT_INFO_HDR_CNT_MSK 0xf0
#define RNR_TBTT_INFO_COUNT(x) (((x) & 0xf) << 4)
#define RNR_TBTT_INFO_COUNT_MAX 16
+#define RNR_TBTT_INFO_COUNT_VAL(x) (((x) & 0xf0) >> 4)
#define RNR_TBTT_INFO_LEN 13
+#define RNR_TBTT_INFO_MLD_LEN 16
#define RNR_NEIGHBOR_AP_OFFSET_UNKNOWN 255
/* Figure 9-632a - BSS Parameters subfield format */
#define RNR_BSS_PARAM_OCT_RECOMMENDED BIT(0)
@@ -2641,6 +2661,7 @@
* STA Control field definitions of Per-STA Profile subelement in Basic
* Multi-Link element as described in Figure 9-1002n: STA Control field format.
*/
+#define BASIC_MLE_STA_CTRL_LEN 2
#define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F
#define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE 0x0010
#define BASIC_MLE_STA_CTRL_PRES_STA_MAC 0x0020
@@ -2694,6 +2715,16 @@
#define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80
#define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000
+#define EHT_PER_STA_CTRL_LINK_ID_MSK 0x000f
+#define EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK 0x0010
+#define EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK 0x0020
+#define EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK 0x0040
+#define EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK 0x0080
+#define EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK 0x0100
+#define EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK 0x0200
+#define EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK 0x0400
+#define EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK 0x0800
+
/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */
struct ieee80211_eht_per_sta_profile {
le16 sta_control;
@@ -2718,9 +2749,17 @@
u8 variable[];
} STRUCT_PACKED;
-/* IEEE P802.11be/D2.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+/* IEEE P802.11be/D4.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
-#define EHT_ML_PRES_BM_RECONFIGURE_MLD_ADDRESS 0x0001
+#define RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR 0x0001
+
+#define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f
+#define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010
+#define EHT_PER_STA_RECONF_CTRL_MAC_ADDR 0x0020
+#define EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER 0x0040
+#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780
+#define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800
+#define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000
/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
@@ -2791,6 +2830,39 @@
SCS_REQ_CHANGE = 2,
};
+/*
+ * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Table 9-404s (Direction subfield encoding)
+ */
+enum scs_direction {
+ SCS_DIRECTION_UP = 0,
+ SCS_DIRECTION_DOWN = 1,
+ SCS_DIRECTION_DIRECT = 2,
+};
+
+/*
+ * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Figure 9-1001av (Control Info field format)
+ */
+#define EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET 0
+#define EHT_QOS_CONTROL_INFO_TID_OFFSET 2
+#define EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET 6
+#define EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET 9
+#define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25
+
+/*
+ * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Presence Bitmap Of Additional Parameters
+ */
+#define SCS_QOS_BIT_MAX_MSDU_SIZE ((u16) BIT(0))
+#define SCS_QOS_BIT_SERVICE_START_TIME ((u16) BIT(1))
+#define SCS_QOS_BIT_SERVICE_START_TIME_LINKID ((u16) BIT(2))
+#define SCS_QOS_BIT_MEAN_DATA_RATE ((u16) BIT(3))
+#define SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE ((u16) BIT(4))
+#define SCS_QOS_BIT_MSDU_LIFETIME ((u16) BIT(5))
+#define SCS_QOS_BIT_MSDU_DELIVERY_INFO ((u16) BIT(6))
+#define SCS_QOS_BIT_MEDIUM_TIME ((u16) BIT(7))
+
/* Optional subelement IDs for MSCS Descriptor element */
enum mscs_description_subelem {
MCSC_SUBELEM_STATUS = 1,
@@ -2837,6 +2909,7 @@
#define FD_CAP_PHY_INDEX_HT 2
#define FD_CAP_PHY_INDEX_VHT 3
#define FD_CAP_PHY_INDEX_HE 4 /* P802.11ax */
+#define FD_CAP_PHY_INDEX_EHT 5 /* P802.11be */
#define FD_CAP_PHY_INDEX_SHIFT 10
/*
@@ -2880,6 +2953,7 @@
/* Wi-Fi Alliance Capabilities element - Capabilities field */
#define WFA_CAPA_QM_DSCP_POLICY BIT(0)
#define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1)
+#define WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC BIT(2)
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
#define WPA_KEY_MGMT_CROSS_AKM_ROAM (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PSK)
@@ -2897,4 +2971,8 @@
u8 data[0];
} STRUCT_PACKED;
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2fcdfbe..7620f03 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -64,6 +64,42 @@
* encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN
* is used when receiving vendor events in userspace from the driver.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE: This command is used to enable TDLS
+ * capability or to form a session with the specified peer.
+ * If %NL80211_ATTR_VENDOR_DATA is sent as an empty nested attribute this
+ * indicates to enable TDLS capability on the interface.
+ * If %QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR is nested in
+ * %NL80211_ATTR_VENDOR_DATA this indicates the userspace requests to
+ * form a TDLS session with the specified peer MAC address.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_tdls_enable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE: This command is used to disable TDLS
+ * capability or to terminate the session with the specified peer.
+ * If %NL80211_ATTR_VENDOR_DATA is sent as an empty nested attribute this
+ * indicates to disable TDLS capability on the interface.
+ * If %QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR is nested in
+ * %NL80211_ATTR_VENDOR_DATA this indicates the userspace requests to
+ * terminate TDLS session with the specified peer MAC address.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_tdls_disable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS: This command is to get the TDLS
+ * status at the interface level or with the specific peer.
+ * If %NL80211_ATTR_VENDOR_DATA is sent as an empty nested attribute this
+ * indicates the TDLS status query is at interface level.
+ * If %QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR is nested in
+ * %NL80211_ATTR_VENDOR_DATA this indicates the userspace requests to
+ * get TDLS session status with the specified peer MAC address.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_tdls_get_status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE: This event is to indicate the result
+ * of the TDLS session request with the peer sent by userspace in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_tdls_state.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
* used to configure PMK to the driver even when not connected. This can
* be used to request offloading of key management operations. Only used
@@ -106,6 +142,32 @@
* @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: Get information from the driver.
* Attributes defined in enum qca_wlan_vendor_attr_get_wifi_info.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: This command is used to
+ * configure various wiphy or interface level configurations. Attributes
+ * are defined in enum qca_wlan_vendor_attr_config. Userspace can send one
+ * or more configuration attributes with a single command. The driver
+ * accepts the command only if all the configurations are known, otherwise
+ * it rejects the command. The driver returns success only if processing of
+ * all the configurations succeeds. The driver continues to process all the
+ * configurations even if processing of some configurations failed and
+ * returns the last error occurred while processing the failed
+ * configurations.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION: This command is used to
+ * get the current values of the various wiphy or interface level
+ * configurations. Attributes are defined in enum
+ * qca_wlan_vendor_attr_config. Userspace needs to specify the
+ * configuration attributes for which it wants to get the values in the
+ * command, there is no significance for the value sent in the attribute
+ * unless explicitly specified in the corresponding configuration
+ * attribute documentation. The driver accepts the command only if all the
+ * configurations are known, otherwise it rejects the command. The driver
+ * returns success only if fetching of all configuration values succeeds
+ * and indicates the configuration values in corresponding attributes in
+ * the response. The driver continues to process all the configurations
+ * even if processing of some configurations failed and returns the last
+ * error occurred while processing the failed configurations.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
* based on enum wifi_logger_supported_features. Attributes defined in
* enum qca_wlan_vendor_attr_get_logger_features.
@@ -858,7 +920,9 @@
* In some implementations, MLO has multiple netdevs out of which one
* netdev is designated as primary to provide a unified interface to the
* bridge. In those implementations this event is sent on every MLO peer
- * connection.
+ * connection. User applications on an AP MLD will use this event to get
+ * info for all the links from non-AP MLD that were negotiated to be used
+ * for the ML association.
*
* The attributes used with this event are defined in
* enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event.
@@ -954,6 +1018,12 @@
*
* Uses the attributes defined in
* enum qca_wlan_vendor_attr_tdls_disc_rsp_ext.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY: This vendor subcommand is used to
+ * configure, retrieve, and report per-link transmit latency statistics.
+ *
+ * The attributes used with this subcommand are defined in
+ * enum qca_wlan_vendor_attr_tx_latency.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1169,6 +1239,9 @@
QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP = 229,
QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG = 230,
QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT = 231,
+ /* 232 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY = 233,
+ /* 234 - reserved for QCA */
};
/* Compatibility defines for previously used subcmd names.
@@ -1381,6 +1454,23 @@
*/
QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43,
+ /* Unsigned 8-bit used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES.
+ * This field describes the maximum number of links supported by the
+ * chip for MLO association.
+ * This is an optional attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_MLO_CAPABILITY_MAX_ASSOCIATION_COUNT = 44,
+
+ /* Unsigned 8-bit used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES.
+ * This field describes the maximum number of Simultaneous Transmit
+ * and Receive (STR) links used in Multi-Link Operation.
+ * The maximum number of STR links used can be different
+ * from the maximum number of radios supported by the chip.
+ * This is a static configuration of the chip.
+ * This is an optional attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_MLO_CAPABILITY_MAX_STR_LINK_COUNT = 45,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -2382,9 +2472,11 @@
* used to calculate statistics like average the TSF offset or average
* number of frame leaked.
* For instance, upon Beacon frame reception:
- * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000
+ * current_avg = ((beacon_TSF - TBTT) * factor +
+ * previous_avg * (0x10000 - factor)) / 0x10000
* For instance, when evaluating leaky APs:
- * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000
+ * current_avg = ((num frame received within guard time) * factor +
+ * previous_avg * (0x10000 - factor)) / 0x10000
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2,
/* Unsigned 32-bit value to configure guard time, i.e., when
@@ -2513,7 +2605,10 @@
/* 32-bit unsigned value to set reorder timeout for AC_BK */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
/* 6-byte MAC address to point out the specific peer */
- QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC = 35,
+ /* Backward compatibility with the original name */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC,
/* 32-bit unsigned value to set window size for specific peer */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
/* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */
@@ -2691,6 +2786,9 @@
* state, it should not exceed the negotiated channel width. If it is
* configured when STA is in disconnected state, the configured value
* will take effect for the next immediate connection.
+ * This configuration can be sent inside
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS to specify the maximum
+ * supported channel width per-MLO link.
*
* This uses values defined in enum nl80211_chan_width.
*/
@@ -2767,8 +2865,13 @@
* configure the asymmetric NSS configuration (such as 1X2).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
- /* 8-bit unsigned value to trigger Optimized Power Management:
- * 1-Enable, 0-Disable
+ /* 8-bit unsigned value to configure Optimized Power Management mode:
+ * Modes are defined by enum qca_wlan_vendor_opm_mode.
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes
+ * when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
@@ -2976,8 +3079,13 @@
QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_SIMULTANEOUS_LINKS = 88,
/* 8-bit unsigned value to configure the driver with EHT MLO maximum
- * number of links to be used for MLO connection.
- * The range of the value is 1 to 16.
+ * number of links to be used for MLO connection. Value 0 restores the
+ * default value of the maximum MLO links capability of the device.
+ * The range of the value is 0 to 15.
+ *
+ * 0 - Restore default device limit.
+ * 1 to 15 - Set the maximum number of links to be used for an MLO
+ * connection.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_NUM_LINKS = 89,
@@ -3037,6 +3145,163 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CTS_CHANNEL_WIDTH = 94,
+ /* 8-bit unsigned value. This attribute is used to dynamically
+ * enable/suspend trigger based UL MU transmission.
+ * This is supported in STA mode and the device sends Operating
+ * Mode Indication to inform the change as described in
+ * IEEE Std 802.11ax-2021, 26.9.
+ *
+ * This attribute can be configured when the STA is associated
+ * to an AP and the configuration is maintained until the current
+ * association terminates.
+ *
+ * By default all UL MU transmissions are enabled.
+ *
+ * Uses enum qca_ul_mu_config values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_UL_MU_CONFIG = 95,
+
+ /* 8-bit unsigned value. Optionally specified along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH when STA is in connected
+ * state. This configuration is applicable only for the current
+ * connection. This configuration not allowed in disconnected state.
+ * This configuration can be sent inside
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS to specify the maximum
+ * supported channel width update type per-MLO link.
+ *
+ * Uses enum qca_chan_width_update_type values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CHAN_WIDTH_UPDATE_TYPE = 96,
+
+ /* 8-bit unsigned value to set EPCS (Emergency Preparedness
+ * Communications Service) feature capability
+ * 1 - Enable, 0 - Disable.
+ *
+ * This configuration is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EPCS_CAPABILITY = 97,
+
+ /* 8-bit unsigned value to enable/disable EPCS priority access
+ * 1 - Enable, 0 - Disable.
+ * The EPCS priority access shall be enabled only when EPCS feature
+ * capability is also enabled (see
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_EPCS_CAPABILITY).
+ *
+ * This configuration is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EPCS_FUNCTION = 98,
+
+ /* 8-bit unsigned value. Used to specify the MLO link ID of a link
+ * that is being configured. This attribute must be included in each
+ * record nested inside %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS, and
+ * may be included without nesting to indicate the link that is the
+ * target of other configuration attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID = 99,
+
+ /* Array of nested links each identified by
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID. This uses values defined in
+ * enum qca_wlan_vendor_attr_config, explicit documentation shall be
+ * added for the attributes in enum qca_wlan_vendor_attr_config to
+ * support per-MLO link configuration through
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS.
+ *
+ * Userspace can configure a single link or multiple links with this
+ * attribute by nesting the corresponding configuration attributes and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID for each link.
+ *
+ * Userspace can fetch the configuration attribute values for a single
+ * link or multiple links with this attribute by nesting the
+ * corresponding configuration attributes and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID for each link.
+ *
+ * For STA interface, this attribute is applicable only in connected
+ * state when the current connection is MLO capable. The valid values of
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID are the link IDs of the
+ * connected AP MLD links.
+ *
+ * For AP interface, this configuration applicable only after adding
+ * MLO links to the AP interface with %NL80211_CMD_ADD_LINK and the
+ * valid values of %QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID are the link
+ * IDs specified in %NL80211_CMD_ADD_LINK while adding the MLO links to
+ * the AP interface.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS = 100,
+
+ /* 16-bit unsigned value to configure power save inactivity timeout in
+ * milliseconds.
+ *
+ * STA enters into power save mode (PM=1) after TX/RX inactivity of time
+ * duration specified by %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO.
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL when
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+ * is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO = 101,
+
+ /* 16-bit unsigned value to configure speculative wake interval in
+ * milliseconds.
+ *
+ * STA speculatively wakes up to look for buffered data by AP at
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL interval after
+ * entering into power save. If configured zero, STA wakes up at
+ * upcoming DTIM beacon.
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+ * to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL = 102,
+
+ /*
+ * 16-bit unsigned value to configure TX max A-MPDU count.
+ *
+ * For STA interface, this attribute is applicable only in connected
+ * state, peer MAC address is not required to be provided.
+ *
+ * For AP interface, this attribute is applicable only in started
+ * state and one of the associated peer STAs must be specified with
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC. If this is for an ML
+ * association, the peer MAC address provided is the link address of
+ * the non-AP MLD.
+ *
+ * This attribute runtime configures the TX maximum aggregation size.
+ * The value must be in range of 1 to BA window size for the specific
+ * peer.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_AMPDU_CNT = 103,
+
+ /*
+ * 8-bit unsigned value to configure TID-to-link mapping negotiation
+ * type.
+ * Uses enum qca_wlan_ttlm_negotiation_support values.
+ *
+ * This value applies to the complete AP/non-AP MLD interface, and the
+ * MLD advertises it within the Basic Multi-Link element in the
+ * association frames. If a new value is configured during an active
+ * connection, it will take effect in the subsequent associations and
+ * is not reset during disconnection.
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TTLM_NEGOTIATION_SUPPORT = 104,
+
+ /* 8-bit unsigned value.
+ *
+ * This attribute configures a traffic shaping mode
+ * applied during coex scenarios.
+ * By default all coex traffic shaping modes are enabled,
+ * i.e., shape WLAN traffic based on coex traffic pattern and priority.
+ * To shape traffic, STA may enter in power save mode
+ * and AP may send CTS-to-self frame.
+ *
+ * Uses enum qca_coex_traffic_shaping_mode values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_COEX_TRAFFIC_SHAPING_MODE = 105,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -3052,6 +3317,16 @@
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
/**
+ * enum qca_ul_mu_config - UL MU configuration
+ * @QCA_UL_MU_SUSPEND - All trigger based UL MU transmission is suspended
+ * @QCA_UL_MU_ENABLE - All trigger based UL MU transmission is enabled
+ */
+enum qca_ul_mu_config {
+ QCA_UL_MU_SUSPEND = 0,
+ QCA_UL_MU_ENABLE = 1,
+};
+
+/**
* enum qca_dbam_config - Specifies DBAM config mode
* @QCA_DBAM_DISABLE: Firmware disables DBAM
* @QCA_DBAM_ENABLE: Firmware enables DBAM opportunistically when
@@ -4152,16 +4427,16 @@
* statistics depending on the peer_mac.
*/
enum qca_wlan_ll_stats_clr_req_bitmap {
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO = BIT(0),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_CCA = BIT(1),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_CHANNELS = BIT(2),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_SCAN = BIT(3),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE = BIT(4),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_TXRATE = BIT(5),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_AC = BIT(6),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO = BIT(0),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_CCA = BIT(1),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_CHANNELS = BIT(2),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_RADIO_SCAN = BIT(3),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE = BIT(4),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_TXRATE = BIT(5),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_AC = BIT(6),
QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_CONTENTION = BIT(7),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_ALL_PEER = BIT(8),
- QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_PER_PEER = BIT(9),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_ALL_PEER = BIT(8),
+ QCA_WLAN_LL_STATS_CLR_REQ_BITMAP_IFACE_PER_PEER = BIT(9),
};
enum qca_wlan_vendor_attr_ll_stats_clr {
@@ -4202,8 +4477,8 @@
enum qca_wlan_ll_stats_get_req_bitmap {
QCA_WLAN_LL_STATS_GET_REQ_BITMAP_RADIO = BIT(0),
QCA_WLAN_LL_STATS_GET_REQ_BITMAP_IFACE = BIT(1),
- QCA_WLAN_LL_STATS_GET_REQ_BITMAP_ALL_PEER = BIT(2),
- QCA_WLAN_LL_STATS_GET_REQ_BITMAP_PER_PEER = BIT(3),
+ QCA_WLAN_LL_STATS_GET_REQ_BITMAP_ALL_PEER = BIT(2),
+ QCA_WLAN_LL_STATS_GET_REQ_BITMAP_PER_PEER = BIT(3),
};
enum qca_wlan_vendor_attr_ll_stats_get {
@@ -5072,7 +5347,10 @@
* due to poor RSSI of the connected AP.
* @QCA_ROAM_TRIGGER_REASON_BETTER_RSSI: Set if the roam has to be triggered
* upon finding a BSSID with a better RSSI than the connected BSSID.
- * Here the RSSI of the current BSSID need not be poor.
+ * Also, set if the roam has to be triggered due to the high RSSI of the
+ * current connected AP (better than
+ * QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET). Here the RSSI of
+ * the current BSSID need not be poor.
* @QCA_ROAM_TRIGGER_REASON_PERIODIC: Set if the roam has to be triggered
* by triggering a periodic scan to find a better AP to roam.
* @QCA_ROAM_TRIGGER_REASON_DENSE: Set if the roam has to be triggered
@@ -5415,7 +5693,11 @@
* @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
* signifying the RSSI threshold of the current connected AP, indicating
* the driver to trigger roam only when the current connected AP's RSSI
- * is less than this threshold.
+ * is less than this threshold. The RSSI threshold through this attribute
+ * is only used by the STA when the connected AP asks it to roam through
+ * a BTM request. Based on this threshold, the STA can either honor or
+ * reject the AP's request to roam, and notify the status to the AP in a
+ * BTM response.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm,
* signifying the RSSI threshold of the candidate AP, indicating
@@ -5550,12 +5832,50 @@
* discovered.
* The default behavior if this flag is not specified is to include all
* the supported 6 GHz PSC frequencies in the roam full scan.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD: Signed 32-bit value
+ * in dBm.
+ * This attribute configures the low RSSI threshold of the connected AP,
+ * based on which the STA can start looking for the neighbor APs and
+ * trigger the roam eventually. STA keeps monitoring for the connected
+ * AP's RSSI and will start scanning for neighboring APs once the RSSI
+ * falls below this threshold. This attribute differs from
+ * QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD where the configured
+ * threshold is used only when the connected AP asks the STA to roam
+ * through a BTM request.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF: Unsigned 8-bit value.
+ * This attribute signifies the RSSI difference threshold between the
+ * connected AP and the new candidate AP. This parameter influences the
+ * STA to roam to the new candidate only when its RSSI is better than
+ * the current connected one by this threshold.
+ * This parameter configures the roam behavior among the 2.4/5/6 GHz bands.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_6GHZ_CANDIDATE_ROAM_RSSI_DIFF: Unsigned 8-bit value.
+ * This attribute signifies the RSSI difference threshold between the
+ * connected AP in the 2.4/5 GHz bands and the new candidate AP in the
+ * 6 GHz band. This parameter influences the STA to roam to the new 6 GHz
+ * candidate only when its RSSI is better than the current connected one
+ * by this threshold. This threshold overrides
+ * QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF for the roam from 2.4/5
+ * GHz to 6 GHz alone with the intention to have a different value to roam
+ * to the preferred 6 GHz band.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET: Unsigned 8-bit value.
+ * This attribute signifies the RSSI offset that is added to low RSSI
+ * threshold (QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD) to imply
+ * high RSSI threshold. STA is expected to trigger roam if the current
+ * connected AP's RSSI gets above this high RSSI threshold. STA's roam
+ * attempt on high RSSI threshold aims to find candidates from other
+ * better Wi-Fi bands. E.g., STA would initially connect to a 2.4 GHz BSSID
+ * and would migrate to 5/6 GHz when it comes closer to the AP (high RSSI
+ * for 2.4 GHz BSS).
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
QCA_ATTR_ROAM_CONTROL_STATUS = 2,
QCA_ATTR_ROAM_CONTROL_CLEAR_ALL = 3,
- QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME= 4,
+ QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME = 4,
QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD = 5,
QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
@@ -5579,6 +5899,10 @@
QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX = 25,
QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ = 26,
QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY = 27,
+ QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD = 28,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF = 29,
+ QCA_ATTR_ROAM_CONTROL_6GHZ_CANDIDATE_ROAM_RSSI_DIFF = 30,
+ QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET = 31,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -8012,7 +8336,12 @@
enum qca_wlan_tdls_caps_features_supported {
WIFI_TDLS_SUPPORT = (1 << (0)),
WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)),
- WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2))
+ WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2)),
+
+ /* Indicates if the TDLS session can be formed with the peer using
+ * higher bandwidth than the bandwidth of the AP path.
+ */
+ WIFI_TDLS_WIDER_BW_SUPPORT = (1 << (3)),
};
/**
@@ -8225,6 +8554,30 @@
* This attribute is used and optional for ndp indication.
*/
QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_ID = 31,
+ /* Unsigned 8-bit value for Cipher Suite capabilities.
+ * u8 attribute.
+ * This attribute is used and optional in ndp request, ndp response,
+ * ndp indication, and ndp confirm.
+ * This attribute is used to indicate the Capabilities field of Cipher
+ * Suite Information attribute (CSIA) of NDP frames as defined in
+ * Wi-Fi Aware Specification v4.0, 9.5.21.2, Table 122.
+ * Firmware can accept or ignore any of the capability bits.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_CSIA_CAPABILITIES = 32,
+ /* Indicate that GTK protection is required for NDP.
+ * NLA_FLAG attribute.
+ * This attribute can be used in ndp request, ndp response, ndp
+ * indication, and ndp confirm.
+ * GTK protection required is indicated in the NDPE attribute of NAN
+ * action frame (NAF) during NDP negotiation as defined in
+ * Wi-Fi Aware Specification v4.0, 9.5.16.2.
+ * If the device and peer supports GTKSA and if GTK protection required
+ * bit is set in NDPE IE, devices will share GTK to each other in SKDA
+ * of Data Path Security Confirm and Data Path Security Install frames
+ * of NDP negotiation to send and receive protected group addressed data
+ * frames from each other.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_GTK_REQUIRED = 33,
/* keep last */
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
@@ -8877,6 +9230,31 @@
};
/**
+ * enum qca_wlan_ttlm_negotiation_support: TID-To-Link Mapping Negotiation
+ * support
+ * @QCA_WLAN_TTLM_DISABLE: TTLM disabled
+ * @QCA_WLAN_TTLM_SAME_LINK_SET: Mapping of all TIDs to the same link set,
+ * both DL and UL
+ * @QCA_WLAN_TTLM_SAME_DIFF_LINK_SET: Mapping of each TID to the same or
+ * different link set
+ */
+enum qca_wlan_ttlm_negotiation_support {
+ QCA_WLAN_TTLM_DISABLE = 0,
+ QCA_WLAN_TTLM_SAME_LINK_SET = 1,
+ QCA_WLAN_TTLM_SAME_DIFF_LINK_SET = 2,
+};
+
+/**
+ * enum qca_coex_traffic_shaping_mode: Coex traffic shaping mode
+ * @QCA_COEX_TRAFFIC_SHAPING_MODE_DISABLE: Coex policies disabled
+ * @QCA_COEX_TRAFFIC_SHAPING_MODE_ENABLE: All coex policies enabled
+ */
+enum qca_coex_traffic_shaping_mode {
+ QCA_COEX_TRAFFIC_SHAPING_MODE_DISABLE = 0,
+ QCA_COEX_TRAFFIC_SHAPING_MODE_ENABLE = 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_omi_tx: Represents attributes for HE and
* EHT operating mode control transmit request. These attributes are
* sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OMI_TX and
@@ -9593,6 +9971,24 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_MLO_STR_TX = 70,
+ /* Nested attribute to indicate EHT MLO links on which powersave to be
+ * enabled. It contains link ID attributes. These nested attributes are
+ * of the type u8 and are used to enable the powersave on associated
+ * MLO links corresponding to the link IDs provided in the command.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_MLO_LINK_POWER_SAVE = 71,
+
+ /* 8-bit unsigned value to configure the MLD ID of the BSS whose link
+ * info is requested in the ML Probe Request frame. In the MLO-MBSSID
+ * testcase, STA can request information of non-Tx BSS through Tx BSS
+ * by configuring non-Tx BSS MLD ID within the ML probe request that
+ * is transmitted via host initiated scan request.
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MLD_ID_ML_PROBE_REQ = 72,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -11620,6 +12016,12 @@
*/
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO = 2,
+ /*
+ * This flag attribute is set if the node being added is an
+ * MLD STA node.
+ */
+ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_IS_ML = 3,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX =
@@ -11870,7 +12272,7 @@
* the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_MCS0: u32, used in the
- * STA mode. This represents the Target power in dBm for for transmissions done
+ * STA mode. This represents the Target power in dBm for transmissions done
* to the AP in 5 GHz at MCS0 rate. This data is maintained per connect session.
* Represents the count of last connected session, when queried in the
* disconnected state.
@@ -12208,7 +12610,7 @@
};
/**
- * enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC
+ * enum qca_wlan_tspec_ack_policy - MAC acknowledgment policy in TSPEC
* As what is defined in IEEE Std 802.11-2016, Table 9-141.
*
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY.
@@ -13018,26 +13420,38 @@
* enum qca_wlan_roam_stats_frame_subtype - Roam frame subtypes. These values
* are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE.
*
- * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH: Pre-authentication frame
- * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC: Reassociation frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_AUTH_RESP: Authentication Response frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC_RESP: Reassociation Response frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1: EAPOL-Key M1 frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2: EAPOL-Key M2 frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3: EAPOL-Key M3 frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4: EAPOL-Key M4 frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1: EAPOL-Key GTK M1 frame
* @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2: EAPOL-Key GTK M2 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_AUTH_REQ: Authentication Request frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC_REQ: Reassociation Request frame
*/
enum qca_wlan_roam_stats_frame_subtype {
- QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH = 1,
- QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC = 2,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_AUTH_RESP = 1,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC_RESP = 2,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1 = 3,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2 = 4,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3 = 5,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4 = 6,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1 = 7,
QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2 = 8,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_AUTH_REQ = 9,
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC_REQ = 10,
};
+/* Compatibility defines for previously used names.
+ * These values should not be used in any new implementation.
+ */
+#define QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH \
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_AUTH_RESP
+#define QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC \
+ QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC_RESP
+
/**
* enum roam_frame_status - Specifies the valid values the vendor roam frame
* attribute QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS can take.
@@ -13074,6 +13488,13 @@
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_TIMESTAMP = 3,
/* Attribute used for padding for 64-bit alignment */
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_PAD = 4,
+ /* This attribute indicates a 6-byte MAC address representing
+ * the BSSID of the AP.
+ * For non-MLO scenario, it indicates the AP BSSID.
+ * For MLO scenario, it indicates the AP BSSID which may be the primary
+ * link BSSID or a nonprimary link BSSID.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_BSSID = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST,
@@ -13285,6 +13706,38 @@
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO = 43,
/* Attribute used for padding for 64-bit alignment */
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PAD = 44,
+ /* 6-byte MAC address used by the driver to send roam stats information
+ * of the original AP BSSID. The original AP is the connected AP before
+ * roam happens, regardless of the roam resulting in success or failure.
+ * This attribute is only present when
+ * QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS has a value of
+ * 0 (success) or 1 (failure).
+ * For non-MLO scenario, it indicates the original connected AP BSSID.
+ * For MLO scenario, it indicates the original BSSID of the link
+ * for which the reassociation occurred during the roam.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ORIGINAL_BSSID = 45,
+ /* 6-byte MAC address used by the driver to send roam stats information
+ * of the roam candidate AP BSSID when roam failed. This is only present
+ * when QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS has a value of
+ * 1 (failure). If the firmware updates more than one candidate AP BSSID
+ * to the driver, the driver only fills the last candidate AP BSSID and
+ * reports it to user space.
+ * For non-MLO scenario, it indicates the last candidate AP BSSID.
+ * For MLO scenario, it indicates the AP BSSID which may be the primary
+ * link BSSID or a nonprimary link BSSID.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CANDIDATE_BSSID = 46,
+ /* 6-byte MAC address used by the driver to send roam stats information
+ * of the roamed AP BSSID when roam succeeds. This is only present when
+ * QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS has a value of
+ * 0 (success).
+ * For non-MLO scenario, it indicates the new AP BSSID to which has
+ * been successfully roamed.
+ * For MLO scenario, it indicates the new AP BSSID of the link on
+ * which the reassociation occurred during the roam.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAMED_BSSID = 47,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST,
@@ -13706,7 +14159,7 @@
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX =
- QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_AFTER_LAST -1,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_AFTER_LAST - 1,
};
/**
@@ -14833,12 +15286,25 @@
* MLD MAC address of the peer.
* @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX: u32 attribute,
* used to pass ifindex of the primary netdev.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_IFINDEX: u32 attribute,
+ * used to pass ifindex of the MLD netdev.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_NUM_LINKS: u8 attribute,
+ * used to indicate the number of links that the non-AP MLD negotiated to be
+ * used in the ML connection.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_LINK_INFO: Nested
+ * attribute, contains information regarding links of the non-AP MLD.
+ * User applications need to know all the links of a non-AP MLD that are
+ * participating in the ML association. The possible attributes inside this
+ * attribute are defined in enum qca_wlan_vendor_attr_mlo_link_info.
*/
enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event {
QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR = 1,
QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR = 2,
QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX = 3,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_IFINDEX = 4,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_NUM_LINKS = 5,
+ QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_LINK_INFO = 6,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST,
@@ -14847,6 +15313,27 @@
};
/**
+ * enum qca_wlan_vendor_attr_mlo_link_info - Defines attributes for
+ * non-AP MLD link parameters used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_LINK_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_IFINDEX: u32 attribute, used
+ * to pass the netdev ifindex of the non-AP MLD link.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_MACADDR: 6 byte MAC address of
+ * the non-AP MLD link.
+ */
+enum qca_wlan_vendor_attr_mlo_link_info {
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_IFINDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_MACADDR = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INFO_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_afc_freq_psd_info: This enum is used with
* nested attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and
* QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST to update the frequency range
@@ -15574,7 +16061,6 @@
* A bitmap of the removed setup links link IDs.
*/
enum qca_wlan_vendor_attr_link_reconfig {
-
QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR = 1,
QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS = 2,
@@ -15603,4 +16089,517 @@
QCA_WLAN_VENDOR_ATTR_TDLS_DISC_RSP_EXT_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_tdls_state - Represents the possible TDLS states.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_DISABLED: TDLS is not enabled, default status
+ * for all stations.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_ENABLED: TDLS is enabled, but not yet tried to
+ * establish the session.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_ESTABLISHED: Direct link TDLS session is
+ * established.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_ESTABLISHED_OFF_CHANNEL: Direct link TDLS
+ * session is established using MCC.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_DROPPED: Direct link TDLS session was
+ * established, but is temporarily dropped currently.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_STATE_FAILED: TDLS session is failed to establish.
+ */
+enum qca_wlan_vendor_tdls_state {
+ QCA_WLAN_VENDOR_TDLS_STATE_DISABLED = 1,
+ QCA_WLAN_VENDOR_TDLS_STATE_ENABLED = 2,
+ QCA_WLAN_VENDOR_TDLS_STATE_ESTABLISHED = 3,
+ QCA_WLAN_VENDOR_TDLS_STATE_ESTABLISHED_OFF_CHANNEL = 4,
+ QCA_WLAN_VENDOR_TDLS_STATE_DROPPED = 5,
+ QCA_WLAN_VENDOR_TDLS_STATE_FAILED = 6,
+};
+
+/**
+ * enum qca_wlan_vendor_tdls_reason - Represents the possible TDLS reasons.
+ *
+ * @QCA_WLAN_TDLS_REASON_SUCCESS: TDLS session is successfully established.
+ *
+ * @QCA_WLAN_TDLS_REASON_UNSPECIFIED: Unspecified reason.
+ *
+ * @QCA_WLAN_TDLS_REASON_NOT_SUPPORTED: TDLS is not supported.
+ *
+ * @QCA_WLAN_TDLS_REASON_UNSUPPORTED_BAND: The specified band is not supported.
+ *
+ * @QCA_WLAN_TDLS_REASON_NOT_BENEFICIAL: Packets going through AP is better
+ * than through direct link.
+ *
+ * @QCA_WLAN_TDLS_REASON_DROPPED_BY_REMOTE: Peer station doesn't want the TDLS
+ * session anymore.
+ */
+
+enum qca_wlan_vendor_tdls_reason {
+ QCA_WLAN_TDLS_REASON_SUCCESS = 0,
+ QCA_WLAN_TDLS_REASON_UNSPECIFIED = -1,
+ QCA_WLAN_TDLS_REASON_NOT_SUPPORTED = -2,
+ QCA_WLAN_TDLS_REASON_UNSUPPORTED_BAND = -3,
+ QCA_WLAN_TDLS_REASON_NOT_BENEFICIAL = -4,
+ QCA_WLAN_TDLS_REASON_DROPPED_BY_REMOTE = -5,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_enable - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR: 6-byte MAC address of the peer
+ * station to enable the TDLS session. Optional attribute. The driver sends the
+ * TDLS session result as an asynchronous response using the command
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE when this peer MAC is provided in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL: u32 attribute. Indicates the
+ * channel on which the TDLS session to be established. Required only when
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS: u32 attribute.
+ * Indicates the global operating class of the TDLS session to be established.
+ * Required only when %QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS: u32 attribute. Indicates
+ * the maximum latency of the WLAN packets to be transmitted/received in
+ * milliseconds on TDLS session. Required only when
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS: u32 attribute.
+ * Indicates the minimum bandwidth to be used to establish the TDLS session
+ * in kbps. Required only when %QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR is
+ * present.
+ */
+enum qca_wlan_vendor_attr_tdls_enable {
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL = 2,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS = 3,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS = 4,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_disable - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR: 6-byte MAC address of the peer
+ * station to disable the TDLS session. Optional attribute.
+ */
+enum qca_wlan_vendor_attr_tdls_disable {
+ QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_get_status - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR: 6-byte MAC address of the
+ * peer station. Optional attribute. Used in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS request and response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE: u32 attribute. Indicates the
+ * TDLS session state with the peer specified in
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR. Uses the values from
+ * enum qca_wlan_vendor_tdls_state. Used in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON: s32 attribute. Indicates the
+ * reason for the TDLS session state indicated in
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE. Uses the values from enum
+ * qca_wlan_vendor_tdls_reason. Used in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL: u32 attribute. Indicates the
+ * channel of the TDLS session established with
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR. Used in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS: u32 attribute.
+ * Indicates the global operating class of the TDLS session established with
+ * %QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR. Used in
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_NUM_SESSIONS: u32 attribute. Indicates
+ * the current number of active TDLS sessions. This is indicated in the response
+ * when %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS is requested with
+ * %NL80211_ATTR_VENDOR_DATA as an empty nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AVAILABLE: Flag attribute. Indicates
+ * whether the driver can initiate new TDLS session. This is indicated in the
+ * response when %QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS is requested with
+ * %NL80211_ATTR_VENDOR_DATA as an empty nested attribute.
+ */
+enum qca_wlan_vendor_attr_tdls_get_status {
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE = 2,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON = 3,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL = 4,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS = 5,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_NUM_SESSIONS = 6,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AVAILABLE = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_state - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR: 6-byte MAC address of the
+ * peer station. Required attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CURRENT_STATE: u32 attribute. Indicates
+ * the current TDLS state. Required attribute. Uses the values from
+ * enum qca_wlan_vendor_tdls_state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON: s32 attribute. Indicates the
+ * reason of the current TDLS session state. Required attribute. Uses the values
+ * from enum qca_wlan_vendor_tdls_reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL: u32 attribute. Indicates the
+ * TDLS session channel. Required attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS: u32 attribute.
+ * Indicates the TDLS session global operating class. Required attribute.
+ */
+enum qca_wlan_vendor_attr_tdls_state {
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_NEW_STATE = 2,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON = 3,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL = 4,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX =
+ QCA_WLAN_VENDOR_ATTR_TDLS_STATE_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_opm_mode - Modes supported by
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT vendor attribute.
+ *
+ * @QCA_WLAN_VENDOR_OPM_MODE_DISABLE: OPM Disabled
+ * @QCA_WLAN_VENDOR_OPM_MODE_ENABLE: OPM Enabled
+ * @QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED: User defined mode which allows user
+ * to configure power save inactivity timeout and speculative wake up
+ * interval through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes.
+ */
+
+enum qca_wlan_vendor_opm_mode {
+ QCA_WLAN_VENDOR_OPM_MODE_DISABLE = 0,
+ QCA_WLAN_VENDOR_OPM_MODE_ENABLE = 1,
+ QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED = 2,
+};
+
+/*
+ * enum qca_wlan_vendor_tx_latency_type - Represents the possible latency
+ * types.
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_TYPE_DRIVER: Per MSDU latency
+ * from: An MSDU is presented to the driver
+ * to: the MSDU is queued into TCL SRNG
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_TYPE_RING: Per MSDU latency
+ * from: the MSDU is queued into TCL SRNG
+ * to: the MSDU is released by the driver
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_TYPE_HW: Per MSDU latency
+ * from: the MSDU is presented to the hardware
+ * to: the MSDU is released by the hardware
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_TYPE_CCA: Per PPDU latency
+ * The time spent on Clear Channel Assessment, the maximum value is 50000 (us)
+ * from: A PPDU is presented to the hardware LMAC
+ * to: over-the-air transmission is started for the PPDU
+ */
+enum qca_wlan_vendor_tx_latency_type {
+ QCA_WLAN_VENDOR_TX_LATENCY_TYPE_DRIVER = 0,
+ QCA_WLAN_VENDOR_TX_LATENCY_TYPE_RING = 1,
+ QCA_WLAN_VENDOR_TX_LATENCY_TYPE_HW = 2,
+ QCA_WLAN_VENDOR_TX_LATENCY_TYPE_CCA = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tx_latency_bucket - Definition of attributes
+ * used inside nested attributes
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS and
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE: u8 attribute.
+ * Indicates the latency type.
+ * See enum qca_wlan_vendor_tx_latency_type for the supported types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY: u32 attribute.
+ * Indicates the granularity (in microseconds) of the distribution for the
+ * type (specified by %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE), the value
+ * must be positive.
+ * If %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE is
+ * %QCA_WLAN_VENDOR_TX_LATENCY_TYPE_CCA, the value must be an integer multiple
+ * of 1000, and the maximum allowed value is 15000 (us).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AVERAGE: u32 attribute.
+ * Indicates the average of the latency (in microseconds) for the type
+ * (specified by %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE) within a cycle.
+ * If there is no transmitted MSDUs/MPDUs during a cycle, this average is 0;
+ * otherwise, it represents the quotient of <accumulated latency of the
+ * transmitted MSDUs/MPDUs in a cycle> divided by <the number of the transmitted
+ * MSDUs/MPDUs in a cycle>.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION:
+ * Array of u32, 4 elements in total, represents the latency distribution for
+ * the type (specified by %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE).
+ * Each element holds the count of MSDUs/PPDUs (according to the latency type)
+ * within a range:
+ * element[0]: latency >= 0 && latency < granularity
+ * element[1]: latency >= granularity && latency < granularity * 2
+ * element[2]: latency >= granularity * 2 && latency < granularity * 3
+ * element[3]: latency >= granularity * 3
+ */
+enum qca_wlan_vendor_attr_tx_latency_bucket {
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY = 2,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AVERAGE = 3,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_MAX =
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tx_latency_link - Definition of attributes
+ * used inside nested attribute %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE: 6-byte MAC address.
+ * Indicates link MAC address of the remote peer. For example, when running
+ * in station mode, it's the BSSID of the link; while when running in AP
+ * mode, it's the link MAC address of the remote station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS:
+ * Array of nested attribute.
+ * Represents the transmit latency statistics for the link specified by
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE.
+ * Each entry represents the statistics for one of the types defined in
+ * enum qca_wlan_vendor_tx_latency_type.
+ * Each defined type has and must have one entry.
+ * See enum qca_wlan_vendor_attr_tx_latency_bucket for nested attributes.
+ */
+enum qca_wlan_vendor_attr_tx_latency_link {
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE = 1,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAX =
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_tx_latency_action - Represents the possible actions
+ * for %QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY.
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_ACTION_DISABLE:
+ * Disable transmit latency monitoring.
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_ACTION_ENABLE:
+ * Enable transmit latency monitoring.
+ *
+ * @QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET:
+ * Get transmit latency statistics of the last cycle (period is specified by
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIOD).
+ */
+enum qca_wlan_vendor_tx_latency_action {
+ QCA_WLAN_VENDOR_TX_LATENCY_ACTION_DISABLE = 0,
+ QCA_WLAN_VENDOR_TX_LATENCY_ACTION_ENABLE = 1,
+ QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tx_latency - Definition of attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY to configure, retrieve, and report
+ * per-link transmit latency statistics.
+ *
+ * There are 6 uses of %QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY:
+ * 1) used as a command to enable the feature
+ * Precondition(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION is
+ * %QCA_WLAN_VENDOR_TX_LATENCY_ACTION_ENABLE
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIOD,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS with nested attributes
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY.
+ * Notes:
+ * The driver will monitor the transmit latency for the active links
+ * and save the statistics for each cycle (period is set by
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIOD) when the feature is enabled.
+ * Set flag %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIODIC_REPORT if periodical
+ * report is required.
+ *
+ * 2) used as a command to disable the feature
+ * Precondition(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION is
+ * %QCA_WLAN_VENDOR_TX_LATENCY_ACTION_DISABLE
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION
+ *
+ * 3) used as a command to retrieve the statistics for all the active links on
+ * the requested interface
+ * Precondition(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION is
+ * QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET and
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS is NOT present.
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION
+ * Notes:
+ * The driver returns failure directly if the feature is not enabled or
+ * there is no active link.
+ * The driver returns the statistics of the last cycle in the case of
+ * success.
+ *
+ * 4) used as a command to retrieve the statistics for the specified links
+ * Precondition(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION is
+ * QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET and
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS is present.
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS, with nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE.
+ * Notes:
+ * The driver returns failure directly if the feature is not enabled or
+ * any of the links (specified by %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS)
+ * does not exist or is not in active state.
+ *
+ * 5) used as a command response for #3 or #4
+ * Precondition(s):
+ * Userspace issues command #3 or #4, and the driver gets corresponding
+ * statistics successfully.
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS, with nested attributes
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS with nested
+ * attributes %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AVERAGE and
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION.
+ *
+ * 6) used as an asynchronous event to report the statistics periodically
+ * Precondition(s):
+ * Userspace set flag %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIODIC_REPORT in
+ * #1.
+ * One or more links are in active state.
+ * Mandatory attribute(s):
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS, with nested attributes
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS with nested
+ * attributes %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY,
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_AVERAGE and
+ * %QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_INVALID: Invalid attribute
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION: u32 attribute.
+ * Action to take in this vendor command.
+ * See enum qca_wlan_vendor_tx_latency_action for supported actions.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIODIC_REPORT: Flag attribute.
+ * Enable (flag attribute present) - The driver needs to report transmit latency
+ * statistics at the end of each statistical period.
+ * Disable (flag attribute not present) - The driver doesn't need to report
+ * transmit latency statistics periodically.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIOD: u32 attribute.
+ * Indicates statistical period for transmit latency in terms of milliseconds,
+ * the minimal allowed value is 100 and the maximum allowed value is 60000.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS: Array of nested attribute.
+ * Each entry represents the latency buckets configuration for one of the types
+ * defined in enum qca_wlan_vendor_tx_latency_type.
+ * Each defined type has and must have one entry.
+ * See enum qca_wlan_vendor_attr_tx_latency_bucket for the list of
+ * supported attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS: Array of nested attribute.
+ * Information of the links, each entry represents for one link.
+ * See enum qca_wlan_vendor_attr_tx_latency_link for the list of
+ * supported attributes for each entry.
+ */
+enum qca_wlan_vendor_attr_tx_latency {
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ACTION = 1,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIODIC_REPORT = 2,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_PERIOD = 3,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS = 4,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_MAX =
+ QCA_WLAN_VENDOR_ATTR_TX_LATENCY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_chan_width_update_type - Represents the possible values for
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_CHAN_WIDTH_UPDATE_TYPE.
+ *
+ * @QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX: The maximum allowed bandwidth change is
+ * applicable for both Tx and Rx paths. The driver shall conduct OMI operation
+ * as defined in 26.9 (Operating mode indication) or OMN operation as defined in
+ * 11.40 (Notification of operating mode changes) in IEEE P802.11-REVme/D2.0
+ * with AP to indicate the change in the maximum allowed operating bandwidth.
+ *
+ * @QCA_CHAN_WIDTH_UPDATE_TYPE_TX_ONLY: Limit the change in maximum allowed
+ * bandwidth only to Tx path. In this case the driver doesn't need to conduct
+ * OMI/OMN operation since %QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH is
+ * expected to be less than the current connection maximum negotiated bandwidth.
+ * For example: Negotiated maximum bandwidth is 160 MHz and the new maximum
+ * bandwidth configured is 80 MHz, now the driver limits the maximum bandwidth
+ * to 80 MHz only in the Tx path.
+ *
+ * @QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX_EXT: This is similar to
+ * %QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX but the driver doesn't change current
+ * phymode bandwidth to avoid interoperability issues with APs which don't
+ * handle the maximum bandwidth change indication correctly.
+ * For example: Negotiated maximum bandwidth is 40 MHz and the new maximum
+ * bandwidth configured is 20 MHz, now the driver indicates the change in
+ * maximum allowed bandwidth to the AP and limits the bandwidth to 20 MHz in the
+ * Tx path but keeps the phymode bandwidth as 40 MHz. This will avoid
+ * interoperability issues with APs which still use 40 MHz for sending the
+ * frames though it received maximum allowed bandwidth indication as 20 MHz
+ * from the STA.
+ */
+enum qca_chan_width_update_type {
+ QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX = 0,
+ QCA_CHAN_WIDTH_UPDATE_TYPE_TX_ONLY = 1,
+ QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX_EXT = 2,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 367af8f..d897e0e 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -17,6 +17,7 @@
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
#include "defs.h"
#include "wpa_common.h"
@@ -25,6 +26,7 @@
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
@@ -70,6 +72,7 @@
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA256:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
return 32;
case WPA_KEY_MGMT_DPP:
return pmk_len <= 32 ? 16 : 32;
@@ -104,6 +107,7 @@
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
@@ -134,6 +138,7 @@
akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
+ akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
wpa_key_mgmt_sae(akmp) ||
wpa_key_mgmt_suite_b(akmp) ||
wpa_key_mgmt_fils(akmp);
@@ -172,6 +177,7 @@
return akmp == WPA_KEY_MGMT_OSEN ||
akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
+ akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
wpa_key_mgmt_sae(akmp) ||
@@ -330,15 +336,18 @@
os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_DPP */
-#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384)
+#ifdef CONFIG_SHA384
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
+#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+#endif /* CONFIG_IEEE80211R */
wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)");
+ "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - 802.1X SHA384)");
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, 24);
break;
-#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */
+#endif /* CONFIG_SHA384 */
default:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
@@ -453,14 +462,14 @@
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len;
if (wpa_key_mgmt_sha384(akmp)) {
-#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
+#ifdef CONFIG_SHA384
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
-#else /* CONFIG_SUITEB192 || CONFIG_FILS */
+#else /* CONFIG_SHA384 */
return -1;
-#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
+#endif /* CONFIG_SHA384 */
} else if (wpa_key_mgmt_sha256(akmp)) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
@@ -889,10 +898,11 @@
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len,
const u8 *rsnxe, size_t rsnxe_len,
+ const struct wpabuf *extra,
u8 *mic)
{
- const u8 *addr[10];
- size_t len[10];
+ const u8 *addr[11];
+ size_t len[11];
size_t i, num_elem = 0;
u8 zero_mic[32];
size_t mic_len, fte_fixed_len;
@@ -970,6 +980,12 @@
num_elem++;
}
+ if (extra) {
+ addr[num_elem] = wpabuf_head(extra);
+ len[num_elem] = wpabuf_len(extra);
+ num_elem++;
+ }
+
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
res = -1;
@@ -1013,6 +1029,7 @@
struct wpa_ft_ies *parse, const u8 *opt)
{
const u8 *end, *pos;
+ u8 link_id;
parse->ftie = ie;
parse->ftie_len = ie_len;
@@ -1078,6 +1095,51 @@
parse->bigtk = pos;
parse->bigtk_len = len;
break;
+ case FTIE_SUBELEM_MLO_GTK:
+ if (len < 2 + 1 + 1 + 8) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Too short MLO GTK in FTE");
+ return -1;
+ }
+ link_id = pos[2] & 0x0f;
+ wpa_printf(MSG_DEBUG, "FT: MLO GTK (Link ID %u)",
+ link_id);
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ break;
+ parse->valid_mlo_gtks |= BIT(link_id);
+ parse->mlo_gtk[link_id] = pos;
+ parse->mlo_gtk_len[link_id] = len;
+ break;
+ case FTIE_SUBELEM_MLO_IGTK:
+ if (len < 2 + 6 + 1 + 1) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Too short MLO IGTK in FTE");
+ return -1;
+ }
+ link_id = pos[2 + 6] & 0x0f;
+ wpa_printf(MSG_DEBUG, "FT: MLO IGTK (Link ID %u)",
+ link_id);
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ break;
+ parse->valid_mlo_igtks |= BIT(link_id);
+ parse->mlo_igtk[link_id] = pos;
+ parse->mlo_igtk_len[link_id] = len;
+ break;
+ case FTIE_SUBELEM_MLO_BIGTK:
+ if (len < 2 + 6 + 1 + 1) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Too short MLO BIGTK in FTE");
+ return -1;
+ }
+ link_id = pos[2 + 6] & 0x0f;
+ wpa_printf(MSG_DEBUG, "FT: MLO BIGTK (Link ID %u)",
+ link_id);
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ break;
+ parse->valid_mlo_bigtks |= BIT(link_id);
+ parse->mlo_bigtk[link_id] = pos;
+ parse->mlo_bigtk_len[link_id] = len;
+ break;
default:
wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
break;
@@ -1151,17 +1213,26 @@
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
- int key_mgmt)
+ int key_mgmt, bool reassoc_resp)
{
const u8 *end, *pos;
struct wpa_ie_data data;
int ret;
int prot_ie_count = 0;
+ const u8 *fte = NULL;
+ size_t fte_len = 0;
+ bool is_fte = false;
+ struct ieee802_11_elems elems;
os_memset(parse, 0, sizeof(*parse));
if (ies == NULL)
return 0;
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse elements");
+ goto fail;
+ }
+
pos = ies;
end = ies + ies_len;
while (end - pos >= 2) {
@@ -1172,6 +1243,10 @@
if (len > end - pos)
break;
+ if (id != WLAN_EID_FAST_BSS_TRANSITION &&
+ id != WLAN_EID_FRAGMENT)
+ is_fte = false;
+
switch (id) {
case WLAN_EID_RSN:
wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len);
@@ -1183,7 +1258,7 @@
if (ret < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse "
"RSN IE: %d", ret);
- return -1;
+ goto fail;
}
parse->rsn_capab = data.capabilities;
if (data.num_pmkid == 1 && data.pmkid)
@@ -1203,7 +1278,7 @@
case WLAN_EID_MOBILITY_DOMAIN:
wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
if (len < sizeof(struct rsn_mdie))
- return -1;
+ goto fail;
parse->mdie = pos;
parse->mdie_len = len;
break;
@@ -1217,12 +1292,19 @@
* using the struct rsn_ftie* definitions. */
if (len < 2)
- return -1;
+ goto fail;
prot_ie_count = pos[1]; /* Element Count field in
* MIC Control */
-
- if (wpa_ft_parse_fte(key_mgmt, pos, len, parse) < 0)
- return -1;
+ is_fte = true;
+ fte = pos;
+ fte_len = len;
+ break;
+ case WLAN_EID_FRAGMENT:
+ if (is_fte) {
+ wpa_hexdump(MSG_DEBUG, "FT: FTE fragment",
+ pos, len);
+ fte_len += 2 + len;
+ }
break;
case WLAN_EID_TIMEOUT_INTERVAL:
wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval",
@@ -1241,6 +1323,25 @@
pos += len;
}
+ if (fte) {
+ int res;
+
+ if (fte_len < 255) {
+ res = wpa_ft_parse_fte(key_mgmt, fte, fte_len, parse);
+ } else {
+ parse->fte_buf = ieee802_11_defrag_data(fte, fte_len,
+ false);
+ if (!parse->fte_buf)
+ goto fail;
+ res = wpa_ft_parse_fte(key_mgmt,
+ wpabuf_head(parse->fte_buf),
+ wpabuf_len(parse->fte_buf),
+ parse);
+ }
+ if (res < 0)
+ goto fail;
+ }
+
if (prot_ie_count == 0)
return 0; /* no MIC */
@@ -1248,24 +1349,39 @@
* Check that the protected IE count matches with IEs included in the
* frame.
*/
- if (parse->rsn)
- prot_ie_count--;
+ if (reassoc_resp && elems.basic_mle) {
+ unsigned int link_id;
+
+ /* TODO: This count should be done based on all _requested_,
+ * not _accepted_ links. */
+ for (link_id = 0; link_id < MAX_NUM_MLO_LINKS; link_id++) {
+ if (parse->mlo_gtk[link_id]) {
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->rsnxe)
+ prot_ie_count--;
+ }
+ }
+ } else {
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->rsnxe)
+ prot_ie_count--;
+ }
if (parse->mdie)
prot_ie_count--;
if (parse->ftie)
prot_ie_count--;
- if (parse->rsnxe)
- prot_ie_count--;
if (prot_ie_count < 0) {
wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
"the protected IE count");
- return -1;
+ goto fail;
}
if (prot_ie_count == 0 && parse->ric) {
wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
"included in protected IE count");
- return -1;
+ goto fail;
}
/* Determine the end of the RIC IE(s) */
@@ -1281,11 +1397,25 @@
if (prot_ie_count) {
wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
"frame", (int) prot_ie_count);
- return -1;
+ goto fail;
}
return 0;
+
+fail:
+ wpa_ft_parse_ies_free(parse);
+ return -1;
}
+
+
+void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse)
+{
+ if (!parse)
+ return;
+ wpabuf_free(parse->fte_buf);
+ parse->fte_buf = NULL;
+}
+
#endif /* CONFIG_IEEE80211R */
@@ -1649,6 +1779,10 @@
return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA384)
+ return WPA_KEY_MGMT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
return WPA_KEY_MGMT_IEEE8021X_SHA256;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
@@ -2665,6 +2799,8 @@
return "DPP";
case WPA_KEY_MGMT_PASN:
return "PASN";
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
+ return "WPA2-EAP-SHA384";
default:
return "UNKNOWN";
}
@@ -2679,6 +2815,8 @@
return RSN_AUTH_KEY_MGMT_FT_802_1X;
if (akm & WPA_KEY_MGMT_FT_PSK)
return RSN_AUTH_KEY_MGMT_FT_PSK;
+ if (akm & WPA_KEY_MGMT_IEEE8021X_SHA384)
+ return RSN_AUTH_KEY_MGMT_802_1X_SHA384;
if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
if (akm & WPA_KEY_MGMT_IEEE8021X)
@@ -3584,6 +3722,11 @@
sizeof(struct ieee80211_he_6ghz_band_cap) &&
pos[2] == WLAN_EID_EXT_HE_6GHZ_BAND_CAP) {
ie->he_6ghz_capabilities = pos + 3;
+ } else if (*pos == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN &&
+ pos[2] == WLAN_EID_EXT_EHT_CAPABILITIES) {
+ ie->eht_capabilities = pos + 3;
+ ie->eht_capab_len = pos[1] - 1;
} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
ie->qosinfo = pos[2];
} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 05b1a8a..1269bf9 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -427,6 +427,9 @@
#define FTIE_SUBELEM_IGTK 4
#define FTIE_SUBELEM_OCI 5
#define FTIE_SUBELEM_BIGTK 6
+#define FTIE_SUBELEM_MLO_GTK 8
+#define FTIE_SUBELEM_MLO_IGTK 9
+#define FTIE_SUBELEM_MLO_BIGTK 10
struct rsn_rdie {
u8 id;
@@ -482,6 +485,7 @@
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len,
const u8 *rsnxe, size_t rsnxe_len,
+ const struct wpabuf *extra,
u8 *mic);
int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
const u8 *ssid, size_t ssid_len,
@@ -553,6 +557,8 @@
const u8 *ie2, size_t ie2len);
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
+#define MAX_NUM_MLO_LINKS 15
+
struct wpa_ft_ies {
const u8 *mdie;
size_t mdie_len;
@@ -589,6 +595,17 @@
int pairwise_cipher;
const u8 *rsnxe;
size_t rsnxe_len;
+ u16 valid_mlo_gtks; /* bitmap of valid link GTK subelements */
+ const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
+ u16 valid_mlo_igtks; /* bitmap of valid link IGTK subelements */
+ const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+ u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK subelements */
+ const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+
+ struct wpabuf *fte_buf;
};
/* IEEE P802.11az/D2.6 - 9.4.2.303 PASN Parameters element */
@@ -624,7 +641,8 @@
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
- int key_mgmt);
+ int key_mgmt, bool reassoc_resp);
+void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse);
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
@@ -671,6 +689,8 @@
const u8 *he_capabilities;
size_t he_capab_len;
const u8 *he_6ghz_capabilities;
+ const u8 *eht_capabilities;
+ size_t eht_capab_len;
const u8 *supp_channels;
size_t supp_channels_len;
const u8 *supp_oper_classes;
@@ -679,7 +699,6 @@
u16 aid;
const u8 *wmm;
size_t wmm_len;
-#define MAX_NUM_MLO_LINKS 15
u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 06149ec..416e0d6 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -359,6 +359,7 @@
#define AP_EVENT_ENABLED "AP-ENABLED "
#define AP_EVENT_DISABLED "AP-DISABLED "
+#define AP_EVENT_NO_IR "AP-NO_IR"
#define INTERFACE_ENABLED "INTERFACE-ENABLED "
#define INTERFACE_DISABLED "INTERFACE-DISABLED "
@@ -472,6 +473,7 @@
#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
#define WPA_BSS_MASK_RNR BIT(25)
#define WPA_BSS_MASK_ML BIT(26)
+#define WPA_BSS_MASK_AP_MLD_ADDR BIT(27)
/* VENDOR_ELEM_* frame id values */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index ff0869c..0ac8fc1 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -21,6 +21,8 @@
#ifndef CRYPTO_H
#define CRYPTO_H
+#define HMAC_VECTOR_MAX_ELEM 11
+
/**
* md4_vector - MD4 hash for data vector
* @num_elem: Number of elements in the data vector
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index f47beeb..2691743 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -10,25 +10,148 @@
#include "common.h"
#include "crypto.h"
+#include "tls/asn1.h"
/* wolfSSL headers */
-#include <wolfssl/options.h>
+#include <wolfssl/options.h> /* options.h needs to be included first */
+#include <wolfssl/version.h>
+#include <wolfssl/openssl/bn.h>
+#include <wolfssl/wolfcrypt/aes.h>
+#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/asn_public.h>
+#include <wolfssl/wolfcrypt/cmac.h>
+#include <wolfssl/wolfcrypt/des3.h>
+#include <wolfssl/wolfcrypt/dh.h>
+#include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/wolfcrypt/hmac.h>
#include <wolfssl/wolfcrypt/md4.h>
#include <wolfssl/wolfcrypt/md5.h>
+#include <wolfssl/wolfcrypt/pkcs7.h>
+#include <wolfssl/wolfcrypt/pwdbased.h>
#include <wolfssl/wolfcrypt/sha.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/sha512.h>
-#include <wolfssl/wolfcrypt/hmac.h>
-#include <wolfssl/wolfcrypt/pwdbased.h>
-#include <wolfssl/wolfcrypt/arc4.h>
-#include <wolfssl/wolfcrypt/des3.h>
-#include <wolfssl/wolfcrypt/aes.h>
-#include <wolfssl/wolfcrypt/dh.h>
-#include <wolfssl/wolfcrypt/cmac.h>
-#include <wolfssl/wolfcrypt/ecc.h>
-#include <wolfssl/wolfcrypt/asn_public.h>
-#include <wolfssl/wolfcrypt/error-crypt.h>
-#include <wolfssl/openssl/bn.h>
+
+#ifdef CONFIG_FIPS
+#ifndef HAVE_FIPS
+#warning "You are compiling wpa_supplicant/hostapd in FIPS mode but wolfSSL is not configured for FIPS mode."
+#endif /* HAVE_FIPS */
+#endif /* CONFIG_FIPS */
+
+
+#ifdef CONFIG_FIPS
+#if !defined(HAVE_FIPS_VERSION) || HAVE_FIPS_VERSION <= 2
+#define WOLFSSL_OLD_FIPS
+#endif
+#endif
+
+#if LIBWOLFSSL_VERSION_HEX < 0x05004000
+static int wc_EccPublicKeyToDer_ex(ecc_key *key, byte *output,
+ word32 inLen, int with_AlgCurve,
+ int comp)
+{
+ return wc_EccPublicKeyToDer(key, output, inLen, with_AlgCurve);
+}
+#endif /* version < 5.4.0 */
+
+#define LOG_WOLF_ERROR_VA(msg, ...) \
+ wpa_printf(MSG_ERROR, "wolfSSL: %s:%d " msg, \
+ __func__, __LINE__, __VA_ARGS__)
+
+#define LOG_WOLF_ERROR(msg) \
+ LOG_WOLF_ERROR_VA("%s", (msg))
+
+#define LOG_WOLF_ERROR_FUNC(func, err) \
+ LOG_WOLF_ERROR_VA(#func " failed with err: %d %s", \
+ (err), wc_GetErrorString(err))
+
+#define LOG_WOLF_ERROR_FUNC_NULL(func) \
+ LOG_WOLF_ERROR(#func " failed with NULL return")
+
+#define LOG_INVALID_PARAMETERS() \
+ LOG_WOLF_ERROR("invalid input parameters")
+
+
+/* Helper functions to make type allocation uniform */
+
+static WC_RNG * wc_rng_init(void)
+{
+ WC_RNG *ret;
+
+#ifdef CONFIG_FIPS
+ ret = os_zalloc(sizeof(WC_RNG));
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+ } else {
+ int err;
+
+ err = wc_InitRng(ret);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitRng, err);
+ os_free(ret);
+ ret = NULL;
+ }
+ }
+#else /* CONFIG_FIPS */
+ ret = wc_rng_new(NULL, 0, NULL);
+ if (!ret)
+ LOG_WOLF_ERROR_FUNC_NULL(wc_rng_new);
+#endif /* CONFIG_FIPS */
+
+ return ret;
+}
+
+
+static void wc_rng_deinit(WC_RNG *rng)
+{
+#ifdef CONFIG_FIPS
+ wc_FreeRng(rng);
+ os_free(rng);
+#else /* CONFIG_FIPS */
+ wc_rng_free(rng);
+#endif /* CONFIG_FIPS */
+}
+
+
+static ecc_key * ecc_key_init(void)
+{
+ ecc_key *ret;
+#ifdef CONFIG_FIPS
+ int err;
+
+ ret = os_zalloc(sizeof(ecc_key));
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+ } else {
+ err = wc_ecc_init_ex(ret, NULL, INVALID_DEVID);
+ if (err != 0) {
+ LOG_WOLF_ERROR("wc_ecc_init_ex failed");
+ os_free(ret);
+ ret = NULL;
+ }
+ }
+#else /* CONFIG_FIPS */
+ ret = wc_ecc_key_new(NULL);
+ if (!ret)
+ LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_key_new);
+#endif /* CONFIG_FIPS */
+
+ return ret;
+}
+
+
+static void ecc_key_deinit(ecc_key *key)
+{
+#ifdef CONFIG_FIPS
+ wc_ecc_free(key);
+ os_free(key);
+#else /* CONFIG_FIPS */
+ wc_ecc_key_free(key);
+#endif /* CONFIG_FIPS */
+}
+
+/* end of helper functions */
#ifndef CONFIG_FIPS
@@ -56,18 +179,36 @@
{
wc_Md5 md5;
size_t i;
+ int err;
+ int ret = -1;
if (TEST_FAIL())
return -1;
- wc_InitMd5(&md5);
+ err = wc_InitMd5(&md5);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitMd5, err);
+ return -1;
+ }
- for (i = 0; i < num_elem; i++)
- wc_Md5Update(&md5, addr[i], len[i]);
+ for (i = 0; i < num_elem; i++) {
+ err = wc_Md5Update(&md5, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Md5Update, err);
+ goto fail;
+ }
+ }
- wc_Md5Final(&md5, mac);
+ err = wc_Md5Final(&md5, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Md5Final, err);
+ goto fail;
+ }
- return 0;
+ ret = 0;
+fail:
+ wc_Md5Free(&md5);
+ return ret;
}
#endif /* CONFIG_FIPS */
@@ -77,19 +218,36 @@
{
wc_Sha sha;
size_t i;
+ int err;
+ int ret = -1;
if (TEST_FAIL())
return -1;
- wc_InitSha(&sha);
+ err = wc_InitSha(&sha);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitSha, err);
+ return -1;
+ }
- for (i = 0; i < num_elem; i++)
- wc_ShaUpdate(&sha, addr[i], len[i]);
+ for (i = 0; i < num_elem; i++) {
+ err = wc_ShaUpdate(&sha, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ShaUpdate, err);
+ goto fail;
+ }
+ }
- wc_ShaFinal(&sha, mac);
+ err = wc_ShaFinal(&sha, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ShaFinal, err);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
wc_ShaFree(&sha);
-
- return 0;
+ return ret;
}
@@ -99,19 +257,36 @@
{
wc_Sha256 sha256;
size_t i;
+ int err;
+ int ret = -1;
if (TEST_FAIL())
return -1;
- wc_InitSha256(&sha256);
+ err = wc_InitSha256(&sha256);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitSha256, err);
+ return -1;
+ }
- for (i = 0; i < num_elem; i++)
- wc_Sha256Update(&sha256, addr[i], len[i]);
+ for (i = 0; i < num_elem; i++) {
+ err = wc_Sha256Update(&sha256, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha256Update, err);
+ goto fail;
+ }
+ }
- wc_Sha256Final(&sha256, mac);
+ err = wc_Sha256Final(&sha256, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha256Final, err);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
wc_Sha256Free(&sha256);
-
- return 0;
+ return ret;
}
#endif /* NO_SHA256_WRAPPER */
@@ -122,19 +297,36 @@
{
wc_Sha384 sha384;
size_t i;
+ int err;
+ int ret = -1;
if (TEST_FAIL())
return -1;
- wc_InitSha384(&sha384);
+ err = wc_InitSha384(&sha384);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitSha384, err);
+ return -1;
+ }
- for (i = 0; i < num_elem; i++)
- wc_Sha384Update(&sha384, addr[i], len[i]);
+ for (i = 0; i < num_elem; i++) {
+ err = wc_Sha384Update(&sha384, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha384Update, err);
+ goto fail;
+ }
+ }
- wc_Sha384Final(&sha384, mac);
+ err = wc_Sha384Final(&sha384, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha384Final, err);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
wc_Sha384Free(&sha384);
-
- return 0;
+ return ret;
}
#endif /* CONFIG_SHA384 */
@@ -145,19 +337,36 @@
{
wc_Sha512 sha512;
size_t i;
+ int err;
+ int ret = -1;
if (TEST_FAIL())
return -1;
- wc_InitSha512(&sha512);
+ err = wc_InitSha512(&sha512);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_InitSha512, err);
+ return -1;
+ }
- for (i = 0; i < num_elem; i++)
- wc_Sha512Update(&sha512, addr[i], len[i]);
+ for (i = 0; i < num_elem; i++) {
+ err = wc_Sha512Update(&sha512, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha512Update, err);
+ goto fail;
+ }
+ }
- wc_Sha512Final(&sha512, mac);
+ err = wc_Sha512Final(&sha512, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_Sha512Final, err);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
wc_Sha512Free(&sha512);
-
- return 0;
+ return ret;
}
#endif /* CONFIG_SHA512 */
@@ -169,23 +378,43 @@
{
Hmac hmac;
size_t i;
+ int err;
+ int ret = -1;
(void) mdlen;
if (TEST_FAIL())
return -1;
- if (wc_HmacInit(&hmac, NULL, INVALID_DEVID) != 0 ||
- wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
+ err = wc_HmacInit(&hmac, NULL, INVALID_DEVID);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_HmacInit, err);
return -1;
- for (i = 0; i < num_elem; i++)
- if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
- return -1;
- if (wc_HmacFinal(&hmac, mac) != 0)
- return -1;
- wc_HmacFree(&hmac);
+ }
- return 0;
+ err = wc_HmacSetKey(&hmac, type, key, (word32) key_len);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_HmacSetKey, err);
+ goto fail;
+ }
+
+ for (i = 0; i < num_elem; i++) {
+ err = wc_HmacUpdate(&hmac, addr[i], len[i]);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_HmacUpdate, err);
+ goto fail;
+ }
+ }
+ err = wc_HmacFinal(&hmac, mac);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_HmacFinal, err);
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ wc_HmacFree(&hmac);
+ return ret;
}
@@ -289,9 +518,8 @@
ssid, ssid_len, iterations, buflen, WC_SHA);
if (ret != 0) {
if (ret == HMAC_MIN_KEYLEN_E) {
- wpa_printf(MSG_ERROR,
- "wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
- HMAC_FIPS_MIN_KEY);
+ LOG_WOLF_ERROR_VA("wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
+ HMAC_FIPS_MIN_KEY);
}
return -1;
}
@@ -326,15 +554,20 @@
void * aes_encrypt_init(const u8 *key, size_t len)
{
Aes *aes;
+ int err;
if (TEST_FAIL())
return NULL;
aes = os_malloc(sizeof(Aes));
- if (!aes)
+ if (!aes) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
return NULL;
+ }
- if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
+ err = wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION);
+ if (err < 0) {
+ LOG_WOLF_ERROR_FUNC(wc_AesSetKey, err);
os_free(aes);
return NULL;
}
@@ -345,7 +578,18 @@
int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
+#if defined(HAVE_FIPS) && \
+ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2))
+ /* Old FIPS has void return on this API */
wc_AesEncryptDirect(ctx, crypt, plain);
+#else
+ int err = wc_AesEncryptDirect(ctx, crypt, plain);
+
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_AesEncryptDirect, err);
+ return -1;
+ }
+#endif
return 0;
}
@@ -359,15 +603,20 @@
void * aes_decrypt_init(const u8 *key, size_t len)
{
Aes *aes;
+ int err;
if (TEST_FAIL())
return NULL;
aes = os_malloc(sizeof(Aes));
- if (!aes)
+ if (!aes) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
return NULL;
+ }
- if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
+ err = wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION);
+ if (err < 0) {
+ LOG_WOLF_ERROR_FUNC(wc_AesSetKey, err);
os_free(aes);
return NULL;
}
@@ -378,7 +627,18 @@
int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
+#if defined(HAVE_FIPS) && \
+ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2))
+ /* Old FIPS has void return on this API */
wc_AesDecryptDirect(ctx, plain, crypt);
+#else
+ int err = wc_AesDecryptDirect(ctx, plain, crypt);
+
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_AesDecryptDirect, err);
+ return -1;
+ }
+#endif
return 0;
}
@@ -1334,17 +1594,52 @@
#ifdef CONFIG_ECC
+static int crypto_ec_group_2_id(int group)
+{
+ switch (group) {
+ case 19:
+ return ECC_SECP256R1;
+ case 20:
+ return ECC_SECP384R1;
+ case 21:
+ return ECC_SECP521R1;
+ case 25:
+ return ECC_SECP192R1;
+ case 26:
+ return ECC_SECP224R1;
+#ifdef HAVE_ECC_BRAINPOOL
+ case 27:
+ return ECC_BRAINPOOLP224R1;
+ case 28:
+ return ECC_BRAINPOOLP256R1;
+ case 29:
+ return ECC_BRAINPOOLP384R1;
+ case 30:
+ return ECC_BRAINPOOLP512R1;
+#endif /* HAVE_ECC_BRAINPOOL */
+ default:
+ LOG_WOLF_ERROR_VA("Unsupported curve (id=%d) in EC key", group);
+ return ECC_CURVE_INVALID;
+ }
+}
+
+
int ecc_map(ecc_point *, mp_int *, mp_digit);
int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
mp_int *a, mp_int *modulus, mp_digit mp);
struct crypto_ec {
- ecc_key key;
+ ecc_key *key;
+#ifdef CONFIG_DPP
+ ecc_point *g; /* Only used in DPP for now */
+#endif /* CONFIG_DPP */
mp_int a;
mp_int prime;
mp_int order;
mp_digit mont_b;
mp_int b;
+ int curve_id;
+ bool own_key; /* Should we free the `key` */
};
@@ -1352,59 +1647,98 @@
{
int built = 0;
struct crypto_ec *e;
- int curve_id;
+ int curve_id = crypto_ec_group_2_id(group);
+ int err;
- /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
- switch (group) {
- case 19:
- curve_id = ECC_SECP256R1;
- break;
- case 20:
- curve_id = ECC_SECP384R1;
- break;
- case 21:
- curve_id = ECC_SECP521R1;
- break;
- case 25:
- curve_id = ECC_SECP192R1;
- break;
- case 26:
- curve_id = ECC_SECP224R1;
- break;
-#ifdef HAVE_ECC_BRAINPOOL
- case 27:
- curve_id = ECC_BRAINPOOLP224R1;
- break;
- case 28:
- curve_id = ECC_BRAINPOOLP256R1;
- break;
- case 29:
- curve_id = ECC_BRAINPOOLP384R1;
- break;
- case 30:
- curve_id = ECC_BRAINPOOLP512R1;
- break;
-#endif /* HAVE_ECC_BRAINPOOL */
- default:
+ if (curve_id == ECC_CURVE_INVALID) {
+ LOG_INVALID_PARAMETERS();
return NULL;
}
e = os_zalloc(sizeof(*e));
- if (!e)
+ if (!e) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
return NULL;
+ }
- if (wc_ecc_init(&e->key) != 0 ||
- wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
- mp_init(&e->a) != MP_OKAY ||
- mp_init(&e->prime) != MP_OKAY ||
- mp_init(&e->order) != MP_OKAY ||
- mp_init(&e->b) != MP_OKAY ||
- mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
- mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
- mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
- mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
- mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
+ e->curve_id = curve_id;
+ e->own_key = true;
+ e->key = ecc_key_init();
+ if (!e->key) {
+ LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
goto done;
+ }
+
+ err = wc_ecc_set_curve(e->key, 0, curve_id);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_set_curve, err);
+ goto done;
+ }
+#ifdef CONFIG_DPP
+ e->g = wc_ecc_new_point();
+ if (!e->g) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
+ goto done;
+ }
+#ifdef CONFIG_FIPS
+ /* Setup generator manually in FIPS mode */
+ if (!e->key->dp) {
+ LOG_WOLF_ERROR_FUNC_NULL(e->key->dp);
+ goto done;
+ }
+ err = mp_read_radix(e->g->x, e->key->dp->Gx, MP_RADIX_HEX);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_read_radix(e->g->y, e->key->dp->Gy, MP_RADIX_HEX);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_set(e->g->z, 1);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_set, err);
+ goto done;
+ }
+#else /* CONFIG_FIPS */
+ err = wc_ecc_get_generator(e->g, wc_ecc_get_curve_idx(curve_id));
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_get_generator, err);
+ goto done;
+ }
+#endif /* CONFIG_FIPS */
+#endif /* CONFIG_DPP */
+ err = mp_init_multi(&e->a, &e->prime, &e->order, &e->b, NULL, NULL);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_init_multi, err);
+ goto done;
+ }
+ err = mp_read_radix(&e->a, e->key->dp->Af, 16);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_read_radix(&e->b, e->key->dp->Bf, 16);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_read_radix(&e->prime, e->key->dp->prime, 16);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_read_radix(&e->order, e->key->dp->order, 16);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+ goto done;
+ }
+ err = mp_montgomery_setup(&e->prime, &e->mont_b);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_montgomery_setup, err);
+ goto done;
+ }
built = 1;
done:
@@ -1425,7 +1759,11 @@
mp_clear(&e->order);
mp_clear(&e->prime);
mp_clear(&e->a);
- wc_ecc_free(&e->key);
+#ifdef CONFIG_DPP
+ wc_ecc_del_point(e->g);
+#endif /* CONFIG_DPP */
+ if (e->own_key)
+ ecc_key_deinit(e->key);
os_free(e);
}
@@ -1490,14 +1828,26 @@
return;
if (clear) {
+#ifdef CONFIG_FIPS
mp_forcezero(point->x);
mp_forcezero(point->y);
mp_forcezero(point->z);
+#else /* CONFIG_FIPS */
+ wc_ecc_forcezero_point(point);
+#endif /* CONFIG_FIPS */
}
wc_ecc_del_point(point);
}
+#ifdef CONFIG_DPP
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
+{
+ return (const struct crypto_ec_point *) e->g;
+}
+#endif /* CONFIG_DPP */
+
+
int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
struct crypto_bignum *x)
{
@@ -1509,27 +1859,41 @@
const struct crypto_ec_point *point, u8 *x, u8 *y)
{
ecc_point *p = (ecc_point *) point;
+ int len;
+ int err;
if (TEST_FAIL())
return -1;
if (!mp_isone(p->z)) {
- if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
+ err = ecc_map(p, &e->prime, e->mont_b);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(ecc_map, err);
return -1;
+ }
+ }
+
+ len = wc_ecc_get_curve_size_from_id(e->curve_id);
+ if (len <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_get_curve_size_from_id, len);
+ LOG_WOLF_ERROR_VA("wc_ecc_get_curve_size_from_id error for curve_id %d", e->curve_id);
+ return -1;
}
if (x) {
if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
- e->key.dp->size,
- e->key.dp->size) <= 0)
+ (size_t) len, (size_t) len) <= 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_bignum_to_bin, -1);
return -1;
+ }
}
if (y) {
if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
- e->key.dp->size,
- e->key.dp->size) <= 0)
+ (size_t) len, (size_t) len) <= 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_bignum_to_bin, -1);
return -1;
+ }
}
return 0;
@@ -1549,10 +1913,10 @@
if (!point)
goto done;
- if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
+ if (mp_read_unsigned_bin(point->x, val, e->key->dp->size) != MP_OKAY)
goto done;
- val += e->key.dp->size;
- if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
+ val += e->key->dp->size;
+ if (mp_read_unsigned_bin(point->y, val, e->key->dp->size) != MP_OKAY)
goto done;
mp_set(point->z, 1);
@@ -1710,53 +2074,123 @@
return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
}
+struct crypto_ec_key {
+ ecc_key *eckey;
+ WC_RNG *rng; /* Needs to be initialized before use.
+ * *NOT* initialized in crypto_ec_key_init */
+};
+
struct crypto_ecdh {
struct crypto_ec *ec;
- WC_RNG rng;
+ WC_RNG *rng;
};
-struct crypto_ecdh * crypto_ecdh_init(int group)
+static struct crypto_ecdh * _crypto_ecdh_init(int group)
{
struct crypto_ecdh *ecdh = NULL;
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
int ret;
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
ecdh = os_zalloc(sizeof(*ecdh));
- if (!ecdh)
- goto fail;
+ if (!ecdh) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+ return NULL;
+ }
- if (wc_InitRng(&ecdh->rng) != 0)
+ ecdh->rng = wc_rng_init();
+ if (!ecdh->rng) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init);
goto fail;
+ }
ecdh->ec = crypto_ec_init(group);
- if (!ecdh->ec)
+ if (!ecdh->ec) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_init);
goto fail;
+ }
- ret = wc_ecc_make_key_ex(&ecdh->rng, ecdh->ec->key.dp->size,
- &ecdh->ec->key, ecdh->ec->key.dp->id);
- if (ret < 0)
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+ ret = wc_ecc_set_rng(ecdh->ec->key, ecdh->rng);
+ if (ret != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_set_rng, ret);
goto fail;
+ }
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
-#if defined(ECC_TIMING_RESISTANT) && !defined(CONFIG_FIPS)
- ret = wc_ecc_set_rng(&ecdh->ec->key, &ecdh->rng);
- if (ret < 0)
- goto fail;
-#endif /* ECC_TIMING_RESISTANT && !CONFIG_FIPS */
-
-done:
return ecdh;
fail:
crypto_ecdh_deinit(ecdh);
- ecdh = NULL;
- goto done;
+ return NULL;
+}
+
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+ struct crypto_ecdh *ret = NULL;
+ int err;
+
+ ret = _crypto_ecdh_init(group);
+
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(_crypto_ecdh_init);
+ return NULL;
+ }
+
+ err = wc_ecc_make_key_ex(ret->rng, 0, ret->ec->key,
+ crypto_ec_group_2_id(group));
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_make_key_ex, err);
+ crypto_ecdh_deinit(ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+
+struct crypto_ecdh * crypto_ecdh_init2(int group, struct crypto_ec_key *own_key)
+{
+ struct crypto_ecdh *ret = NULL;
+
+ if (!own_key || crypto_ec_key_group(own_key) != group) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ ret = _crypto_ecdh_init(group);
+ if (ret) {
+ /* Already init'ed to the right group. Enough to substitute the
+ * key. */
+ ecc_key_deinit(ret->ec->key);
+ ret->ec->key = own_key->eckey;
+ ret->ec->own_key = false;
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+ if (!ret->ec->key->rng) {
+ int err = wc_ecc_set_rng(ret->ec->key, ret->rng);
+
+ if (err != 0)
+ LOG_WOLF_ERROR_FUNC(wc_ecc_set_rng, err);
+ }
+#endif /* ECC_TIMING_RESISTANT && !CONFIG_FIPS */
+ }
+
+ return ret;
}
void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
{
if (ecdh) {
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+ /* Disassociate the rng */
+ if (ecdh->ec && ecdh->ec->key &&
+ ecdh->ec->key->rng == ecdh->rng)
+ (void) wc_ecc_set_rng(ecdh->ec->key, NULL);
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
crypto_ec_deinit(ecdh->ec);
- wc_FreeRng(&ecdh->rng);
+ wc_rng_deinit(ecdh->rng);
os_free(ecdh);
}
}
@@ -1766,20 +2200,20 @@
{
struct wpabuf *buf = NULL;
int ret;
- int len = ecdh->ec->key.dp->size;
+ int len = ecdh->ec->key->dp->size;
buf = wpabuf_alloc(inc_y ? 2 * len : len);
if (!buf)
goto fail;
ret = crypto_bignum_to_bin((struct crypto_bignum *)
- ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
+ ecdh->ec->key->pubkey.x, wpabuf_put(buf, len),
len, len);
if (ret < 0)
goto fail;
if (inc_y) {
ret = crypto_bignum_to_bin((struct crypto_bignum *)
- ecdh->ec->key.pubkey.y,
+ ecdh->ec->key->pubkey.y,
wpabuf_put(buf, len), len, len);
if (ret < 0)
goto fail;
@@ -1800,35 +2234,47 @@
int ret;
struct wpabuf *pubkey = NULL;
struct wpabuf *secret = NULL;
- word32 key_len = ecdh->ec->key.dp->size;
+ word32 key_len = ecdh->ec->key->dp->size;
ecc_point *point = NULL;
size_t need_key_len = inc_y ? 2 * key_len : key_len;
- if (len < need_key_len)
+ if (len < need_key_len) {
+ LOG_WOLF_ERROR("key len too small");
goto fail;
+ }
pubkey = wpabuf_alloc(1 + 2 * key_len);
- if (!pubkey)
+ if (!pubkey) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
goto fail;
+ }
wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
wpabuf_put_data(pubkey, key, need_key_len);
point = wc_ecc_new_point();
- if (!point)
+ if (!point) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
goto fail;
+ }
ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
- ecdh->ec->key.idx, point);
- if (ret != MP_OKAY)
+ ecdh->ec->key->idx, point);
+ if (ret != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_import_point_der, ret);
goto fail;
+ }
secret = wpabuf_alloc(key_len);
- if (!secret)
+ if (!secret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
goto fail;
+ }
- ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
+ ret = wc_ecc_shared_secret_ex(ecdh->ec->key, point,
wpabuf_put(secret, key_len), &key_len);
- if (ret != MP_OKAY)
+ if (ret != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_shared_secret_ex, ret);
goto fail;
+ }
done:
wc_ecc_del_point(point);
@@ -1846,41 +2292,21 @@
return crypto_ec_prime_len(ecdh->ec);
}
-
-struct crypto_ec_key {
- ecc_key *eckey;
- WC_RNG *rng; /* Needs to be initialized before use.
- * *NOT* initialized in crypto_ec_key_init */
-};
-
-
static struct crypto_ec_key * crypto_ec_key_init(void)
{
struct crypto_ec_key *key;
key = os_zalloc(sizeof(struct crypto_ec_key));
if (key) {
-#ifdef CONFIG_FIPS
- key->eckey = os_zalloc(sizeof(ecc_key));
-#else /* CONFIG_FIPS */
- key->eckey = wc_ecc_key_new(NULL);
-#endif /* CONFIG_FIPS */
+ key->eckey = ecc_key_init();
/* Omit key->rng initialization because it seeds itself and thus
* consumes entropy that may never be used. Lazy initialize when
* necessary. */
if (!key->eckey) {
- wpa_printf(MSG_ERROR,
- "wolfSSL: crypto_ec_key_init() failed");
+ LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
crypto_ec_key_deinit(key);
key = NULL;
}
-#ifdef CONFIG_FIPS
- else if (wc_ecc_init_ex(key->eckey, NULL, INVALID_DEVID) != 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_init_ex failed");
- crypto_ec_key_deinit(key);
- key = NULL;
- }
-#endif /* CONFIG_FIPS */
}
return key;
}
@@ -1889,32 +2315,40 @@
void crypto_ec_key_deinit(struct crypto_ec_key *key)
{
if (key) {
-#ifdef CONFIG_FIPS
- os_free(key->rng);
- os_free(key->eckey);
-#else /* CONFIG_FIPS */
- wc_rng_free(key->rng);
- wc_ecc_key_free(key->eckey);
-#endif /* CONFIG_FIPS */
+ ecc_key_deinit(key->eckey);
+ wc_rng_deinit(key->rng);
os_free(key);
}
}
+static WC_RNG * crypto_ec_key_init_rng(struct crypto_ec_key *key)
+{
+ if (!key->rng) {
+ /* Lazy init key->rng */
+ key->rng = wc_rng_init();
+ if (!key->rng)
+ LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init);
+ }
+ return key->rng;
+}
+
+
struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
{
struct crypto_ec_key *ret;
word32 idx = 0;
+ int err;
ret = crypto_ec_key_init();
if (!ret) {
- wpa_printf(MSG_ERROR, "wolfSSL: crypto_ec_key_init failed");
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
goto fail;
}
- if (wc_EccPrivateKeyDecode(der, &idx, ret->eckey, (word32) der_len) !=
- 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPrivateKeyDecode failed");
+ err = wc_EccPrivateKeyDecode(der, &idx, ret->eckey, (word32) der_len);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_EccPrivateKeyDecode, err);
goto fail;
}
@@ -1930,8 +2364,7 @@
{
if (!key || !key->eckey || !key->eckey->dp) {
- wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
- __func__);
+ LOG_INVALID_PARAMETERS();
return -1;
}
@@ -1942,54 +2375,114 @@
return 20;
case ECC_SECP521R1:
return 21;
+ case ECC_SECP192R1:
+ return 25;
+ case ECC_SECP224R1:
+ return 26;
+#ifdef HAVE_ECC_BRAINPOOL
+ case ECC_BRAINPOOLP224R1:
+ return 27;
case ECC_BRAINPOOLP256R1:
return 28;
case ECC_BRAINPOOLP384R1:
return 29;
case ECC_BRAINPOOLP512R1:
return 30;
+#endif /* HAVE_ECC_BRAINPOOL */
}
- wpa_printf(MSG_ERROR, "wolfSSL: Unsupported curve (id=%d) in EC key",
- key->eckey->dp->id);
+ LOG_WOLF_ERROR_VA("Unsupported curve (id=%d) in EC key",
+ key->eckey->dp->id);
return -1;
}
+static int crypto_ec_key_gen_public_key(struct crypto_ec_key *key)
+{
+ int err;
+
+#ifdef WOLFSSL_OLD_FIPS
+ err = wc_ecc_make_pub(key->eckey, NULL);
+#else /* WOLFSSL_OLD_FIPS */
+ /* Have wolfSSL generate the public key to make it available for output
+ */
+ if (!crypto_ec_key_init_rng(key)) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+ return -1;
+ }
+
+ err = wc_ecc_make_pub_ex(key->eckey, NULL, key->rng);
+#endif /* WOLFSSL_OLD_FIPS */
+
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_make_pub_ex, err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
{
- byte *der = NULL;
int der_len;
struct wpabuf *ret = NULL;
+ int err;
if (!key || !key->eckey) {
- wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
- __func__);
+ LOG_INVALID_PARAMETERS();
goto fail;
}
- der_len = wc_EccPublicKeyDerSize(key->eckey, 1);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyDerSize failed");
+#ifdef WOLFSSL_OLD_FIPS
+ if (key->eckey->type == ECC_PRIVATEKEY_ONLY &&
+ crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ goto fail;
+ }
+#endif /* WOLFSSL_OLD_FIPS */
+
+ der_len = err = wc_EccPublicKeyToDer_ex(key->eckey, NULL, 0, 1, 1);
+ if (err == ECC_PRIVATEONLY_E) {
+ if (crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ goto fail;
+ }
+ der_len = err = wc_EccPublicKeyToDer_ex(key->eckey, NULL, 0, 1,
+ 1);
+ }
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyDerSize, err);
goto fail;
}
- der = os_malloc(der_len);
- if (!der)
- goto fail;
-
- der_len = wc_EccPublicKeyToDer(key->eckey, der, der_len, 1);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyToDer failed");
+ ret = wpabuf_alloc(der_len);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
goto fail;
}
- ret = wpabuf_alloc_copy(der, der_len);
- os_free(der);
+ err = wc_EccPublicKeyToDer_ex(key->eckey, wpabuf_mhead(ret), der_len, 1,
+ 1);
+ if (err == ECC_PRIVATEONLY_E) {
+ if (crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ goto fail;
+ }
+ err = wc_EccPublicKeyToDer_ex(key->eckey, wpabuf_mhead(ret),
+ der_len, 1, 1);
+ }
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyToDer, err);
+ goto fail;
+ }
+ der_len = err;
+ wpabuf_put(ret, der_len);
+
return ret;
fail:
- os_free(der);
+ wpabuf_free(ret);
return NULL;
}
@@ -1998,16 +2491,17 @@
{
word32 idx = 0;
struct crypto_ec_key *ret = NULL;
+ int err;
ret = crypto_ec_key_init();
if (!ret) {
- wpa_printf(MSG_ERROR, "wolfSSL: crypto_ec_key_init failed");
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
goto fail;
}
- if (wc_EccPublicKeyDecode(der, &idx, ret->eckey, (word32) der_len) != 0)
- {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyDecode failed");
+ err = wc_EccPublicKeyDecode(der, &idx, ret->eckey, (word32) der_len);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyDecode, err);
goto fail;
}
@@ -2021,60 +2515,45 @@
struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
size_t len)
{
- byte *der = NULL;
int der_len;
+ int err;
word32 w32_der_len;
struct wpabuf *ret = NULL;
if (!key || !key->eckey || !data || len == 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
- __func__);
+ LOG_INVALID_PARAMETERS();
goto fail;
}
- if (!key->rng) {
- /* Lazy init key->rng */
-#ifdef CONFIG_FIPS
- key->rng = os_zalloc(sizeof(WC_RNG));
-#else /* CONFIG_FIPS */
- key->rng = wc_rng_new(NULL, 0, NULL);
-#endif /* CONFIG_FIPS */
- if (!key->rng) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_rng_new failed");
- goto fail;
- }
-#ifdef CONFIG_FIPS
- if (wc_InitRng(key->rng) != 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_InitRng failed");
- goto fail;
- }
-#endif /* CONFIG_FIPS */
+ if (!crypto_ec_key_init_rng(key)) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+ goto fail;
}
der_len = wc_ecc_sig_size(key->eckey);
if (der_len <= 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_sig_size failed");
+ LOG_WOLF_ERROR_FUNC(wc_ecc_sig_size, der_len);
goto fail;
}
- der = os_malloc(der_len);
- if (!der)
+ ret = wpabuf_alloc(der_len);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
goto fail;
+ }
w32_der_len = (word32) der_len;
- if (wc_ecc_sign_hash(data, len, der, &w32_der_len, key->rng, key->eckey)
- != 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_sign_hash failed");
+ err = wc_ecc_sign_hash(data, len, wpabuf_mhead(ret), &w32_der_len,
+ key->rng, key->eckey);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash, err);
goto fail;
}
+ wpabuf_put(ret, w32_der_len);
- ret = wpabuf_alloc_copy(der, der_len);
- os_free(der);
- if (!ret)
- wpa_printf(MSG_ERROR, "wolfSSL: wpabuf_alloc_copy failed");
return ret;
fail:
- os_free(der);
+ wpabuf_free(ret);
return NULL;
}
@@ -2085,26 +2564,996 @@
int res = 0;
if (!key || !key->eckey || !data || len == 0 || !sig || sig_len == 0) {
- wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
- __func__);
+ LOG_INVALID_PARAMETERS();
return -1;
}
if (wc_ecc_verify_hash(sig, sig_len, data, len, &res, key->eckey) != 0)
{
- wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_verify_hash failed");
+ LOG_WOLF_ERROR("wc_ecc_verify_hash failed");
return -1;
}
if (res != 1)
- wpa_printf(MSG_DEBUG,
- "wolfSSL: crypto_ec_key_verify_signature failed");
+ LOG_WOLF_ERROR("crypto_ec_key_verify_signature failed");
return res;
}
#endif /* CONFIG_ECC */
+#ifdef CONFIG_DPP
+
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+ bool include_pub)
+{
+ int len;
+ int err;
+ struct wpabuf *ret = NULL;
+
+ if (!key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+#ifdef WOLFSSL_OLD_FIPS
+ if (key->eckey->type != ECC_PRIVATEKEY &&
+ key->eckey->type != ECC_PRIVATEKEY_ONLY) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+#endif /* WOLFSSL_OLD_FIPS */
+
+ len = err = wc_EccKeyDerSize(key->eckey, include_pub);
+ if (err == ECC_PRIVATEONLY_E && include_pub) {
+ if (crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ return NULL;
+ }
+ len = err = wc_EccKeyDerSize(key->eckey, include_pub);
+ }
+ if (err <= 0) {
+ /* Exception for BAD_FUNC_ARG because higher levels blindly call
+ * this function to determine if this is a private key or not.
+ * BAD_FUNC_ARG most probably means that key->eckey is a public
+ * key not private. */
+ if (err != BAD_FUNC_ARG)
+ LOG_WOLF_ERROR_FUNC(wc_EccKeyDerSize, err);
+ return NULL;
+ }
+
+ ret = wpabuf_alloc(len);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+ return NULL;
+ }
+
+ if (include_pub)
+ err = wc_EccKeyToDer(key->eckey, wpabuf_put(ret, len), len);
+ else
+ err = wc_EccPrivateKeyToDer(key->eckey, wpabuf_put(ret, len),
+ len);
+
+ if (err != len) {
+ LOG_WOLF_ERROR_VA("%s failed with err: %d", include_pub ?
+ "wc_EccKeyToDer" : "wc_EccPrivateKeyToDer",
+ err);
+ wpabuf_free(ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+ int prefix)
+{
+ int err;
+ word32 len = 0;
+ struct wpabuf *ret = NULL;
+
+ if (!key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ err = wc_ecc_export_x963(key->eckey, NULL, &len);
+ if (err != LENGTH_ONLY_E) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+ goto fail;
+ }
+
+ ret = wpabuf_alloc(len);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+ goto fail;
+ }
+
+ err = wc_ecc_export_x963(key->eckey, wpabuf_mhead(ret), &len);
+ if (err == ECC_PRIVATEONLY_E) {
+ if (crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ goto fail;
+ }
+ err = wc_ecc_export_x963(key->eckey, wpabuf_mhead(ret), &len);
+ }
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+ goto fail;
+ }
+
+ if (!prefix)
+ os_memmove(wpabuf_mhead(ret), wpabuf_mhead_u8(ret) + 1,
+ (size_t)--len);
+ wpabuf_put(ret, len);
+
+ return ret;
+
+fail:
+ wpabuf_free(ret);
+ return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
+ const u8 *y, size_t len)
+{
+ struct crypto_ec_key *ret = NULL;
+ int curve_id = crypto_ec_group_2_id(group);
+ int err;
+
+ if (!x || !y || len == 0 || curve_id == ECC_CURVE_INVALID ||
+ wc_ecc_get_curve_size_from_id(curve_id) != (int) len) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ ret = crypto_ec_key_init();
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+ return NULL;
+ }
+
+ /* Cast necessary for FIPS API */
+ err = wc_ecc_import_unsigned(ret->eckey, (u8 *) x, (u8 *) y, NULL,
+ curve_id);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_import_unsigned, err);
+ crypto_ec_key_deinit(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
+{
+ int ret;
+ struct wpabuf *key1_buf = crypto_ec_key_get_subject_public_key(key1);
+ struct wpabuf *key2_buf = crypto_ec_key_get_subject_public_key(key2);
+
+ if ((key1 && !key1_buf) || (key2 && !key2_buf)) {
+ LOG_WOLF_ERROR("crypto_ec_key_get_subject_public_key failed");
+ return -1;
+ }
+
+ ret = wpabuf_cmp(key1_buf, key2_buf);
+ if (ret != 0)
+ ret = -1; /* Default to -1 for different keys */
+
+ wpabuf_clear_free(key1_buf);
+ wpabuf_clear_free(key2_buf);
+ return ret;
+}
+
+
+/* wolfSSL doesn't have a pretty print function for keys so just print out the
+ * PEM of the private key. */
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+ const char *title)
+{
+ struct wpabuf * key_buf;
+ struct wpabuf * out = NULL;
+ int err;
+ int pem_len;
+
+ if (!key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return;
+ }
+
+ if (key->eckey->type == ECC_PUBLICKEY)
+ key_buf = crypto_ec_key_get_subject_public_key(
+ (struct crypto_ec_key *) key);
+ else
+ key_buf = crypto_ec_key_get_ecprivate_key(
+ (struct crypto_ec_key *) key, 1);
+
+ if (!key_buf) {
+ LOG_WOLF_ERROR_VA("%s has returned NULL",
+ key->eckey->type == ECC_PUBLICKEY ?
+ "crypto_ec_key_get_subject_public_key" :
+ "crypto_ec_key_get_ecprivate_key");
+ goto fail;
+ }
+
+ if (!title)
+ title = "";
+
+ err = wc_DerToPem(wpabuf_head(key_buf), wpabuf_len(key_buf), NULL, 0,
+ ECC_TYPE);
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+ goto fail;
+ }
+ pem_len = err;
+
+ out = wpabuf_alloc(pem_len + 1);
+ if (!out) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_DerToPem);
+ goto fail;
+ }
+
+ err = wc_DerToPem(wpabuf_head(key_buf), wpabuf_len(key_buf),
+ wpabuf_mhead(out), pem_len, ECC_TYPE);
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+ goto fail;
+ }
+
+ wpabuf_mhead_u8(out)[err] = '\0';
+ wpabuf_put(out, err + 1);
+ wpa_printf(MSG_DEBUG, "%s:\n%s", title,
+ (const char *) wpabuf_head(out));
+
+fail:
+ wpabuf_clear_free(key_buf);
+ wpabuf_clear_free(out);
+}
+
+
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+ const struct crypto_ec_point *p,
+ const char *title)
+{
+ u8 x[ECC_MAXSIZE];
+ u8 y[ECC_MAXSIZE];
+ int coord_size;
+ int err;
+
+ if (!p || !e) {
+ LOG_INVALID_PARAMETERS();
+ return;
+ }
+
+ coord_size = e->key->dp->size;
+
+ if (!title)
+ title = "";
+
+ err = crypto_ec_point_to_bin((struct crypto_ec *)e, p, x, y);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_point_to_bin, err);
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, title, x, coord_size);
+ wpa_hexdump(MSG_DEBUG, title, y, coord_size);
+}
+
+
+struct crypto_ec_key * crypto_ec_key_gen(int group)
+{
+ int curve_id = crypto_ec_group_2_id(group);
+ int err;
+ struct crypto_ec_key * ret = NULL;
+
+ if (curve_id == ECC_CURVE_INVALID) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ ret = crypto_ec_key_init();
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+ return NULL;
+ }
+
+ if (!crypto_ec_key_init_rng(ret)) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+ goto fail;
+ }
+
+ err = wc_ecc_make_key_ex(ret->rng, 0, ret->eckey, curve_id);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_make_key_ex, err);
+ goto fail;
+ }
+
+ return ret;
+fail:
+ crypto_ec_key_deinit(ret);
+ return NULL;
+}
+
+
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len,
+ const u8 *r, size_t r_len,
+ const u8 *s, size_t s_len)
+{
+ int err;
+ u8 sig[ECC_MAX_SIG_SIZE];
+ word32 sig_len = ECC_MAX_SIG_SIZE;
+
+ if (!key || !key->eckey || !data || !len || !r || !r_len ||
+ !s || !s_len) {
+ LOG_INVALID_PARAMETERS();
+ return -1;
+ }
+
+ err = wc_ecc_rs_raw_to_sig(r, r_len, s, s_len, sig, &sig_len);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_rs_raw_to_sig, err);
+ return -1;
+ }
+
+ return crypto_ec_key_verify_signature(key, data, len, sig, sig_len);
+}
+
+
+struct crypto_ec_point * crypto_ec_key_get_public_key(struct crypto_ec_key *key)
+{
+ ecc_point *point = NULL;
+ int err;
+ u8 *der = NULL;
+ word32 der_len = 0;
+
+ if (!key || !key->eckey || !key->eckey->dp) {
+ LOG_INVALID_PARAMETERS();
+ goto fail;
+ }
+
+ err = wc_ecc_export_x963(key->eckey, NULL, &der_len);
+ if (err != LENGTH_ONLY_E) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+ goto fail;
+ }
+
+ der = os_malloc(der_len);
+ if (!der) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+ goto fail;
+ }
+
+ err = wc_ecc_export_x963(key->eckey, der, &der_len);
+ if (err == ECC_PRIVATEONLY_E) {
+ if (crypto_ec_key_gen_public_key(key) != 0) {
+ LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+ goto fail;
+ }
+ err = wc_ecc_export_x963(key->eckey, der, &der_len);
+ }
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+ goto fail;
+ }
+
+ point = wc_ecc_new_point();
+ if (!point) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
+ goto fail;
+ }
+
+ err = wc_ecc_import_point_der(der, der_len, key->eckey->idx, point);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_import_point_der, err);
+ goto fail;
+ }
+
+ os_free(der);
+ return (struct crypto_ec_point *) point;
+
+fail:
+ os_free(der);
+ if (point)
+ wc_ecc_del_point(point);
+ return NULL;
+}
+
+
+struct crypto_bignum * crypto_ec_key_get_private_key(struct crypto_ec_key *key)
+{
+ u8 priv[ECC_MAXSIZE];
+ word32 priv_len = ECC_MAXSIZE;
+#ifdef WOLFSSL_OLD_FIPS
+ /* Needed to be compliant with the old API */
+ u8 qx[ECC_MAXSIZE];
+ word32 qx_len = ECC_MAXSIZE;
+ u8 qy[ECC_MAXSIZE];
+ word32 qy_len = ECC_MAXSIZE;
+#endif /* WOLFSSL_OLD_FIPS */
+ struct crypto_bignum *ret = NULL;
+ int err;
+
+ if (!key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+#ifndef WOLFSSL_OLD_FIPS
+ err = wc_ecc_export_private_raw(key->eckey, NULL, NULL, NULL, NULL,
+ priv, &priv_len);
+#else /* WOLFSSL_OLD_FIPS */
+ err = wc_ecc_export_private_raw(key->eckey, qx, &qx_len, qy, &qy_len,
+ priv, &priv_len);
+#endif /* WOLFSSL_OLD_FIPS */
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_private_raw, err);
+ return NULL;
+ }
+
+ ret = crypto_bignum_init_set(priv, priv_len);
+ forced_memzero(priv, priv_len);
+ return ret;
+}
+
+
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len)
+{
+ int err;
+ u8 success = 0;
+ mp_int r;
+ mp_int s;
+ u8 rs_init = 0;
+ int sz;
+ struct wpabuf * ret = NULL;
+
+ if (!key || !key->eckey || !key->eckey->dp || !data || !len) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ sz = key->eckey->dp->size;
+
+ if (!crypto_ec_key_init_rng(key)) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+ goto fail;
+ }
+
+ err = mp_init_multi(&r, &s, NULL, NULL, NULL, NULL);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(mp_init_multi, err);
+ goto fail;
+ }
+ rs_init = 1;
+
+ err = wc_ecc_sign_hash_ex(data, len, key->rng, key->eckey, &r, &s);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash_ex, err);
+ goto fail;
+ }
+
+ if (mp_unsigned_bin_size(&r) > sz || mp_unsigned_bin_size(&s) > sz) {
+ LOG_WOLF_ERROR_VA("Unexpected size of r or s (%d %d %d)", sz,
+ mp_unsigned_bin_size(&r),
+ mp_unsigned_bin_size(&s));
+ goto fail;
+ }
+
+ ret = wpabuf_alloc(2 * sz);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+ goto fail;
+ }
+
+ err = mp_to_unsigned_bin_len(&r, wpabuf_put(ret, sz), sz);
+ if (err == MP_OKAY)
+ err = mp_to_unsigned_bin_len(&s, wpabuf_put(ret, sz), sz);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash_ex, err);
+ goto fail;
+ }
+
+ success = 1;
+fail:
+ if (rs_init) {
+ mp_free(&r);
+ mp_free(&s);
+ }
+ if (!success) {
+ wpabuf_free(ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *e,
+ const struct crypto_ec_point *pub)
+{
+ struct crypto_ec_key *ret = NULL;
+ int err;
+ byte *buf = NULL;
+ word32 buf_len = 0;
+
+ if (!e || !pub) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ /* Export to DER to not mess with wolfSSL internals */
+ err = wc_ecc_export_point_der(wc_ecc_get_curve_idx(e->curve_id),
+ (ecc_point *) pub, NULL, &buf_len);
+ if (err != LENGTH_ONLY_E || !buf_len) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_point_der, err);
+ goto fail;
+ }
+
+ buf = os_malloc(buf_len);
+ if (!buf) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+ goto fail;
+ }
+
+ err = wc_ecc_export_point_der(wc_ecc_get_curve_idx(e->curve_id),
+ (ecc_point *) pub, buf, &buf_len);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_export_point_der, err);
+ goto fail;
+ }
+
+ ret = crypto_ec_key_init();
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+ goto fail;
+ }
+
+ err = wc_ecc_import_x963_ex(buf, buf_len, ret->eckey, e->curve_id);
+ if (err != MP_OKAY) {
+ LOG_WOLF_ERROR_FUNC(wc_ecc_import_x963_ex, err);
+ goto fail;
+ }
+
+ os_free(buf);
+ return ret;
+
+fail:
+ os_free(buf);
+ crypto_ec_key_deinit(ret);
+ return NULL;
+}
+
+
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
+{
+ PKCS7 *p7 = NULL;
+ struct wpabuf *ret = NULL;
+ int err = 0;
+ int total_sz = 0;
+ int i;
+
+ if (!pkcs7) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ if (!p7) {
+ LOG_WOLF_ERROR_FUNC_NULL(wc_PKCS7_New);
+ return NULL;
+ }
+
+ err = wc_PKCS7_VerifySignedData(p7, (byte *) wpabuf_head(pkcs7),
+ wpabuf_len(pkcs7));
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_PKCS7_VerifySignedData, err);
+ wc_PKCS7_Free(p7);
+ goto fail;
+ }
+
+ /* Need to access p7 members directly */
+ for (i = 0; i < MAX_PKCS7_CERTS; i++) {
+ if (p7->certSz[i] == 0)
+ continue;
+ err = wc_DerToPem(p7->cert[i], p7->certSz[i], NULL, 0,
+ CERT_TYPE);
+ if (err > 0) {
+ total_sz += err;
+ } else {
+ LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+ goto fail;
+ }
+ }
+
+ if (total_sz == 0) {
+ LOG_WOLF_ERROR("No certificates found in PKCS7 input");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc(total_sz);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+ goto fail;
+ }
+
+ /* Need to access p7 members directly */
+ for (i = 0; i < MAX_PKCS7_CERTS; i++) {
+ if (p7->certSz[i] == 0)
+ continue;
+ /* Not using wpabuf_put() here so that wpabuf_overflow() isn't
+ * called in case of a size mismatch. wc_DerToPem() checks if
+ * the output is large enough internally. */
+ err = wc_DerToPem(p7->cert[i], p7->certSz[i],
+ wpabuf_mhead_u8(ret) + wpabuf_len(ret),
+ wpabuf_tailroom(ret),
+ CERT_TYPE);
+ if (err > 0) {
+ wpabuf_put(ret, err);
+ } else {
+ LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+ wpabuf_free(ret);
+ ret = NULL;
+ goto fail;
+ }
+ }
+
+fail:
+ if (p7)
+ wc_PKCS7_Free(p7);
+ return ret;
+}
+
+
+/* BEGIN Certificate Signing Request (CSR) APIs */
+
+enum cert_type {
+ cert_type_none = 0,
+ cert_type_decoded_cert,
+ cert_type_cert,
+};
+
+struct crypto_csr {
+ union {
+ /* For parsed csr should be read-only for higher levels */
+ DecodedCert dc;
+ Cert c; /* For generating a csr */
+ } req;
+ enum cert_type type;
+ struct crypto_ec_key *pubkey;
+};
+
+
+/* Helper function to make sure that the correct type is initialized */
+static void crypto_csr_init_type(struct crypto_csr *csr, enum cert_type type,
+ const byte *source, word32 in_sz)
+{
+ int err;
+
+ if (csr->type == type)
+ return; /* Already correct type */
+
+ switch (csr->type) {
+ case cert_type_decoded_cert:
+ wc_FreeDecodedCert(&csr->req.dc);
+ break;
+ case cert_type_cert:
+#ifdef WOLFSSL_CERT_GEN_CACHE
+ wc_SetCert_Free(&csr->req.c);
+#endif /* WOLFSSL_CERT_GEN_CACHE */
+ break;
+ case cert_type_none:
+ break;
+ }
+
+ switch (type) {
+ case cert_type_decoded_cert:
+ wc_InitDecodedCert(&csr->req.dc, source, in_sz, NULL);
+ break;
+ case cert_type_cert:
+ err = wc_InitCert(&csr->req.c);
+ if (err != 0)
+ LOG_WOLF_ERROR_FUNC(wc_InitCert, err);
+ break;
+ case cert_type_none:
+ break;
+ }
+
+ csr->type = type;
+}
+
+
+struct crypto_csr * crypto_csr_init(void)
+{
+ struct crypto_csr *ret = os_malloc(sizeof(struct crypto_csr));
+
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+ return NULL;
+ }
+
+ ret->type = cert_type_none;
+ crypto_csr_init_type(ret, cert_type_cert, NULL, 0);
+ ret->pubkey = NULL;
+
+ return ret;
+}
+
+
+void crypto_csr_deinit(struct crypto_csr *csr)
+{
+ if (csr) {
+ crypto_csr_init_type(csr, cert_type_none, NULL, 0);
+ crypto_ec_key_deinit(csr->pubkey);
+ os_free(csr);
+ }
+}
+
+
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
+ struct crypto_ec_key *key)
+{
+ struct wpabuf *der = NULL;
+
+ if (!csr || !key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return -1;
+ }
+
+ if (csr->pubkey) {
+ crypto_ec_key_deinit(csr->pubkey);
+ csr->pubkey = NULL;
+ }
+
+ /* Create copy of key to mitigate use-after-free errors */
+ der = crypto_ec_key_get_subject_public_key(key);
+ if (!der) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_get_subject_public_key);
+ return -1;
+ }
+
+ csr->pubkey = crypto_ec_key_parse_pub(wpabuf_head(der),
+ wpabuf_len(der));
+ wpabuf_free(der);
+ if (!csr->pubkey) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_parse_pub);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+ const char *name)
+{
+ int name_len;
+ char *dest;
+
+ if (!csr || !name) {
+ LOG_INVALID_PARAMETERS();
+ return -1;
+ }
+
+ if (csr->type != cert_type_cert) {
+ LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+ return -1;
+ }
+
+ name_len = os_strlen(name);
+ if (name_len >= CTC_NAME_SIZE) {
+ LOG_WOLF_ERROR("name input too long");
+ return -1;
+ }
+
+ switch (type) {
+ case CSR_NAME_CN:
+ dest = csr->req.c.subject.commonName;
+ break;
+ case CSR_NAME_SN:
+ dest = csr->req.c.subject.sur;
+ break;
+ case CSR_NAME_C:
+ dest = csr->req.c.subject.country;
+ break;
+ case CSR_NAME_O:
+ dest = csr->req.c.subject.org;
+ break;
+ case CSR_NAME_OU:
+ dest = csr->req.c.subject.unit;
+ break;
+ default:
+ LOG_INVALID_PARAMETERS();
+ return -1;
+ }
+
+ os_memcpy(dest, name, name_len);
+ dest[name_len] = '\0';
+
+ return 0;
+}
+
+
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+ int attr_type, const u8 *value, size_t len)
+{
+ if (!csr || attr_type != ASN1_TAG_UTF8STRING || !value ||
+ len >= CTC_NAME_SIZE) {
+ LOG_INVALID_PARAMETERS();
+ return -1;
+ }
+
+ if (csr->type != cert_type_cert) {
+ LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+ return -1;
+ }
+
+ switch (attr) {
+ case CSR_ATTR_CHALLENGE_PASSWORD:
+ os_memcpy(csr->req.c.challengePw, value, len);
+ csr->req.c.challengePw[len] = '\0';
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+ enum crypto_csr_attr attr,
+ size_t *len, int *type)
+{
+ if (!csr || !len || !type) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;;
+ }
+
+ switch (attr) {
+ case CSR_ATTR_CHALLENGE_PASSWORD:
+ switch (csr->type) {
+ case cert_type_decoded_cert:
+ *type = ASN1_TAG_UTF8STRING;
+ *len = csr->req.dc.cPwdLen;
+ return (const u8 *) csr->req.dc.cPwd;
+ case cert_type_cert:
+ *type = ASN1_TAG_UTF8STRING;
+ *len = os_strlen(csr->req.c.challengePw);
+ return (const u8 *) csr->req.c.challengePw;
+ case cert_type_none:
+ return NULL;
+ }
+ break;
+ }
+ return NULL;
+}
+
+
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+ struct crypto_ec_key *key,
+ enum crypto_hash_alg algo)
+{
+ int err;
+ int len;
+ u8 *buf = NULL;
+ int buf_len;
+ struct wpabuf *ret = NULL;
+
+ if (!csr || !key || !key->eckey) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ if (csr->type != cert_type_cert) {
+ LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+ return NULL;
+ }
+
+ if (!crypto_ec_key_init_rng(key)) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+ return NULL;
+ }
+
+ switch (algo) {
+ case CRYPTO_HASH_ALG_SHA256:
+ csr->req.c.sigType = CTC_SHA256wECDSA;
+ break;
+ case CRYPTO_HASH_ALG_SHA384:
+ csr->req.c.sigType = CTC_SHA384wECDSA;
+ break;
+ case CRYPTO_HASH_ALG_SHA512:
+ csr->req.c.sigType = CTC_SHA512wECDSA;
+ break;
+ default:
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ /* Pass in large value that is guaranteed to be larger than the
+ * necessary buffer */
+ err = wc_MakeCertReq(&csr->req.c, NULL, 100000, NULL,
+ csr->pubkey->eckey);
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_MakeCertReq, err);
+ goto fail;
+ }
+ len = err;
+
+ buf_len = len + MAX_SEQ_SZ * 2 + MAX_ENCODED_SIG_SZ;
+ buf = os_malloc(buf_len);
+ if (!buf) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+ goto fail;
+ }
+
+ err = wc_MakeCertReq(&csr->req.c, buf, buf_len, NULL,
+ csr->pubkey->eckey);
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_MakeCertReq, err);
+ goto fail;
+ }
+ len = err;
+
+ err = wc_SignCert(len, csr->req.c.sigType, buf, buf_len, NULL,
+ key->eckey, key->rng);
+ if (err <= 0) {
+ LOG_WOLF_ERROR_FUNC(wc_SignCert, err);
+ goto fail;
+ }
+ len = err;
+
+ ret = wpabuf_alloc_copy(buf, len);
+ if (!ret) {
+ LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc_copy);
+ goto fail;
+ }
+
+fail:
+ os_free(buf);
+ return ret;
+}
+
+
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
+{
+ struct crypto_csr *csr = NULL;
+ int err;
+
+ if (!req) {
+ LOG_INVALID_PARAMETERS();
+ return NULL;
+ }
+
+ csr = crypto_csr_init();
+ if (!csr) {
+ LOG_WOLF_ERROR_FUNC_NULL(crypto_csr_init);
+ goto fail;
+ }
+
+ crypto_csr_init_type(csr, cert_type_decoded_cert,
+ wpabuf_head(req), wpabuf_len(req));
+ err = wc_ParseCert(&csr->req.dc, CERTREQ_TYPE, VERIFY, NULL);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_ParseCert, err);
+ goto fail;
+ }
+
+ return csr;
+fail:
+ crypto_csr_deinit(csr);
+ return NULL;
+}
+
+/* END Certificate Signing Request (CSR) APIs */
+
+#endif /* CONFIG_DPP */
+
void crypto_unload(void)
{
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 1ad1068..1e8a122 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -28,11 +28,11 @@
{
unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
unsigned char tk[32];
- const u8 *_addr[11];
- size_t _len[11], i;
+ const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+ size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
int ret;
- if (num_elem > 10) {
+ if (num_elem > HMAC_VECTOR_MAX_ELEM) {
/*
* Fixed limit on the number of fragments to avoid having to
* allocate memory (which could fail).
diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c
index fd84b82..be07e9c 100644
--- a/src/crypto/sha384.c
+++ b/src/crypto/sha384.c
@@ -28,10 +28,10 @@
{
unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
unsigned char tk[48];
- const u8 *_addr[11];
- size_t _len[11], i;
+ const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+ size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
- if (num_elem > 10) {
+ if (num_elem > HMAC_VECTOR_MAX_ELEM) {
/*
* Fixed limit on the number of fragments to avoid having to
* allocate memory (which could fail).
diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c
index f60a576..73b54c7 100644
--- a/src/crypto/sha512.c
+++ b/src/crypto/sha512.c
@@ -28,10 +28,10 @@
{
unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
unsigned char tk[64];
- const u8 *_addr[11];
- size_t _len[11], i;
+ const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+ size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
- if (num_elem > 10) {
+ if (num_elem > HMAC_VECTOR_MAX_ELEM) {
/*
* Fixed limit on the number of fragments to avoid having to
* allocate memory (which could fail).
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 82276c5..f43aefe 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -80,9 +80,15 @@
};
struct tls_config {
+#ifndef CONFIG_OPENSC_ENGINE_PATH
const char *opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
const char *pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
const char *pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
int fips_mode;
int cert_in_cb;
const char *openssl_ciphers;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index b378356..103c333 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -1029,6 +1029,26 @@
SSL_CTX *ssl;
struct tls_context *context;
const char *ciphers;
+#ifndef OPENSSL_NO_ENGINE
+#ifdef CONFIG_OPENSC_ENGINE_PATH
+ char const * const opensc_engine_path = CONFIG_OPENSC_ENGINE_PATH;
+#else /* CONFIG_OPENSC_ENGINE_PATH */
+ char const * const opensc_engine_path =
+ conf ? conf->opensc_engine_path : NULL;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifdef CONFIG_PKCS11_ENGINE_PATH
+ char const * const pkcs11_engine_path = CONFIG_PKCS11_ENGINE_PATH;
+#else /* CONFIG_PKCS11_ENGINE_PATH */
+ char const * const pkcs11_engine_path =
+ conf ? conf->pkcs11_engine_path : NULL;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifdef CONFIG_PKCS11_MODULE_PATH
+ char const * const pkcs11_module_path = CONFIG_PKCS11_MODULE_PATH;
+#else /* CONFIG_PKCS11_MODULE_PATH */
+ char const * const pkcs11_module_path =
+ conf ? conf->pkcs11_module_path : NULL;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
+#endif /* OPENSSL_NO_ENGINE */
if (tls_openssl_ref_count == 0) {
void openssl_load_legacy_provider(void);
@@ -1171,12 +1191,10 @@
wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines");
ENGINE_load_builtin_engines();
- if (conf &&
- (conf->opensc_engine_path || conf->pkcs11_engine_path ||
- conf->pkcs11_module_path)) {
- if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
- tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
- conf->pkcs11_module_path)) {
+ if (opensc_engine_path || pkcs11_engine_path || pkcs11_module_path) {
+ if (tls_engine_load_dynamic_opensc(opensc_engine_path) ||
+ tls_engine_load_dynamic_pkcs11(pkcs11_engine_path,
+ pkcs11_module_path)) {
tls_deinit(data);
return NULL;
}
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index b4f1bbe..0b2947d 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -284,6 +284,7 @@
ciphers = conf->openssl_ciphers;
else
ciphers = "ALL";
+ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers);
if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
wpa_printf(MSG_ERROR,
"wolfSSL: Failed to set cipher string '%s'",
@@ -1323,6 +1324,8 @@
return -1;
}
+ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
+ params->openssl_ciphers ? params->openssl_ciphers : "N/A");
if (params->openssl_ciphers &&
wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
wpa_printf(MSG_INFO,
@@ -1553,6 +1556,8 @@
return -1;
}
+ wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
+ params->openssl_ciphers ? params->openssl_ciphers : "N/A");
if (params->openssl_ciphers &&
wolfSSL_CTX_set_cipher_list(tls_ctx,
params->openssl_ciphers) != 1) {
@@ -1665,20 +1670,31 @@
wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
}
- if (res != 1) {
+ if (res != WOLFSSL_SUCCESS) {
int err = wolfSSL_get_error(conn->ssl, res);
- if (err == SSL_ERROR_WANT_READ) {
+ if (err == WOLFSSL_ERROR_NONE) {
wpa_printf(MSG_DEBUG,
- "SSL: wolfSSL_connect - want more data");
- } else if (err == SSL_ERROR_WANT_WRITE) {
+ "SSL: %s - WOLFSSL_ERROR_NONE (%d)",
+ server ? "wolfSSL_accept" :
+ "wolfSSL_connect", res);
+ } else if (err == WOLFSSL_ERROR_WANT_READ) {
wpa_printf(MSG_DEBUG,
- "SSL: wolfSSL_connect - want to write");
+ "SSL: %s - want more data",
+ server ? "wolfSSL_accept" :
+ "wolfSSL_connect");
+ } else if (err == WOLFSSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG,
+ "SSL: %s - want to write",
+ server ? "wolfSSL_accept" :
+ "wolfSSL_connect");
} else {
char msg[80];
wpa_printf(MSG_DEBUG,
- "SSL: wolfSSL_connect - failed %s",
+ "SSL: %s - failed %s",
+ server ? "wolfSSL_accept" :
+ "wolfSSL_connect",
wolfSSL_ERR_error_string(err, msg));
conn->failed++;
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 3c4de7a..7ae7d90 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -334,6 +334,8 @@
* @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
* @bssid: BSSID
* @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @max_cw: the max channel width of the connection (calculated during scan
+ * result processing)
* @beacon_int: beacon interval in TUs (host byte order)
* @caps: capability information field in host byte order
* @qual: signal quality
@@ -370,6 +372,7 @@
unsigned int flags;
u8 bssid[ETH_ALEN];
int freq;
+ enum chan_width max_cw;
u16 beacon_int;
u16 caps;
int qual;
@@ -685,6 +688,13 @@
*/
unsigned int non_coloc_6ghz:1;
+ /**
+ * min_probe_req_content - Minimize probe request content to only have
+ * minimal requirement elements, e.g., supported rates etc., and no
+ * additional elements other then those provided by user space.
+ */
+ unsigned int min_probe_req_content:1;
+
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -841,6 +851,11 @@
* eht_enabled - Whether EHT is enabled
*/
bool eht_enabled;
+
+ /**
+ * link_id: If >=0 indicates the link of the AP MLD to configure
+ */
+ int link_id;
};
/**
@@ -1117,6 +1132,23 @@
const u8 *psk;
/**
+ * sae_password - Password for SAE authentication
+ *
+ * This value is made available only for WPA3-Personal (SAE) and only
+ * for drivers that set WPA_DRIVER_FLAGS2_SAE_OFFLOAD.
+ */
+ const char *sae_password;
+
+ /**
+ * sae_password_id - Password Identifier for SAE authentication
+ *
+ * This value is made available only for WPA3-Personal (SAE) and only
+ * for drivers that set WPA_DRIVER_FLAGS2_SAE_OFFLOAD. If %NULL, SAE
+ * password identifier is not used.
+ */
+ const char *sae_password_id;
+
+ /**
* drop_unencrypted - Enable/disable unencrypted frame filtering
*
* Configure the driver to drop all non-EAPOL frames (both receive and
@@ -1777,6 +1809,31 @@
* channels whenever performing operations like ACS and DFS.
*/
int *allowed_freqs;
+
+ /*
+ * mld_ap - Whether operating as an AP MLD
+ */
+ bool mld_ap;
+
+ /**
+ * mld_link_id - Link id for MLD BSS's
+ */
+ u8 mld_link_id;
+
+ /**
+ * psk - PSK passed to the driver for 4-way handshake offload
+ */
+ u8 psk[PMK_LEN];
+
+ /**
+ * psk_len - PSK length in bytes (0 = not set)
+ */
+ size_t psk_len;
+
+ /**
+ * sae_password - SAE password for SAE offload
+ */
+ const char *sae_password;
};
struct wpa_driver_mesh_bss_params {
@@ -2233,7 +2290,7 @@
/** Driver handles SA Query procedures in AP mode */
#define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP 0x0000000000000200ULL
/** Driver supports background radar/CAC detection */
-#define WPA_DRIVER_RADAR_BACKGROUND 0x0000000000000400ULL
+#define WPA_DRIVER_FLAGS2_RADAR_BACKGROUND 0x0000000000000400ULL
/** Driver supports secure LTF in STA mode */
#define WPA_DRIVER_FLAGS2_SEC_LTF_STA 0x0000000000000800ULL
/** Driver supports secure RTT measurement exchange in STA mode */
@@ -2245,6 +2302,18 @@
#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA 0x0000000000002000ULL
/** Driver supports MLO in station/AP mode */
#define WPA_DRIVER_FLAGS2_MLO 0x0000000000004000ULL
+/** Driver supports minimal scan request probe content */
+#define WPA_DRIVER_FLAGS2_SCAN_MIN_PREQ 0x0000000000008000ULL
+/** Driver supports SAE authentication offload in STA mode */
+#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA 0x0000000000010000ULL
+/** Driver support AP_PSK authentication offload */
+#define WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK 0x0000000000020000ULL
+/** Driver supports OWE STA offload */
+#define WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA 0x0000000000040000ULL
+/** Driver supports OWE AP offload */
+#define WPA_DRIVER_FLAGS2_OWE_OFFLOAD_AP 0x0000000000080000ULL
+/** Driver support AP SAE authentication offload */
+#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP 0x0000000000100000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -3268,12 +3337,13 @@
* @no_encrypt: Do not encrypt frame even if appropriate key exists
* (used only for testing purposes)
* @wait: Time to wait off-channel for a response (in ms), or zero
+ * @link_id: Link ID to use for TX, or -1 if not set
* Returns: 0 on success, -1 on failure
*/
int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
int noack, unsigned int freq, const u16 *csa_offs,
size_t csa_offs_len, int no_encrypt,
- unsigned int wait);
+ unsigned int wait, int link_id);
/**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -3479,6 +3549,7 @@
* @priv: Private driver interface data
* @addr: MAC address of the station or %NULL for group keys
* @idx: Key index
+ * @link_id: Link ID for a group key, or -1 if not set
* @seq: Buffer for returning the latest used TSC/packet number
* Returns: 0 on success, -1 on failure
*
@@ -3488,7 +3559,7 @@
* unicast keys (i.e., addr != %NULL).
*/
int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
- int idx, u8 *seq);
+ int idx, int link_id, u8 *seq);
/**
* flush - Flush all association stations (AP only)
@@ -3535,6 +3606,7 @@
* @buf: Frame payload starting from IEEE 802.1X header
* @len: Frame payload length
* @no_encrypt: Do not encrypt frame
+ * @link_id: Link ID to use for TX, or -1 if not set
*
* Returns 0 on success, else an error
*
@@ -3552,7 +3624,7 @@
*/
int (*tx_control_port)(void *priv, const u8 *dest,
u16 proto, const u8 *buf, size_t len,
- int no_encrypt);
+ int no_encrypt, int link_id);
/**
* hapd_send_eapol - Send an EAPOL packet (AP only)
@@ -3563,26 +3635,28 @@
* @encrypt: Whether the frame should be encrypted
* @own_addr: Source MAC address
* @flags: WPA_STA_* flags for the destination station
+ * @link_id: Link ID to use for TX, or -1 if not set
*
* Returns: 0 on success, -1 on failure
*/
int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr, u32 flags);
+ const u8 *own_addr, u32 flags, int link_id);
/**
* sta_deauth - Deauthenticate a station (AP only)
* @priv: Private driver interface data
* @own_addr: Source address and BSSID for the Deauthentication frame
* @addr: MAC address of the station to deauthenticate
- * @reason: Reason code for the Deauthentiation frame
+ * @reason: Reason code for the Deauthentication frame
+ * @link_id: Link ID to use for Deauthentication frame, or -1 if not set
* Returns: 0 on success, -1 on failure
*
* This function requests a specific station to be deauthenticated and
* a Deauthentication frame to be sent to it.
*/
int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason);
+ u16 reason, int link_id);
/**
* sta_disassoc - Disassociate a station (AP only)
@@ -3731,9 +3805,10 @@
* @cw_min: cwMin
* @cw_max: cwMax
* @burst_time: Maximum length for bursting in 0.1 msec units
+ * @link_id: Link ID to use, or -1 for non MLD.
*/
int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
- int cw_max, int burst_time);
+ int cw_max, int burst_time, int link_id);
/**
* if_add - Add a virtual interface
@@ -3776,6 +3851,7 @@
* @ifname: Interface (main or virtual BSS or VLAN)
* @addr: MAC address of the associated station
* @vlan_id: VLAN ID
+ * @link_id: The link ID or -1 for non-MLO
* Returns: 0 on success, -1 on failure
*
* This function is used to bind a station to a specific virtual
@@ -3785,7 +3861,7 @@
* domains to be used with a single BSS.
*/
int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
- int vlan_id);
+ int vlan_id, int link_id);
/**
* commit - Optional commit changes handler (AP only)
@@ -4091,6 +4167,8 @@
* @initiator: Is the current end the TDLS link initiator
* @buf: TDLS IEs to add to the message
* @len: Length of buf in octets
+ * @link_id: If >= 0 indicates the link of the AP MLD to specify the
+ * operating channel on which to send a TDLS Discovery Response frame.
* Returns: 0 on success, negative (<0) on failure
*
* This optional function can be used to send packet to driver which is
@@ -4098,7 +4176,8 @@
*/
int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code, u32 peer_capab,
- int initiator, const u8 *buf, size_t len);
+ int initiator, const u8 *buf, size_t len,
+ int link_id);
/**
* tdls_oper - Ask the driver to perform high-level TDLS operations
@@ -4865,6 +4944,17 @@
unsigned int *ext_capab_len);
/**
+ * get_mld_capab - Get MLD capabilities for the specified interface
+ * @priv: Private driver interface data
+ * @type: Interface type for which to get MLD capabilities
+ * @eml_capa: EML capabilities
+ * @mld_capa_and_ops: MLD Capabilities and Operations
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*get_mld_capab)(void *priv, enum wpa_driver_if_type type,
+ u16 *eml_capa, u16 *mld_capa_and_ops);
+
+ /**
* p2p_lo_start - Start offloading P2P listen to device
* @priv: Private driver interface data
* @freq: Listening frequency (MHz) for P2P listen
@@ -5930,6 +6020,17 @@
* fils_pmkid - PMKID used or generated in FILS authentication
*/
const u8 *fils_pmkid;
+
+ /**
+ * link_addr - Link address of the STA
+ */
+ const u8 *link_addr;
+
+ /**
+ * assoc_link_id - Association link id of the MLO association or
+ * -1 if MLO is not used
+ */
+ int assoc_link_id;
} assoc_info;
/**
@@ -6165,6 +6266,7 @@
const u8 *data;
size_t data_len;
int ack;
+ int link_id;
} tx_status;
/**
@@ -6382,6 +6484,7 @@
* @data: Data starting with IEEE 802.1X header (!)
* @data_len: Length of data
* @ack: Indicates ack or lost frame
+ * @link_id: MLD link id used to transmit the frame or -1 for non MLO
*
* This corresponds to hapd_send_eapol if the frame sent
* there isn't just reported as EVENT_TX_STATUS.
@@ -6391,6 +6494,7 @@
const u8 *data;
int data_len;
int ack;
+ int link_id;
} eapol_tx_status;
/**
@@ -6514,6 +6618,7 @@
u8 vht_seg1_center_ch;
u16 ch_width;
enum hostapd_hw_mode hw_mode;
+ u16 puncture_bitmap;
} acs_selected_channels;
/**
@@ -6575,6 +6680,8 @@
const u8 *peer;
const u8 *ie;
size_t ie_len;
+ int assoc_link_id;
+ const u8 *link_addr;
} update_dh;
/**
@@ -6598,10 +6705,19 @@
/**
* struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+ * @td_bitmap: For STA mode, transition disable bitmap, if received in
+ * EAPOL-Key msg 3/4
+ * @td_bitmap_len: For STA mode, length of td_bitmap
+ * @sta_addr: For AP mode, the MAC address of the associated STA
+ *
+ * This event is used to indicate that the port is authorized and
+ * open for normal data in STA and AP modes when 4-way handshake is
+ * offloaded to the driver.
*/
struct port_authorized {
const u8 *td_bitmap;
size_t td_bitmap_len;
+ const u8 *sta_addr;
} port_authorized;
/**
@@ -6645,15 +6761,21 @@
* event indication for some of the common events.
*/
-static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
- size_t ielen, int reassoc)
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *req_ies,
+ size_t req_ielen, const u8 *resp_ies,
+ size_t resp_ielen, const u8 *link_addr,
+ int assoc_link_id, int reassoc)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
event.assoc_info.reassoc = reassoc;
- event.assoc_info.req_ies = ie;
- event.assoc_info.req_ies_len = ielen;
+ event.assoc_info.req_ies = req_ies;
+ event.assoc_info.req_ies_len = req_ielen;
+ event.assoc_info.resp_ies = resp_ies;
+ event.assoc_info.resp_ies_len = resp_ielen;
event.assoc_info.addr = addr;
+ event.assoc_info.link_addr = link_addr;
+ event.assoc_info.assoc_link_id = assoc_link_id;
wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
}
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index cb66dfa..59f65b8 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -81,7 +81,7 @@
};
static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code);
+ u16 reason_code, int link_id);
static int atheros_set_privacy(void *priv, int enabled);
static const char * athr_get_ioctl_name(int op)
@@ -586,7 +586,7 @@
static int
atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- u8 *seq)
+ int link_id, u8 *seq)
{
struct atheros_driver_data *drv = priv;
struct ieee80211req_key wk;
@@ -637,7 +637,7 @@
u8 allsta[IEEE80211_ADDR_LEN];
os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
return atheros_sta_deauth(priv, NULL, allsta,
- IEEE80211_REASON_AUTH_LEAVE);
+ IEEE80211_REASON_AUTH_LEAVE, -1);
}
@@ -756,7 +756,7 @@
static int
atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code)
+ u16 reason_code, int link_id)
{
struct atheros_driver_data *drv = priv;
struct ieee80211req_mlme mlme;
@@ -913,14 +913,16 @@
break;
ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
iebuf = mgmt->u.assoc_req.variable;
- drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+ drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
+ NULL, -1, 0);
break;
case WLAN_FC_STYPE_REASSOC_REQ:
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
break;
ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
iebuf = mgmt->u.reassoc_req.variable;
- drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+ drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
+ NULL, -1, 1);
break;
case WLAN_FC_STYPE_AUTH:
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
@@ -1222,7 +1224,7 @@
ielen += 2;
no_ie:
- drv_event_assoc(hapd, addr, iebuf, ielen, 0);
+ drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
/* Cached accounting data is not valid anymore. */
@@ -1644,7 +1646,7 @@
static int
atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr, u32 flags)
+ int encrypt, const u8 *own_addr, u32 flags, int link_id)
{
struct atheros_driver_data *drv = priv;
unsigned char buf[3000];
@@ -1964,7 +1966,7 @@
static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
int noack, unsigned int freq,
const u16 *csa_offs, size_t csa_offs_len,
- int no_encrypt, unsigned int wait)
+ int no_encrypt, unsigned int wait, int link_id)
{
struct atheros_driver_data *drv = priv;
u8 buf[1510];
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 00d970a..850637f 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -553,12 +553,12 @@
ielen += 2;
no_ie:
- drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+ drv_event_assoc(ctx, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
}
static int
bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr, u32 flags)
+ int encrypt, const u8 *own_addr, u32 flags, int link_id)
{
struct bsd_driver_data *drv = priv;
@@ -882,7 +882,7 @@
#undef WPA_OUI_TYPE
static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code);
+ u16 reason_code, int link_id);
static const char *
ether_sprintf(const u8 *addr)
@@ -906,7 +906,7 @@
static int
bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- u8 *seq)
+ int link_id, u8 *seq)
{
struct ieee80211req_key wk;
@@ -951,7 +951,8 @@
u8 allsta[IEEE80211_ADDR_LEN];
memset(allsta, 0xff, IEEE80211_ADDR_LEN);
- return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
+ return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE,
+ -1);
}
@@ -974,7 +975,8 @@
}
static int
-bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code)
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code,
+ int link_id)
{
return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
addr);
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index f3625e8..9bc5a73 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -358,6 +358,21 @@
switch (flag2) {
DF2S(CONTROL_PORT_RX);
DF2S(CONTROL_PORT_TX_STATUS);
+ DF2S(SEC_LTF_AP);
+ DF2S(SEC_RTT_AP);
+ DF2S(PROT_RANGE_NEG_AP);
+ DF2S(BEACON_RATE_HE);
+ DF2S(BEACON_PROTECTION_CLIENT);
+ DF2S(OCV);
+ DF2S(AP_SME);
+ DF2S(SA_QUERY_OFFLOAD_AP);
+ DF2S(RADAR_BACKGROUND);
+ DF2S(SEC_LTF_STA);
+ DF2S(SEC_RTT_STA);
+ DF2S(PROT_RANGE_NEG_STA);
+ DF2S(MLO);
+ DF2S(SCAN_MIN_PREQ);
+ DF2S(SAE_OFFLOAD_STA);
}
return "UNKNOWN";
#undef DF2S
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index b9c42e4..d3520aa 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -264,7 +264,7 @@
static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
unsigned int freq,
const u16 *csa_offs, size_t csa_offs_len,
- int no_encrypt, unsigned int wait)
+ int no_encrypt, unsigned int wait, int link_id)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -281,7 +281,7 @@
static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt, const u8 *own_addr,
- u32 flags)
+ u32 flags, int link_id)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_hdr *hdr;
@@ -313,7 +313,7 @@
pos += 2;
memcpy(pos, data, data_len);
- res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0, 0, 0);
+ res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0, 0, 0, -1);
if (res < 0) {
wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
"failed: %d (%s)",
@@ -459,7 +459,7 @@
static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
- int idx, u8 *seq)
+ int idx, int link_id, u8 *seq)
{
struct hostap_driver_data *drv = priv;
struct prism2_hostapd_param *param;
@@ -1032,7 +1032,7 @@
static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason)
+ u16 reason, int link_id)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_mgmt mgmt;
@@ -1055,7 +1055,7 @@
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.deauth.reason_code = host_to_le16(reason);
return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth), 0, 0, NULL, 0, 0, 0);
+ sizeof(mgmt.u.deauth), 0, 0, NULL, 0, 0, 0, -1);
}
@@ -1093,7 +1093,8 @@
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.disassoc.reason_code = host_to_le16(reason);
return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.disassoc), 0, 0, NULL, 0, 0, 0);
+ sizeof(mgmt.u.disassoc), 0, 0, NULL, 0, 0, 0,
+ -1);
}
@@ -1173,7 +1174,8 @@
os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
- hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0);
+ hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0,
+ -1);
}
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index c79e873..c867154 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -1640,7 +1640,7 @@
static int macsec_drv_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
- const u8 *own_addr, u32 flags)
+ const u8 *own_addr, u32 flags, int link_id)
{
struct macsec_drv_data *drv = priv;
struct ieee8023_hdr *hdr;
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index eccaf63..58c48c2 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -366,7 +366,7 @@
static int macsec_qca_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
- const u8 *own_addr, u32 flags)
+ const u8 *own_addr, u32 flags, int link_id)
{
struct macsec_qca_data *drv = priv;
struct ieee8023_hdr *hdr;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 1acc43b..fede740 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -175,8 +175,6 @@
const u16 *csa_offs, size_t csa_offs_len);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
-static int nl80211_put_freq_params(struct nl_msg *msg,
- const struct hostapd_freq_params *freq);
#define IFIDX_ANY -1
@@ -3160,9 +3158,9 @@
os_free(drv->extended_capa);
os_free(drv->extended_capa_mask);
- for (i = 0; i < drv->num_iface_ext_capa; i++) {
- os_free(drv->iface_ext_capa[i].ext_capa);
- os_free(drv->iface_ext_capa[i].ext_capa_mask);
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ os_free(drv->iface_capa[i].ext_capa);
+ os_free(drv->iface_capa[i].ext_capa_mask);
}
os_free(drv->first_bss);
#ifdef CONFIG_DRIVER_NL80211_QCA
@@ -3292,6 +3290,7 @@
__AKM(OWE, OWE);
__AKM(DPP, DPP);
__AKM(FT_IEEE8021X_SHA384, FT_802_1X_SHA384);
+ __AKM(IEEE8021X_SHA384, 802_1X_SHA384);
#undef __AKM
return num_suites;
@@ -4233,13 +4232,60 @@
}
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (bss->links[i].link_id != link_id)
+ continue;
+
+ return &bss->links[i];
+ }
+
+ return bss->flink;
+}
+
+
+static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq)
+{
+ struct i802_link *link = nl80211_get_link(bss, link_id);
+
+ link->freq = freq;
+}
+
+
+static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
+ bool bss_freq_debug)
+{
+ size_t i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (os_memcmp(bss->links[i].addr, addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Use link freq=%d for address "
+ MACSTR,
+ bss->links[i].freq, MAC2STR(addr));
+ return bss->links[i].freq;
+ }
+ }
+
+ if (bss_freq_debug)
+ wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
+ bss->flink->freq);
+
+ return bss->flink->freq;
+}
+
+
static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
size_t data_len, int noack,
unsigned int freq, int no_cck,
int offchanok,
unsigned int wait_time,
const u16 *csa_offs,
- size_t csa_offs_len, int no_encrypt)
+ size_t csa_offs_len, int no_encrypt,
+ int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
@@ -4247,6 +4293,7 @@
u16 fc;
int use_cookie = 1;
int res;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
@@ -4277,13 +4324,15 @@
}
if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
- if (freq == 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
- bss->flink->freq);
- freq = bss->flink->freq;
- }
- if ((int) freq == bss->flink->freq)
+ unsigned int link_freq = nl80211_get_link_freq(bss, mgmt->sa,
+ !freq);
+
+ if (!freq)
+ freq = link_freq;
+
+ if (freq == link_freq)
wait_time = 0;
+
goto send_frame_cmd;
}
@@ -4344,20 +4393,21 @@
}
if (freq == 0) {
wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
- bss->flink->freq);
- freq = bss->flink->freq;
+ link->freq);
+ freq = link->freq;
}
if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
wpa_printf(MSG_DEBUG,
"nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
- freq, bss->flink->freq);
+ freq, link->freq);
return nl80211_send_monitor(drv, data, data_len, encrypt,
noack);
}
- if (noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
- WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+ if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) &&
+ link_id == NL80211_DRV_LINK_ID_NA)
use_cookie = 0;
send_frame_cmd:
#ifdef CONFIG_TESTING_OPTIONS
@@ -4377,6 +4427,8 @@
res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
use_cookie, no_cck, noack, offchanok,
csa_offs, csa_offs_len);
+ if (!res)
+ drv->send_frame_link_id = link_id;
return res;
}
@@ -4593,7 +4645,7 @@
}
if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
- (u8) params->beacon_rate / 5) ||
+ (u8) (params->beacon_rate / 5)) ||
nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
(params->freq->vht_enabled &&
nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
@@ -4916,11 +4968,118 @@
#endif /* CONFIG_DRIVER_NL80211_QCA */
+static int nl80211_put_freq_params(struct nl_msg *msg,
+ const struct hostapd_freq_params *freq)
+{
+ enum hostapd_hw_mode hw_mode;
+ int is_24ghz;
+ u8 channel;
+
+ wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+ return -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, " * eht_enabled=%d", freq->eht_enabled);
+ wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
+ wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
+ wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
+ wpa_printf(MSG_DEBUG, " * radar_background=%d",
+ freq->radar_background);
+
+ hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B;
+
+ if (freq->vht_enabled ||
+ ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
+ enum nl80211_chan_width cw;
+
+ wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth);
+ switch (freq->bandwidth) {
+ case 20:
+ cw = NL80211_CHAN_WIDTH_20;
+ break;
+ case 40:
+ cw = NL80211_CHAN_WIDTH_40;
+ break;
+ case 80:
+ if (freq->center_freq2)
+ cw = NL80211_CHAN_WIDTH_80P80;
+ else
+ cw = NL80211_CHAN_WIDTH_80;
+ break;
+ case 160:
+ cw = NL80211_CHAN_WIDTH_160;
+ break;
+ case 320:
+ cw = NL80211_CHAN_WIDTH_320;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wpa_printf(MSG_DEBUG, " * channel_width=%d", cw);
+ wpa_printf(MSG_DEBUG, " * center_freq1=%d",
+ freq->center_freq1);
+ wpa_printf(MSG_DEBUG, " * center_freq2=%d",
+ freq->center_freq2);
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+ freq->center_freq1) ||
+ (freq->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+ freq->center_freq2)))
+ return -ENOBUFS;
+ } else if (freq->ht_enabled) {
+ enum nl80211_channel_type ct;
+
+ wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d",
+ freq->sec_channel_offset);
+ switch (freq->sec_channel_offset) {
+ case -1:
+ ct = NL80211_CHAN_HT40MINUS;
+ break;
+ case 1:
+ ct = NL80211_CHAN_HT40PLUS;
+ break;
+ default:
+ ct = NL80211_CHAN_HT20;
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, " * channel_type=%d", ct);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+ return -ENOBUFS;
+ } else if (freq->edmg.channels && freq->edmg.bw_config) {
+ wpa_printf(MSG_DEBUG,
+ " * EDMG configuration: channels=0x%x bw_config=%d",
+ freq->edmg.channels, freq->edmg.bw_config);
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+ freq->edmg.channels) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+ freq->edmg.bw_config))
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, " * channel_type=%d",
+ NL80211_CHAN_NO_HT);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT))
+ return -ENOBUFS;
+ }
+ if (freq->radar_background &&
+ nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND))
+ return -ENOBUFS;
+
+ return 0;
+}
+
+
static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_ap_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_link *link = bss->flink;
struct nl_msg *msg;
u8 cmd = NL80211_CMD_NEW_BEACON;
int ret = -ENOBUFS;
@@ -4932,7 +5091,24 @@
struct wpa_driver_mesh_bss_params mesh_params;
#endif /* CONFIG_MESH */
- beacon_set = params->reenable ? 0 : bss->flink->beacon_set;
+ if (params->mld_ap) {
+ size_t i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (bss->links[i].link_id == params->mld_link_id) {
+ link = &bss->links[i];
+ break;
+ }
+ }
+
+ if (i == bss->n_links) {
+ wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
+ params->mld_link_id);
+ return -EINVAL;
+ }
+ }
+
+ beacon_set = params->reenable ? 0 : link->beacon_set;
wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
beacon_set);
@@ -4964,6 +5140,21 @@
nl80211_put_dtim_period(msg, params->dtim_period) ||
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
+
+ if (params->mld_ap) {
+ wpa_printf(MSG_DEBUG, "nl80211: link_id=%u",
+ params->mld_link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id) ||
+ (params->freq &&
+ nl80211_put_freq_params(msg, params->freq) < 0))
+ goto fail;
+
+ nl80211_link_set_freq(bss, params->mld_link_id,
+ params->freq->freq);
+ }
+
if (params->proberesp && params->proberesp_len) {
wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
params->proberesp, params->proberesp_len);
@@ -5032,6 +5223,27 @@
suites))
goto fail;
+ if ((params->key_mgmt_suites & WPA_KEY_MGMT_PSK) &&
+ (drv->capa.flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK) &&
+ params->psk_len) {
+ if (nla_put(msg, NL80211_ATTR_PMK, params->psk_len, params->psk)) {
+ wpa_printf(MSG_ERROR, "nl80211: Setting PSK failed");
+ goto fail;
+ } else
+ wpa_printf(MSG_DEBUG, "nl80211: Setting PSK for offload");
+ }
+
+ if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
+ (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP) &&
+ params->sae_password) {
+ if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
+ os_strlen(params->sae_password), params->sae_password)) {
+ wpa_printf(MSG_ERROR, "nl80211: Setting SAE password failed");
+ goto fail;
+ } else
+ wpa_printf(MSG_DEBUG, "nl80211: SAE password for offload");
+ }
+
if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
(!params->pairwise_ciphers ||
params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
@@ -5249,17 +5461,17 @@
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
ret, strerror(-ret));
} else {
- bss->flink->beacon_set = 1;
+ link->beacon_set = 1;
nl80211_set_bss(bss, params->cts_protect, params->preamble,
params->short_slot_time, params->ht_opmode,
params->isolate, params->basic_rates);
nl80211_set_multicast_to_unicast(bss,
params->multicast_to_unicast);
if (beacon_set && params->freq &&
- params->freq->bandwidth != bss->flink->bandwidth) {
+ params->freq->bandwidth != link->bandwidth) {
wpa_printf(MSG_DEBUG,
"nl80211: Update BSS %s bandwidth: %d -> %d",
- bss->ifname, bss->flink->bandwidth,
+ bss->ifname, link->bandwidth,
params->freq->bandwidth);
ret = nl80211_set_channel(bss, params->freq, 1);
if (ret) {
@@ -5269,7 +5481,7 @@
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Frequency set succeeded for ht2040 coex");
- bss->flink->bandwidth = params->freq->bandwidth;
+ link->bandwidth = params->freq->bandwidth;
}
} else if (!beacon_set && params->freq) {
/*
@@ -5277,7 +5489,7 @@
* mode only at the point when beaconing is started, so
* set the initial value here.
*/
- bss->flink->bandwidth = params->freq->bandwidth;
+ link->bandwidth = params->freq->bandwidth;
}
}
@@ -5299,109 +5511,24 @@
}
-static int nl80211_put_freq_params(struct nl_msg *msg,
- const struct hostapd_freq_params *freq)
+static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
{
- enum hostapd_hw_mode hw_mode;
- int is_24ghz;
- u8 channel;
+ unsigned int i;
- wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
- return -ENOBUFS;
+ if (link_id < 0)
+ return false;
- wpa_printf(MSG_DEBUG, " * eht_enabled=%d", freq->eht_enabled);
- wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
- wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
- wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
- wpa_printf(MSG_DEBUG, " * radar_background=%d",
- freq->radar_background);
+ for (i = 0; i < bss->n_links; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: %s - i=%u, link_id=%u",
+ __func__, i, bss->links[i].link_id);
+ if (bss->links[i].link_id == NL80211_DRV_LINK_ID_NA)
+ continue;
- hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
- is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
- hw_mode == HOSTAPD_MODE_IEEE80211B;
-
- if (freq->vht_enabled ||
- ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
- enum nl80211_chan_width cw;
-
- wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth);
- switch (freq->bandwidth) {
- case 20:
- cw = NL80211_CHAN_WIDTH_20;
- break;
- case 40:
- cw = NL80211_CHAN_WIDTH_40;
- break;
- case 80:
- if (freq->center_freq2)
- cw = NL80211_CHAN_WIDTH_80P80;
- else
- cw = NL80211_CHAN_WIDTH_80;
- break;
- case 160:
- cw = NL80211_CHAN_WIDTH_160;
- break;
- case 320:
- cw = NL80211_CHAN_WIDTH_320;
- break;
- default:
- return -EINVAL;
- }
-
- wpa_printf(MSG_DEBUG, " * channel_width=%d", cw);
- wpa_printf(MSG_DEBUG, " * center_freq1=%d",
- freq->center_freq1);
- wpa_printf(MSG_DEBUG, " * center_freq2=%d",
- freq->center_freq2);
- if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
- nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
- freq->center_freq1) ||
- (freq->center_freq2 &&
- nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
- freq->center_freq2)))
- return -ENOBUFS;
- } else if (freq->ht_enabled) {
- enum nl80211_channel_type ct;
-
- wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d",
- freq->sec_channel_offset);
- switch (freq->sec_channel_offset) {
- case -1:
- ct = NL80211_CHAN_HT40MINUS;
- break;
- case 1:
- ct = NL80211_CHAN_HT40PLUS;
- break;
- default:
- ct = NL80211_CHAN_HT20;
- break;
- }
-
- wpa_printf(MSG_DEBUG, " * channel_type=%d", ct);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
- return -ENOBUFS;
- } else if (freq->edmg.channels && freq->edmg.bw_config) {
- wpa_printf(MSG_DEBUG,
- " * EDMG configuration: channels=0x%x bw_config=%d",
- freq->edmg.channels, freq->edmg.bw_config);
- if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
- freq->edmg.channels) ||
- nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
- freq->edmg.bw_config))
- return -1;
- } else {
- wpa_printf(MSG_DEBUG, " * channel_type=%d",
- NL80211_CHAN_NO_HT);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
- NL80211_CHAN_NO_HT))
- return -ENOBUFS;
+ if (bss->links[i].link_id == link_id)
+ return true;
}
- if (freq->radar_background &&
- nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND))
- return -ENOBUFS;
- return 0;
+ return false;
}
@@ -5425,9 +5552,19 @@
return -1;
}
+ if (nl80211_link_valid(bss, freq->link_id)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
+ freq->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0) {
- bss->flink->freq = freq->freq;
+ nl80211_link_set_freq(bss, freq->link_id, freq->freq);
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
@@ -6105,7 +6242,7 @@
static int nl80211_tx_control_port(void *priv, const u8 *dest,
u16 proto, const u8 *buf, size_t len,
- int no_encrypt)
+ int no_encrypt, int link_id)
{
struct nl80211_ack_ext_arg ext_arg;
struct i802_bss *bss = priv;
@@ -6124,7 +6261,9 @@
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dest) ||
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
(no_encrypt &&
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) {
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -6144,6 +6283,7 @@
"nl80211: tx_control_port cookie=0x%llx",
(long long unsigned int) cookie);
drv->eapol_tx_cookie = cookie;
+ drv->eapol_tx_link_id = link_id;
}
return ret;
@@ -6182,7 +6322,8 @@
static int wpa_driver_nl80211_hapd_send_eapol(
void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
+ size_t data_len, int encrypt, const u8 *own_addr, u32 flags,
+ int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -6197,7 +6338,8 @@
if (drv->control_port_ap &&
(drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT))
return nl80211_tx_control_port(bss, addr, ETH_P_EAPOL,
- data, data_len, !encrypt);
+ data, data_len, !encrypt,
+ link_id);
if (drv->device_ap_sme || !drv->use_monitor)
return nl80211_send_eapol_data(bss, addr, data, data_len);
@@ -6496,7 +6638,8 @@
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
- params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384) {
wpa_printf(MSG_DEBUG, " * control port");
if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
goto fail;
@@ -6746,8 +6889,14 @@
if (params->wpa_proto & WPA_PROTO_WPA)
ver |= NL80211_WPA_VERSION_1;
- if (params->wpa_proto & WPA_PROTO_RSN)
- ver |= NL80211_WPA_VERSION_2;
+ if (params->wpa_proto & WPA_PROTO_RSN) {
+#if !defined(CONFIG_DRIVER_NL80211_BRCM) && !defined(CONFIG_DRIVER_NL80211_SYNA)
+ if (wpa_key_mgmt_sae(params->key_mgmt_suite))
+ ver |= NL80211_WPA_VERSION_3;
+ else
+#endif
+ ver |= NL80211_WPA_VERSION_2;
+ }
wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
@@ -6796,7 +6945,8 @@
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
- params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
+ params->key_mgmt_suite == WPA_KEY_MGMT_DPP ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384) {
u32 *mgmt;
unsigned int akm_count = 1, i;
@@ -6880,6 +7030,9 @@
case WPA_KEY_MGMT_DPP:
mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
break;
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
+ break;
case WPA_KEY_MGMT_PSK:
default:
mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
@@ -7040,6 +7193,27 @@
wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail;
+
+ /* Add SAE password in case of SAE authentication offload */
+ if ((params->sae_password || params->passphrase) &&
+ (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA)) {
+ const char *password;
+ size_t pwd_len;
+
+ if (params->sae_password && params->sae_password_id) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Use of SAE password identifiers not supported with driver-based SAE");
+ goto fail;
+ }
+
+ password = params->sae_password;
+ if (!password)
+ password = params->passphrase;
+ pwd_len = os_strlen(password);
+ wpa_printf(MSG_DEBUG, " * SAE password");
+ if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD, pwd_len, password))
+ goto fail;
+ }
#endif /* CONFIG_SAE */
algs = 0;
@@ -7053,6 +7227,8 @@
algs++;
if (params->auth_alg & WPA_AUTH_ALG_FT)
algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_SAE)
+ algs++;
if (algs > 1) {
wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
"selection");
@@ -7532,24 +7708,33 @@
static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
- int idx, u8 *seq)
+ int idx, int link_id, u8 *seq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ int res;
msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
NL80211_CMD_GET_KEY);
if (!msg ||
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
nlmsg_free(msg);
return -ENOBUFS;
}
- memset(seq, 0, 6);
+ os_memset(seq, 0, 6);
+ res = send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to get current TX sequence for a key (link_id=%d idx=%d): %d (%s)",
+ link_id, idx, res, strerror(-res));
+ }
- return send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
+ return res;
}
@@ -7975,7 +8160,8 @@
static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
- int cw_min, int cw_max, int burst_time)
+ int cw_min, int cw_max, int burst_time,
+ int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8027,6 +8213,10 @@
nla_nest_end(msg, txq);
+ if (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+
res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG,
"nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
@@ -8041,7 +8231,7 @@
static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
- const char *ifname, int vlan_id)
+ const char *ifname, int vlan_id, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
@@ -8055,6 +8245,8 @@
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
(vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -8097,14 +8289,15 @@
static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason)
+ u16 reason, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt mgmt;
u8 channel;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
- if (ieee80211_freq_to_chan(bss->flink->freq, &channel) ==
+ if (ieee80211_freq_to_chan(link->freq, &channel) ==
HOSTAPD_MODE_IEEE80211AD) {
/* Deauthentication is not used in DMG/IEEE 802.11ad;
* disassociate the STA instead. */
@@ -8127,7 +8320,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
- 0, NULL, 0, 0);
+ 0, NULL, 0, 0, -1);
}
@@ -8154,7 +8347,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
- 0, NULL, 0, 0);
+ 0, NULL, 0, 0, -1);
}
@@ -8312,7 +8505,8 @@
wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
"interface %s up", name);
}
- return i802_set_sta_vlan(priv, addr, name, 0);
+ return i802_set_sta_vlan(priv, addr, name, 0,
+ NL80211_DRV_LINK_ID_NA);
} else {
if (bridge_ifname &&
linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
@@ -8321,7 +8515,8 @@
"nl80211: Failed to remove interface %s from bridge %s: %s",
name, bridge_ifname, strerror(errno));
- i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0,
+ NL80211_DRV_LINK_ID_NA);
nl80211_remove_iface(drv, if_nametoindex(name));
os_memset(&event, 0, sizeof(event));
event.wds_sta_interface.sta_addr = addr;
@@ -9028,7 +9223,7 @@
!drv->use_monitor))
ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
0, freq, no_cck, offchanok,
- wait_time, NULL, 0, 0);
+ wait_time, NULL, 0, 0, -1);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
@@ -10127,7 +10322,7 @@
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
- 0, 0, NULL, 0, 0) < 0)
+ 0, 0, NULL, 0, 0, -1) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
@@ -10270,10 +10465,46 @@
}
+static int
+nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv,
+ int link_id)
+{
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS Discovery Response Tx link ID %u",
+ link_id);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_TDLS_DISC_RSP_EXT_TX_LINK,
+ link_id)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+#else /* CONFIG_DRIVER_NL80211_QCA */
+ wpa_printf(MSG_ERROR,
+ "nl80211: Setting TX link for TDLS Discovery Response not supported");
+ return -1;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+
+
static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capab, int initiator, const u8 *buf,
- size_t len)
+ size_t len, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -10285,6 +10516,10 @@
if (!dst)
return -EINVAL;
+ if (link_id >= 0 &&
+ nl80211_tdls_set_discovery_resp_link(drv, link_id) < 0)
+ return -EOPNOTSUPP;
+
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
@@ -10476,12 +10711,13 @@
size_t data_len, int noack,
unsigned int freq,
const u16 *csa_offs, size_t csa_offs_len,
- int no_encrypt, unsigned int wait)
+ int no_encrypt, unsigned int wait,
+ int link_id)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
freq, 0, 0, wait, csa_offs,
- csa_offs_len, no_encrypt);
+ csa_offs_len, no_encrypt, link_id);
}
@@ -10493,10 +10729,11 @@
static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
- const char *ifname, int vlan_id)
+ const char *ifname, int vlan_id,
+ int link_id)
{
struct i802_bss *bss = priv;
- return i802_set_sta_vlan(bss, addr, ifname, vlan_id);
+ return i802_set_sta_vlan(bss, addr, ifname, vlan_id, link_id);
}
@@ -10773,6 +11010,7 @@
"capa.enc=0x%x\n"
"capa.auth=0x%x\n"
"capa.flags=0x%llx\n"
+ "capa.flags2=0x%llx\n"
"capa.rrm_flags=0x%x\n"
"capa.max_scan_ssids=%d\n"
"capa.max_sched_scan_ssids=%d\n"
@@ -10797,6 +11035,7 @@
drv->capa.enc,
drv->capa.auth,
(unsigned long long) drv->capa.flags,
+ (unsigned long long) drv->capa.flags2,
drv->capa.rrm_flags,
drv->capa.max_scan_ssids,
drv->capa.max_sched_scan_ssids,
@@ -11293,7 +11532,7 @@
wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
qos_map_set, qos_map_set_len);
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_QOS_MAP)) ||
nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -11597,6 +11836,15 @@
if (!addr)
addr = drv->perm_addr;
+ /*
+ * Try to change the address first without setting the interface
+ * down and then fall back to DOWN/set addr/UP if the first
+ * attempt failed. This can reduce the interface setup time
+ * significantly with some drivers.
+ */
+ if (!linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr))
+ goto done;
+
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
return -1;
@@ -11613,17 +11861,19 @@
return -1;
}
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not restore interface UP after set_mac_addr");
+ }
+
+done:
wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
- bss->ifname, MAC2STR(addr));
+ bss->ifname, MAC2STR(addr));
drv->addr_changed = new_addr;
os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
os_memcpy(bss->addr, addr, ETH_ALEN);
- if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
- {
- wpa_printf(MSG_DEBUG,
- "nl80211: Could not restore interface UP after set_mac_addr");
- }
return 0;
}
@@ -13289,11 +13539,42 @@
*ext_capa_len = drv->extended_capa_len;
/* Replace the default value if a per-interface type value exists */
- for (i = 0; i < drv->num_iface_ext_capa; i++) {
- if (nlmode == drv->iface_ext_capa[i].iftype) {
- *ext_capa = drv->iface_ext_capa[i].ext_capa;
- *ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask;
- *ext_capa_len = drv->iface_ext_capa[i].ext_capa_len;
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ if (nlmode == drv->iface_capa[i].iftype) {
+ *ext_capa = drv->iface_capa[i].ext_capa;
+ *ext_capa_mask = drv->iface_capa[i].ext_capa_mask;
+ *ext_capa_len = drv->iface_capa[i].ext_capa_len;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int nl80211_get_mld_capab(void *priv, enum wpa_driver_if_type type,
+ u16 *eml_capa, u16 *mld_capa_and_ops)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ enum nl80211_iftype nlmode;
+ unsigned int i;
+
+ if (!eml_capa || !mld_capa_and_ops)
+ return -1;
+
+ nlmode = wpa_driver_nl80211_if_type(type);
+
+ /* By default, set to zero */
+ *eml_capa = 0;
+ *mld_capa_and_ops = 0;
+
+ /* Replace the default value if a per-interface type value exists */
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ if (nlmode == drv->iface_capa[i].iftype) {
+ *eml_capa = drv->iface_capa[i].eml_capa;
+ *mld_capa_and_ops =
+ drv->iface_capa[i].mld_capa_and_ops;
break;
}
}
@@ -13773,6 +14054,7 @@
.do_acs = nl80211_do_acs,
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
+ .get_mld_capab = nl80211_get_mld_capab,
.update_connect_params = nl80211_update_connection_params,
.send_external_auth_status = nl80211_send_external_auth_status,
.set_4addr_mode = nl80211_set_4addr_mode,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index c597295..d922912 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -119,12 +119,14 @@
struct wpa_driver_capa capa;
u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
- struct drv_nl80211_ext_capa {
+ struct drv_nl80211_iface_capa {
enum nl80211_iftype iftype;
u8 *ext_capa, *ext_capa_mask;
unsigned int ext_capa_len;
- } iface_ext_capa[NL80211_IFTYPE_MAX];
- unsigned int num_iface_ext_capa;
+ u16 eml_capa;
+ u16 mld_capa_and_ops;
+ } iface_capa[NL80211_IFTYPE_MAX];
+ unsigned int num_iface_capa;
int has_capability;
int has_driver_key_mgmt;
@@ -204,10 +206,12 @@
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_frame_cookie;
+ int send_frame_link_id;
#define MAX_SEND_FRAME_COOKIES 20
u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
unsigned int num_send_frame_cookies;
u64 eapol_tx_cookie;
+ int eapol_tx_link_id;
unsigned int last_mgmt_freq;
@@ -324,6 +328,7 @@
const char * nl80211_iftype_str(enum nl80211_iftype mode);
void nl80211_restore_ap_mode(struct i802_bss *bss);
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
#ifdef ANDROID
int android_nl_socket_set_nonblocking(struct nl_sock *handle);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 09771bb..fdfe8f6 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -600,6 +600,10 @@
capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA;
+
+ if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MFP_OPTIONAL))
capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
@@ -676,7 +680,7 @@
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_RADAR_BACKGROUND))
- capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_RADAR_BACKGROUND;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SECURE_LTF)) {
@@ -696,6 +700,26 @@
capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
}
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SCAN_MIN_PREQ;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_OWE_OFFLOAD_AP;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD_AP))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP;
}
@@ -816,12 +840,12 @@
int rem = 0, i;
struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
- if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+ if (!tb || drv->num_iface_capa == NL80211_IFTYPE_MAX)
return;
nla_for_each_nested(attr, tb, rem) {
unsigned int len;
- struct drv_nl80211_ext_capa *capa;
+ struct drv_nl80211_iface_capa *capa;
nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
nla_len(attr), NULL);
@@ -831,7 +855,7 @@
!tb1[NL80211_ATTR_EXT_CAPA_MASK])
continue;
- capa = &drv->iface_ext_capa[drv->num_iface_ext_capa];
+ capa = &drv->iface_capa[drv->num_iface_capa];
capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
wpa_printf(MSG_DEBUG,
"nl80211: Driver-advertised extended capabilities for interface type %s",
@@ -857,8 +881,16 @@
wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
capa->ext_capa_mask, capa->ext_capa_len);
- drv->num_iface_ext_capa++;
- if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+ if (tb1[NL80211_ATTR_EML_CAPABILITY] &&
+ tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]) {
+ capa->eml_capa =
+ nla_get_u16(tb1[NL80211_ATTR_EML_CAPABILITY]);
+ capa->mld_capa_and_ops =
+ nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
+ }
+
+ drv->num_iface_capa++;
+ if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
break;
}
@@ -867,13 +899,13 @@
err:
/* Cleanup allocated memory on error */
for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
- os_free(drv->iface_ext_capa[i].ext_capa);
- drv->iface_ext_capa[i].ext_capa = NULL;
- os_free(drv->iface_ext_capa[i].ext_capa_mask);
- drv->iface_ext_capa[i].ext_capa_mask = NULL;
- drv->iface_ext_capa[i].ext_capa_len = 0;
+ os_free(drv->iface_capa[i].ext_capa);
+ drv->iface_capa[i].ext_capa = NULL;
+ os_free(drv->iface_capa[i].ext_capa_mask);
+ drv->iface_capa[i].ext_capa_mask = NULL;
+ drv->iface_capa[i].ext_capa_len = 0;
}
- drv->num_iface_ext_capa = 0;
+ drv->num_iface_capa = 0;
}
@@ -1560,9 +1592,10 @@
#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG,
- "nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d",
+ "nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx flags2=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d",
drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth,
- (unsigned long long) drv->capa.flags, drv->capa.rrm_flags,
+ (unsigned long long) drv->capa.flags,
+ (unsigned long long) drv->capa.flags2, drv->capa.rrm_flags,
drv->capa.probe_resp_offloads, drv->capa.max_stations,
drv->capa.max_remain_on_chan, drv->capa.max_scan_ssids);
return 0;
@@ -2557,8 +2590,7 @@
for (j = 0; j < mode->num_channels; j++) {
struct hostapd_channel_data *chan = &mode->channels[j];
- if (chan->freq >= 5925 && chan->freq <= 7125 &&
- !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ if (is_6ghz_freq(chan->freq))
drv->uses_6ghz = true;
res = os_snprintf(pos, end - pos, " %d%s%s%s",
chan->freq,
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 16d6f5b..fdaf07b 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -184,6 +184,7 @@
C2S(NL80211_CMD_MODIFY_LINK_STA)
C2S(NL80211_CMD_REMOVE_LINK_STA)
C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ C2S(NL80211_CMD_LINKS_REMOVED)
C2S(__NL80211_CMD_AFTER_LAST)
}
#undef C2S
@@ -1269,7 +1270,7 @@
if (finished)
bss->flink->freq = data.ch_switch.freq;
- if (link) {
+ if (link && is_sta_interface(drv->nlmode)) {
u8 link_id = nla_get_u8(link);
if (link_id < MAX_NUM_MLD_LINKS &&
@@ -1424,6 +1425,8 @@
event.tx_status.data = frame;
event.tx_status.data_len = len;
event.tx_status.ack = ack != NULL;
+ event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
+ drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
}
@@ -1611,6 +1614,21 @@
}
+static struct i802_link *
+nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if ((unsigned int) bss->links[i].freq == freq &&
+ bss->links[i].link_id != -1)
+ return &bss->links[i];
+ }
+
+ return NULL;
+}
+
+
static void mlme_event(struct i802_bss *bss,
enum nl80211_commands cmd, struct nlattr *frame,
struct nlattr *addr, struct nlattr *timed_out,
@@ -1623,7 +1641,8 @@
u16 stype = 0, auth_type = 0;
const u8 *data;
size_t len;
- int link_id;
+ int link_id = -1;
+ struct i802_link *mld_link = NULL;
if (timed_out && addr) {
mlme_timeout_event(drv, cmd, addr);
@@ -1637,10 +1656,15 @@
return;
}
+ /* Determine the MLD link either by an explicitly provided link id or
+ * finding a match based on the frequency. */
if (link)
- link_id = nla_get_u8(link);
- else
- link_id = -1;
+ mld_link = nl80211_get_link(bss, nla_get_u8(link));
+ else if (freq)
+ mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
+
+ if (mld_link)
+ link_id = mld_link->link_id;
data = nla_data(frame);
len = nla_len(frame);
@@ -1683,7 +1707,9 @@
os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
(is_zero_ether_addr(drv->first_bss->prev_addr) ||
os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
- ETH_ALEN) != 0)) {
+ ETH_ALEN) != 0) &&
+ (!mld_link ||
+ os_memcmp(mld_link->addr, data + 4, ETH_ALEN) != 0)) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
return;
@@ -1880,6 +1906,8 @@
struct nlattr *tb[])
{
union wpa_event_data data;
+ u8 *addr, *link_addr = NULL;
+ int assoc_link_id = -1;
if (!is_ap_interface(drv->nlmode))
return;
@@ -1887,9 +1915,37 @@
return;
os_memset(&data, 0, sizeof(data));
- data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]);
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+
+ if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+ (tb[NL80211_ATTR_MLO_LINK_ID] ||
+ tb[NL80211_ATTR_MLD_ADDR])) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Link info not expected for DH event for non-MLD AP");
+ return;
+ }
+
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ assoc_link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: STA assoc link ID %d in UPDATE_OWE_INFO event",
+ assoc_link_id);
+
+ if (assoc_link_id != NL80211_DRV_LINK_ID_NA &&
+ tb[NL80211_ATTR_MLD_ADDR]) {
+ link_addr = addr;
+ addr = nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: STA assoc link addr " MACSTR,
+ MAC2STR(link_addr));
+ }
+ }
+
+ data.update_dh.peer = addr;
data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]);
data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+ data.update_dh.assoc_link_id = assoc_link_id;
+ data.update_dh.link_addr = link_addr;
wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR,
MAC2STR(data.update_dh.peer));
@@ -2110,23 +2166,60 @@
struct i802_bss *bss,
struct nlattr **tb)
{
- u8 *addr;
+ u8 *peer_addr;
union wpa_event_data data;
if (tb[NL80211_ATTR_MAC] == NULL)
return;
- addr = nla_data(tb[NL80211_ATTR_MAC]);
- wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+ peer_addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR,
+ MAC2STR(peer_addr));
if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
- u8 *ies = NULL;
- size_t ies_len = 0;
- if (tb[NL80211_ATTR_IE]) {
- ies = nla_data(tb[NL80211_ATTR_IE]);
- ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ u8 *link_addr = NULL;
+ int assoc_link_id = -1;
+ u8 *req_ies = NULL, *resp_ies = NULL;
+ size_t req_ies_len = 0, resp_ies_len = 0;
+
+ if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+ (tb[NL80211_ATTR_MLO_LINK_ID] ||
+ tb[NL80211_ATTR_MLD_ADDR])) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO info not expected for new station event for non-MLD AP");
+ return;
}
- wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
- drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ assoc_link_id =
+ nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ wpa_printf(MSG_DEBUG, "nl80211: STA assoc link ID %d",
+ assoc_link_id);
+ if (tb[NL80211_ATTR_MLD_ADDR]) {
+ peer_addr = nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+ link_addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: STA MLD address " MACSTR,
+ MAC2STR(peer_addr));
+ }
+ }
+
+ if (tb[NL80211_ATTR_IE]) {
+ req_ies = nla_data(tb[NL80211_ATTR_IE]);
+ req_ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs",
+ req_ies, req_ies_len);
+ }
+
+ if (tb[NL80211_ATTR_RESP_IE]) {
+ resp_ies = nla_data(tb[NL80211_ATTR_RESP_IE]);
+ resp_ies_len = nla_len(tb[NL80211_ATTR_RESP_IE]);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Resp IEs",
+ resp_ies, resp_ies_len);
+ }
+
+ drv_event_assoc(bss->ctx, peer_addr, req_ies, req_ies_len,
+ resp_ies, resp_ies_len, link_addr,
+ assoc_link_id, 0);
return;
}
@@ -2134,7 +2227,7 @@
return;
os_memset(&data, 0, sizeof(data));
- os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+ os_memcpy(data.ibss_rsn_start.peer, peer_addr, ETH_ALEN);
wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
}
@@ -2626,15 +2719,19 @@
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
event.acs_selected_channels.ch_width =
nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP])
+ event.acs_selected_channels.puncture_bitmap =
+ nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP]);
wpa_printf(MSG_INFO,
- "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d",
+ "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d PUNCBITMAP: 0x%x",
event.acs_selected_channels.pri_freq,
event.acs_selected_channels.sec_freq,
event.acs_selected_channels.ch_width,
event.acs_selected_channels.vht_seg0_center_ch,
event.acs_selected_channels.vht_seg1_center_ch,
event.acs_selected_channels.hw_mode,
- event.acs_selected_channels.edmg_channel);
+ event.acs_selected_channels.edmg_channel,
+ event.acs_selected_channels.puncture_bitmap);
/* Ignore ACS channel list check for backwards compatibility */
@@ -3410,7 +3507,13 @@
}
addr = nla_data(tb[NL80211_ATTR_MAC]);
- if (os_memcmp(addr, connected_addr, ETH_ALEN) != 0) {
+ if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+ event.port_authorized.sta_addr = addr;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Port authorized for STA addr " MACSTR,
+ MAC2STR(addr));
+ } else if (is_sta_interface(drv->nlmode) &&
+ os_memcmp(addr, connected_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Ignore port authorized event for " MACSTR
" (not the currently connected BSSID " MACSTR ")",
@@ -3567,6 +3670,10 @@
event.eapol_tx_status.data = frame + ETH_HLEN;
event.eapol_tx_status.data_len = len - ETH_HLEN;
event.eapol_tx_status.ack = ack != NULL;
+ event.eapol_tx_status.link_id =
+ nla_get_u64(cookie) == drv->eapol_tx_cookie ?
+ drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
+
wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
}
@@ -3621,7 +3728,7 @@
#ifdef CONFIG_IEEE80211AX
-static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+static void nl80211_obss_color_collision(struct i802_bss *bss,
struct nlattr *tb[])
{
union wpa_event_data data;
@@ -3635,37 +3742,34 @@
wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
(long long unsigned int) data.bss_color_collision.bitmap);
- wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
+ wpa_supplicant_event(bss->ctx, EVENT_BSS_COLOR_COLLISION, &data);
}
-static void
-nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
+static void nl80211_color_change_announcement_started(struct i802_bss *bss)
{
union wpa_event_data data = {};
wpa_printf(MSG_DEBUG, "nl80211: CCA started");
- wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
+ wpa_supplicant_event(bss->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
}
-static void
-nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
+static void nl80211_color_change_announcement_aborted(struct i802_bss *bss)
{
union wpa_event_data data = {};
wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
- wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
+ wpa_supplicant_event(bss->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
}
-static void
-nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
+static void nl80211_color_change_announcement_completed(struct i802_bss *bss)
{
union wpa_event_data data = {};
wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
- wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
+ wpa_supplicant_event(bss->ctx, EVENT_CCA_NOTIFY, &data);
}
#endif /* CONFIG_IEEE80211AX */
@@ -3925,16 +4029,16 @@
break;
#ifdef CONFIG_IEEE80211AX
case NL80211_CMD_OBSS_COLOR_COLLISION:
- nl80211_obss_color_collision(drv, tb);
+ nl80211_obss_color_collision(bss, tb);
break;
case NL80211_CMD_COLOR_CHANGE_STARTED:
- nl80211_color_change_announcement_started(drv);
+ nl80211_color_change_announcement_started(bss);
break;
case NL80211_CMD_COLOR_CHANGE_ABORTED:
- nl80211_color_change_announcement_aborted(drv);
+ nl80211_color_change_announcement_aborted(bss);
break;
case NL80211_CMD_COLOR_CHANGE_COMPLETED:
- nl80211_color_change_announcement_completed(drv);
+ nl80211_color_change_announcement_completed(bss);
break;
#endif /* CONFIG_IEEE80211AX */
default:
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 4d33b14..4907e3c 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -41,7 +41,7 @@
struct nl80211_noise_info *info = arg;
if (info->count >= MAX_NL80211_NOISE_FREQS)
- return NL_STOP;
+ return NL_SKIP;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -315,6 +315,14 @@
NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION;
}
+ if (params->min_probe_req_content) {
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SCAN_MIN_PREQ)
+ scan_flags |= NL80211_SCAN_FLAG_MIN_PREQ_CONTENT;
+ else
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_SCAN_FLAG_MIN_PREQ_CONTENT not supported");
+ }
+
if (scan_flags &&
nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
goto fail;
@@ -377,7 +385,15 @@
if (params->bssid) {
wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
MACSTR, MAC2STR(params->bssid));
- if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+ if (nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
+ goto fail;
+ /* NL80211_ATTR_MAC was used for this purpose initially and the
+ * NL80211_ATTR_BSSID was added in 2016 when MAC address
+ * randomization was added. For compatibility with older kernel
+ * versions, add the NL80211_ATTR_MAC attribute as well when
+ * the conflicting functionality is not in use. */
+ if (!params->mac_addr_rand &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
goto fail;
}
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index c7537b7..03366b8 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -285,7 +285,7 @@
static int wired_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
- const u8 *own_addr, u32 flags)
+ const u8 *own_addr, u32 flags, int link_id)
{
struct wpa_driver_wired_data *drv = priv;
struct ieee8023_hdr *hdr;
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index c59fec4..dced2c4 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -11,7 +11,7 @@
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2022 Intel Corporation
+ * Copyright (C) 2018-2023 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -167,7 +167,7 @@
* following events occur.
* a) Expiration of hardware timer whose expiration time is set to maximum
* coalescing delay of matching coalesce rule.
- * b) Coalescing buffer in hardware reaches it's limit.
+ * b) Coalescing buffer in hardware reaches its limit.
* c) Packet doesn't match any of the configured coalesce rules.
*
* User needs to configure following parameters for creating a coalesce
@@ -326,7 +326,7 @@
/**
* DOC: Multi-Link Operation
*
- * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * In Multi-Link Operation, a connection between two MLDs utilizes multiple
* links. To use this in nl80211, various commands and responses now need
* to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
* Additionally, various commands that need to operate on a specific link
@@ -335,6 +335,15 @@
*/
/**
+ * DOC: OWE DH IE handling offload
+ *
+ * By setting @NL80211_EXT_FEATURE_OWE_OFFLOAD flag, drivers can indicate
+ * kernel/application space to avoid DH IE handling. When this flag is
+ * advertised, the driver/device will take care of DH IE inclusion and
+ * processing of peer DH IE to generate PMK.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -1309,6 +1318,11 @@
* The number of peers that HW timestamping can be enabled for concurrently
* is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
*
+ * @NL80211_CMD_LINKS_REMOVED: Notify userspace about the removal of STA MLD
+ * setup links due to AP MLD removing the corresponding affiliated APs with
+ * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+ * information about the removed STA MLD setup links.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1562,6 +1576,8 @@
NL80211_CMD_SET_HW_TIMESTAMP,
+ NL80211_CMD_LINKS_REMOVED,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -2683,11 +2699,13 @@
*
* @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
* discovery. It is a nested attribute, see
- * &enum nl80211_fils_discovery_attributes.
+ * &enum nl80211_fils_discovery_attributes. Userspace should pass an empty
+ * nested attribute to disable this feature and delete the templates.
*
* @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
* unsolicited broadcast probe response. It is a nested attribute, see
- * &enum nl80211_unsol_bcast_probe_resp_attributes.
+ * &enum nl80211_unsol_bcast_probe_resp_attributes. Userspace should pass an empty
+ * nested attribute to disable this feature and delete the templates.
*
* @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION)
@@ -2805,6 +2823,9 @@
* index. If the userspace includes more RNR elements than number of
* MBSSID elements then these will be added in every EMA beacon.
*
+ * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
+ * disabled.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3341,6 +3362,8 @@
NL80211_ATTR_EMA_RNR_ELEMS,
+ NL80211_ATTR_MLO_LINK_DISABLED,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3667,6 +3690,13 @@
* (u8, see &enum nl80211_eht_gi)
* @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
* non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
+ * @NL80211_RATE_INFO_S1G_MCS: S1G MCS index (u8, 0-10)
+ * @NL80211_RATE_INFO_S1G_NSS: S1G NSS value (u8, 1-4)
+ * @NL80211_RATE_INFO_1_MHZ_WIDTH: 1 MHz S1G rate
+ * @NL80211_RATE_INFO_2_MHZ_WIDTH: 2 MHz S1G rate
+ * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
+ * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
+ * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
* @__NL80211_RATE_INFO_AFTER_LAST: internal use
*/
enum nl80211_rate_info {
@@ -3693,6 +3723,13 @@
NL80211_RATE_INFO_EHT_NSS,
NL80211_RATE_INFO_EHT_GI,
NL80211_RATE_INFO_EHT_RU_ALLOC,
+ NL80211_RATE_INFO_S1G_MCS,
+ NL80211_RATE_INFO_S1G_NSS,
+ NL80211_RATE_INFO_1_MHZ_WIDTH,
+ NL80211_RATE_INFO_2_MHZ_WIDTH,
+ NL80211_RATE_INFO_4_MHZ_WIDTH,
+ NL80211_RATE_INFO_8_MHZ_WIDTH,
+ NL80211_RATE_INFO_16_MHZ_WIDTH,
/* keep last */
__NL80211_RATE_INFO_AFTER_LAST,
@@ -4187,6 +4224,8 @@
* as the primary or any of the secondary channels isn't possible
* @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
* in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
+ * is allowed on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4225,6 +4264,7 @@
NL80211_FREQUENCY_ATTR_16MHZ,
NL80211_FREQUENCY_ATTR_NO_320MHZ,
NL80211_FREQUENCY_ATTR_NO_EHT,
+ NL80211_FREQUENCY_ATTR_PSD,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4325,6 +4365,8 @@
* a given frequency range. The value is in mBm (100 * dBm).
* @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
* If not present or 0 default CAC time will be used.
+ * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
+ * This could be negative.
* @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
* currently defined
* @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -4342,6 +4384,8 @@
NL80211_ATTR_DFS_CAC_TIME,
+ NL80211_ATTR_POWER_RULE_PSD,
+
/* keep last */
__NL80211_REG_RULE_ATTR_AFTER_LAST,
NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -4424,6 +4468,8 @@
* @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
* @NL80211_RRF_NO_HE: HE operation not allowed
* @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+ * @NL80211_RRF_NO_EHT: EHT operation not allowed
+ * @NL80211_RRF_PSD: Ruleset has power spectral density value
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
@@ -4443,6 +4489,8 @@
NL80211_RRF_NO_160MHZ = 1<<16,
NL80211_RRF_NO_HE = 1<<17,
NL80211_RRF_NO_320MHZ = 1<<18,
+ NL80211_RRF_NO_EHT = 1<<19,
+ NL80211_RRF_PSD = 1<<20,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
@@ -5010,7 +5058,7 @@
* elements from a Beacon frame (bin); not present if no Beacon frame has
* yet been received
* @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
- * (u32, enum nl80211_bss_scan_width)
+ * (u32, enum nl80211_bss_scan_width) - No longer used!
* @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
* (not present if no beacon frame has been received yet)
* @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
@@ -6372,6 +6420,12 @@
* in authentication and deauthentication frames sent to unassociated peer
* using @NL80211_CMD_FRAME.
*
+ * @NL80211_EXT_FEATURE_OWE_OFFLOAD: Driver/Device wants to do OWE DH IE
+ * handling in station mode.
+ *
+ * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
+ * handling in AP mode.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -6443,6 +6497,8 @@
NL80211_EXT_FEATURE_PUNCT,
NL80211_EXT_FEATURE_SECURE_NAN,
NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -7578,7 +7634,7 @@
* @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU).
* Allowed range: 0..10000 (TU = Time Unit)
* @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU).
- * Allowed range: 0..10000 (TU = Time Unit)
+ * Allowed range: 0..10000 (TU = Time Unit). If set to 0, the feature is disabled.
* @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action
* frame including the headers.
*
@@ -7611,7 +7667,8 @@
*
* @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU).
* Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0
- * 26.17.2.3.2 (AP behavior for fast passive scanning).
+ * 26.17.2.3.2 (AP behavior for fast passive scanning). If set to 0, the feature is
+ * disabled.
* @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response
* frame template (binary).
*
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index ff7dc1e..4a09908 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1773,6 +1773,10 @@
wpabuf_put_data(resp, identity, identity_len);
wpabuf_free(privacy_identity);
+ os_free(sm->identity);
+ sm->identity = os_memdup(identity, identity_len);
+ sm->identity_len = identity_len;
+
return resp;
}
@@ -2248,9 +2252,15 @@
dl_list_init(&sm->erp_keys);
os_memset(&tlsconf, 0, sizeof(tlsconf));
+#ifndef CONFIG_OPENSC_ENGINE_PATH
tlsconf.opensc_engine_path = conf->opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
tlsconf.openssl_ciphers = conf->openssl_ciphers;
#ifdef CONFIG_FIPS
tlsconf.fips_mode = 1;
@@ -2297,6 +2307,7 @@
tls_deinit(sm->ssl_ctx2);
tls_deinit(sm->ssl_ctx);
eap_peer_erp_free_keys(sm);
+ os_free(sm->identity);
os_free(sm);
}
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 49338cf..8c52582 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -41,8 +41,8 @@
size_t reauth_id_len;
int reauth;
unsigned int counter, counter_too_small;
- u8 *last_eap_identity;
- size_t last_eap_identity_len;
+ u8 *mk_identity;
+ size_t mk_identity_len;
enum {
CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
} state;
@@ -140,6 +140,13 @@
}
}
+ if (sm->identity) {
+ /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY
+ * is not used. */
+ data->mk_identity = os_memdup(sm->identity, sm->identity_len);
+ data->mk_identity_len = sm->identity_len;
+ }
+
return data;
}
@@ -177,7 +184,7 @@
if (data) {
os_free(data->pseudonym);
os_free(data->reauth_id);
- os_free(data->last_eap_identity);
+ os_free(data->mk_identity);
wpabuf_free(data->id_msgs);
os_free(data->network_name);
eap_aka_clear_keys(data, 0);
@@ -373,7 +380,6 @@
#define CLEAR_PSEUDONYM 0x01
#define CLEAR_REAUTH_ID 0x02
-#define CLEAR_EAP_ID 0x04
static void eap_aka_clear_identities(struct eap_sm *sm,
struct eap_aka_data *data, int id)
@@ -392,12 +398,6 @@
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
- wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
- os_free(data->last_eap_identity);
- data->last_eap_identity = NULL;
- data->last_eap_identity_len = 0;
- }
}
@@ -694,6 +694,8 @@
size_t identity_len = 0;
struct eap_sim_msg *msg;
struct wpabuf *enc_identity = NULL;
+ struct eap_peer_config *config = NULL;
+ bool use_imsi_identity = false;
data->reauth = 0;
if (id_req == ANY_ID && data->reauth_id) {
@@ -723,10 +725,13 @@
data->pseudonym_len))
ids &= ~CLEAR_PSEUDONYM;
eap_aka_clear_identities(sm, data, ids);
+
+ config = eap_get_config(sm);
+ if (config && config->imsi_identity)
+ use_imsi_identity = true;
}
#ifdef CRYPTO_RSA_OAEP_SHA256
if (identity && data->imsi_privacy_key) {
- struct eap_peer_config *config;
const char *attr = NULL;
config = eap_get_config(sm);
@@ -742,13 +747,16 @@
data, id,
EAP_AKA_UNABLE_TO_PROCESS_PACKET);
}
+ /* Use the real identity, not the encrypted one, in MK
+ * derivation. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(identity, identity_len);
+ data->mk_identity_len = identity_len;
identity = wpabuf_head(enc_identity);
identity_len = wpabuf_len(enc_identity);
}
#endif /* CRYPTO_RSA_OAEP_SHA256 */
}
- if (id_req != NO_ID_REQ)
- eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
@@ -759,6 +767,22 @@
identity, identity_len);
eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
identity, identity_len);
+ if (use_imsi_identity && config && config->imsi_identity) {
+ /* Use the IMSI identity override, i.e., the not
+ * encrypted one, in MK derivation, when using
+ * externally encrypted identity in configuration. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(
+ config->imsi_identity,
+ config->imsi_identity_len);
+ data->mk_identity_len = config->imsi_identity_len;
+ } else if (!enc_identity) {
+ /* Use the last AT_IDENTITY value as the identity in
+ * MK derivation. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(identity, identity_len);
+ data->mk_identity_len = identity_len;
+ }
}
wpabuf_free(enc_identity);
@@ -1148,25 +1172,9 @@
data->network_name_len);
}
#endif /* EAP_AKA_PRIME */
- if (data->last_eap_identity) {
- identity = data->last_eap_identity;
- identity_len = data->last_eap_identity_len;
- } else if (data->pseudonym &&
- !eap_sim_anonymous_username(data->pseudonym,
- data->pseudonym_len)) {
- identity = data->pseudonym;
- identity_len = data->pseudonym_len;
- } else {
- struct eap_peer_config *config;
- config = eap_get_config(sm);
- if (config && config->imsi_identity) {
- identity = config->imsi_identity;
- identity_len = config->imsi_identity_len;
- } else {
- identity = eap_get_config_identity(sm, &identity_len);
- }
- }
+ identity = data->mk_identity;
+ identity_len = data->mk_identity_len;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
"derivation", identity, identity_len);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
@@ -1195,7 +1203,7 @@
* other words, if no new identities are received, full
* authentication will be used on next reauthentication (using
* pseudonym identity or permanent identity). */
- eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
if (attr->encr_data) {
u8 *decrypted;
@@ -1405,14 +1413,8 @@
/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
* reauth_id must not be used to start a new reauthentication.
- * However, since it was used in the last EAP-Response-Identity
- * packet, it has to saved for the following fullauth to be
- * used in MK derivation. */
- os_free(data->last_eap_identity);
- data->last_eap_identity = data->reauth_id;
- data->last_eap_identity_len = data->reauth_id_len;
- data->reauth_id = NULL;
- data->reauth_id_len = 0;
+ */
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
os_free(decrypted);
@@ -1437,7 +1439,7 @@
data->nonce_s, data->mk,
data->msk, data->emsk);
}
- eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
eap_aka_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
@@ -1453,8 +1455,7 @@
if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
"fast reauths performed - force fullauth");
- eap_aka_clear_identities(sm, data,
- CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
}
os_free(decrypted);
return eap_aka_response_reauth(data, id, 0, data->nonce_s);
@@ -1570,7 +1571,10 @@
static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_aka_data *data = priv;
- eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
+
+ os_free(data->mk_identity);
+ data->mk_identity = NULL;
+ data->mk_identity_len = 0;
data->prev_id = -1;
wpabuf_free(data->id_msgs);
data->id_msgs = NULL;
@@ -1583,6 +1587,15 @@
static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_aka_data *data = priv;
+
+ if (sm->identity) {
+ /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY
+ * is not used. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(sm->identity, sm->identity_len);
+ data->mk_identity_len = sm->identity_len;
+ }
+
data->num_id_req = 0;
data->num_notification = 0;
eap_aka_state(data, CONTINUE);
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 3fe5f77..15d1cb7 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -390,6 +390,10 @@
unsigned int use_machine_cred:1;
struct dl_list erp_keys; /* struct eap_erp_key */
+
+ /* Identity used in EAP-Response/Identity */
+ u8 *identity;
+ size_t identity_len;
};
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 6f18ebf..1a5f81a 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -43,8 +43,8 @@
size_t reauth_id_len;
int reauth;
unsigned int counter, counter_too_small;
- u8 *last_eap_identity;
- size_t last_eap_identity_len;
+ u8 *mk_identity;
+ size_t mk_identity_len;
enum {
CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE
} state;
@@ -158,6 +158,13 @@
}
}
+ if (sm->identity) {
+ /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY
+ * is not used. */
+ data->mk_identity = os_memdup(sm->identity, sm->identity_len);
+ data->mk_identity_len = sm->identity_len;
+ }
+
eap_sim_state(data, CONTINUE);
return data;
@@ -185,7 +192,7 @@
os_free(data->ver_list);
os_free(data->pseudonym);
os_free(data->reauth_id);
- os_free(data->last_eap_identity);
+ os_free(data->mk_identity);
eap_sim_clear_keys(data, 0);
#ifdef CRYPTO_RSA_OAEP_SHA256
crypto_rsa_key_free(data->imsi_privacy_key);
@@ -399,7 +406,6 @@
#define CLEAR_PSEUDONYM 0x01
#define CLEAR_REAUTH_ID 0x02
-#define CLEAR_EAP_ID 0x04
static void eap_sim_clear_identities(struct eap_sm *sm,
struct eap_sim_data *data, int id)
@@ -418,12 +424,6 @@
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
- wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id");
- os_free(data->last_eap_identity);
- data->last_eap_identity = NULL;
- data->last_eap_identity_len = 0;
- }
}
@@ -562,6 +562,8 @@
struct eap_sim_msg *msg;
struct wpabuf *resp;
struct wpabuf *enc_identity = NULL;
+ struct eap_peer_config *config = NULL;
+ bool use_imsi_identity = false;
data->reauth = 0;
if (id_req == ANY_ID && data->reauth_id) {
@@ -591,10 +593,13 @@
data->pseudonym_len))
ids &= ~CLEAR_PSEUDONYM;
eap_sim_clear_identities(sm, data, ids);
+
+ config = eap_get_config(sm);
+ if (config && config->imsi_identity)
+ use_imsi_identity = true;
}
#ifdef CRYPTO_RSA_OAEP_SHA256
if (identity && data->imsi_privacy_key) {
- struct eap_peer_config *config;
const char *attr = NULL;
config = eap_get_config(sm);
@@ -610,13 +615,16 @@
data, id,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
+ /* Use the real identity, not the encrypted one, in MK
+ * derivation. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(identity, identity_len);
+ data->mk_identity_len = identity_len;
identity = wpabuf_head(enc_identity);
identity_len = wpabuf_len(enc_identity);
}
#endif /* CRYPTO_RSA_OAEP_SHA256 */
}
- if (id_req != NO_ID_REQ)
- eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
@@ -626,6 +634,22 @@
identity, identity_len);
eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
identity, identity_len);
+ if (use_imsi_identity && config && config->imsi_identity) {
+ /* Use the IMSI identity override, i.e., the not
+ * encrypted one, in MK derivation, when using
+ * externally encrypted identity in configuration. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(
+ config->imsi_identity,
+ config->imsi_identity_len);
+ data->mk_identity_len = config->imsi_identity_len;
+ } else if (!enc_identity) {
+ /* Use the last AT_IDENTITY value as the identity in
+ * MK derivation. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(identity, identity_len);
+ data->mk_identity_len = identity_len;
+ }
}
wpabuf_free(enc_identity);
if (!data->reauth) {
@@ -887,25 +911,9 @@
return eap_sim_client_error(data, id,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- if (data->last_eap_identity) {
- identity = data->last_eap_identity;
- identity_len = data->last_eap_identity_len;
- } else if (data->pseudonym &&
- !eap_sim_anonymous_username(data->pseudonym,
- data->pseudonym_len)) {
- identity = data->pseudonym;
- identity_len = data->pseudonym_len;
- } else {
- struct eap_peer_config *config;
- config = eap_get_config(sm);
- if (config && config->imsi_identity) {
- identity = config->imsi_identity;
- identity_len = config->imsi_identity_len;
- } else {
- identity = eap_get_config_identity(sm, &identity_len);
- }
- }
+ identity = data->mk_identity;
+ identity_len = data->mk_identity_len;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
"derivation", identity, identity_len);
eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
@@ -931,7 +939,7 @@
* other words, if no new reauth identity is received, full
* authentication will be used on next reauthentication (using
* pseudonym identity or permanent identity). */
- eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
if (attr->encr_data) {
u8 *decrypted;
@@ -1141,14 +1149,8 @@
/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
* reauth_id must not be used to start a new reauthentication.
- * However, since it was used in the last EAP-Response-Identity
- * packet, it has to saved for the following fullauth to be
- * used in MK derivation. */
- os_free(data->last_eap_identity);
- data->last_eap_identity = data->reauth_id;
- data->last_eap_identity_len = data->reauth_id_len;
- data->reauth_id = NULL;
- data->reauth_id_len = 0;
+ */
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
os_free(decrypted);
@@ -1165,7 +1167,7 @@
data->reauth_id, data->reauth_id_len,
data->nonce_s, data->mk, data->msk,
data->emsk);
- eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
eap_sim_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
@@ -1181,8 +1183,7 @@
if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
"fast reauths performed - force fullauth");
- eap_sim_clear_identities(sm, data,
- CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
}
os_free(decrypted);
return eap_sim_response_reauth(data, id, 0, data->nonce_s);
@@ -1291,7 +1292,10 @@
static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
- eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
+
+ os_free(data->mk_identity);
+ data->mk_identity = NULL;
+ data->mk_identity_len = 0;
data->use_result_ind = 0;
eap_sim_clear_keys(data, 1);
}
@@ -1306,6 +1310,15 @@
eap_sim_deinit(sm, data);
return NULL;
}
+
+ if (sm->identity) {
+ /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY
+ * is not used. */
+ os_free(data->mk_identity);
+ data->mk_identity = os_memdup(sm->identity, sm->identity_len);
+ data->mk_identity_len = sm->identity_len;
+ }
+
data->num_id_req = 0;
data->num_notification = 0;
eap_sim_state(data, CONTINUE);
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 3696e1d..d965a25 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -220,6 +220,10 @@
int eap_sim_aka_result_ind;
int eap_sim_id;
+ /* Maximum number of fast re-authentications allowed after each full
+ * EAP-SIM/AKA authentication. */
+ int eap_sim_aka_fast_reauth_limit;
+
/**
* tnc - Trusted Network Connect (TNC)
*
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 1c59bb0..10affa4 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -160,6 +160,7 @@
size_t identity_len;
char *serial_num;
char imsi[20];
+ char sim_aka_permanent[20];
/* Whether Phase 2 method should validate identity match */
int require_identity_match;
int lastId; /* Identifier used in the last EAP-Packet */
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 5fb19e9..880ffa3 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -110,7 +110,29 @@
return 0;
}
- wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
+ if (data->reauth->counter > sm->cfg->eap_sim_aka_fast_reauth_limit) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-AKA: Too many fast re-authentication attemps - fall back to full authentication");
+ if (sm->cfg->eap_sim_id & 0x04) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-AKA: Permanent identity recognized - skip AKA-Identity exchange");
+ os_strlcpy(data->permanent, data->reauth->permanent,
+ sizeof(data->permanent));
+ os_strlcpy(sm->sim_aka_permanent,
+ data->reauth->permanent,
+ sizeof(sm->sim_aka_permanent));
+ eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
+ data->reauth);
+ data->reauth = NULL;
+ eap_aka_fullauth(sm, data);
+ return 1;
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-AKA: Using fast re-authentication (counter=%d)",
+ data->reauth->counter);
os_strlcpy(data->permanent, data->reauth->permanent,
sizeof(data->permanent));
data->counter = data->reauth->counter;
@@ -134,10 +156,17 @@
struct eap_aka_data *data)
{
char *username;
+ const u8 *identity = sm->identity;
+ size_t identity_len = sm->identity_len;
+
+ if (sm->sim_aka_permanent[0]) {
+ identity = (const u8 *) sm->sim_aka_permanent;
+ identity_len = os_strlen(sm->sim_aka_permanent);
+ }
/* Check if we already know the identity from EAP-Response/Identity */
- username = sim_get_username(sm->identity, sm->identity_len);
+ username = sim_get_username(identity, identity_len);
if (username == NULL)
return;
@@ -150,6 +179,16 @@
return;
}
+ if (sm->sim_aka_permanent[0] && data->state == IDENTITY) {
+ /* Skip AKA/Identity exchange since the permanent identity
+ * was recognized. */
+ os_free(username);
+ os_strlcpy(data->permanent, sm->sim_aka_permanent,
+ sizeof(data->permanent));
+ eap_aka_fullauth(sm, data);
+ return;
+ }
+
if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
(data->eap_method == EAP_TYPE_AKA &&
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 1bcf26c..e418c07 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -106,12 +106,28 @@
{
struct eap_sim_msg *msg;
u8 ver[2];
+ bool id_req = true;
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_START);
data->start_round++;
- if (data->start_round == 1) {
+
+ if (data->start_round == 1 && (sm->cfg->eap_sim_id & 0x04)) {
+ char *username;
+
+ username = sim_get_username(sm->identity, sm->identity_len);
+ if (username && username[0] == EAP_SIM_REAUTH_ID_PREFIX &&
+ eap_sim_db_get_reauth_entry(sm->cfg->eap_sim_db_priv,
+ username))
+ id_req = false;
+
+ os_free(username);
+ }
+
+ if (!id_req) {
+ wpa_printf(MSG_DEBUG, " No identity request");
+ } else if (data->start_round == 1) {
/*
* RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
* ignored and the SIM/Start is used to request the identity.
@@ -434,6 +450,7 @@
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
+ const u8 *identity;
size_t identity_len;
u8 ver_list[2];
u8 *new_identity;
@@ -449,9 +466,13 @@
goto skip_id_update;
}
+ if ((sm->cfg->eap_sim_id & 0x04) &&
+ (!attr->identity || attr->identity_len == 0))
+ goto skip_id_attr;
+
/*
- * We always request identity in SIM/Start, so the peer is required to
- * have replied with one.
+ * Unless explicitly configured otherwise, we always request identity
+ * in SIM/Start, so the peer is required to have replied with one.
*/
if (!attr->identity || attr->identity_len == 0) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
@@ -467,9 +488,17 @@
os_memcpy(sm->identity, attr->identity, attr->identity_len);
sm->identity_len = attr->identity_len;
+skip_id_attr:
+ if (sm->sim_aka_permanent[0]) {
+ identity = (const u8 *) sm->sim_aka_permanent;
+ identity_len = os_strlen(sm->sim_aka_permanent);
+ } else {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ }
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
- sm->identity, sm->identity_len);
- username = sim_get_username(sm->identity, sm->identity_len);
+ identity, identity_len);
+ username = sim_get_username(identity, identity_len);
if (username == NULL)
goto failed;
@@ -485,7 +514,30 @@
/* Remain in START state for another round */
return;
}
- wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
+
+ if (data->reauth->counter >
+ sm->cfg->eap_sim_aka_fast_reauth_limit &&
+ (sm->cfg->eap_sim_id & 0x04)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-SIM: Too many fast re-authentication attemps - fall back to full authentication");
+ wpa_printf(MSG_DEBUG,
+ "EAP-SIM: Permanent identity recognized - skip new Identity query");
+ os_strlcpy(data->permanent,
+ data->reauth->permanent,
+ sizeof(data->permanent));
+ os_strlcpy(sm->sim_aka_permanent,
+ data->reauth->permanent,
+ sizeof(sm->sim_aka_permanent));
+ eap_sim_db_remove_reauth(
+ sm->cfg->eap_sim_db_priv,
+ data->reauth);
+ data->reauth = NULL;
+ goto skip_id_update;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-SIM: Using fast re-authentication (counter=%d)",
+ data->reauth->counter);
os_strlcpy(data->permanent, data->reauth->permanent,
sizeof(data->permanent));
data->counter = data->reauth->counter;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 334ce6c..f033233 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -2180,9 +2180,15 @@
sm->authPeriod = 30;
os_memset(&conf, 0, sizeof(conf));
+#ifndef CONFIG_OPENSC_ENGINE_PATH
conf.opensc_engine_path = ctx->opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
conf.pkcs11_module_path = ctx->pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
conf.openssl_ciphers = ctx->openssl_ciphers;
conf.wps = ctx->wps;
conf.cert_in_cb = ctx->cert_in_cb;
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index fe34ec9..b229426 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -188,6 +188,7 @@
*/
void (*aborted_cached)(void *ctx);
+#ifndef CONFIG_OPENSC_ENGINE_PATH
/**
* opensc_engine_path - Path to the OpenSSL engine for opensc
*
@@ -195,7 +196,9 @@
* engine (engine_opensc.so); if %NULL, this engine is not loaded.
*/
const char *opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
/**
* pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
*
@@ -203,7 +206,9 @@
* engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
*/
const char *pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
/**
* pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
*
@@ -212,6 +217,7 @@
* module is not loaded.
*/
const char *pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
/**
* openssl_ciphers - OpenSSL cipher string
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index 60de9fe..3f0b299 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -20,6 +20,7 @@
#include <sys/sysctl.h>
#endif /* __sun__ */
+#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
@@ -94,6 +95,13 @@
} else {
buf = (unsigned char *) (ethhdr + 1);
len = hdr.caplen - sizeof(*ethhdr);
+
+ /* Handle IEEE 802.1Q encapsulated frames */
+ if (len >= ETHER_VLAN_ENCAP_LEN &&
+ ethhdr->h_proto == htons(ETH_P_8021Q)) {
+ buf += ETHER_VLAN_ENCAP_LEN;
+ len -= ETHER_VLAN_ENCAP_LEN;
+ }
}
l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
}
@@ -122,10 +130,10 @@
os_snprintf(pcap_filter, sizeof(pcap_filter),
"not ether src " MACSTR " and "
"( ether dst " MACSTR " or ether dst " MACSTR " ) and "
- "ether proto 0x%x",
+ "( ether proto 0x%x or ( vlan 0 and ether proto 0x%x ) )",
MAC2STR(l2->own_addr), /* do not receive own packets */
MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
- protocol);
+ protocol, protocol);
if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
return -1;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index a1fe121..267399d 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1502,11 +1502,12 @@
} else {
/* Select any random available channel from the first available
* operating class */
- p2p_channel_select(&p2p->cfg->channels, NULL,
- &p2p->op_reg_class,
- &p2p->op_channel);
- p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference",
- p2p->op_channel, p2p->op_reg_class);
+ if (p2p_channel_select(&p2p->cfg->channels, NULL,
+ &p2p->op_reg_class,
+ &p2p->op_channel) == 0)
+ p2p_dbg(p2p,
+ "Select random available channel %d from operating class %d as operating channel preference",
+ p2p->op_channel, p2p->op_reg_class);
}
p2p_copy_channels(&p2p->channels, &p2p->cfg->channels, p2p->allow_6ghz);
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 5d2299c..07d6ca0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -93,6 +93,12 @@
return -1;
}
msg->listen_channel = data;
+ if (has_ctrl_char(data, 2)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: * Listen Channel: Country(binary) %02x %02x (0x%02x) Regulatory Class %d Channel Number %d",
+ data[0], data[1], data[2], data[3], data[4]);
+ break;
+ }
wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
"Country %c%c(0x%02x) Regulatory "
"Class %d Channel Number %d", data[0], data[1],
@@ -110,6 +116,12 @@
return -1;
}
msg->operating_channel = data;
+ if (has_ctrl_char(data, 2)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: * Operating Channel: Country(binary) %02x %02x (0x%02x) Regulatory Class %d Channel Number %d",
+ data[0], data[1], data[2], data[3], data[4]);
+ break;
+ }
wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
"Country %c%c(0x%02x) Regulatory "
"Class %d Channel Number %d", data[0], data[1],
@@ -123,8 +135,15 @@
}
msg->channel_list = data;
msg->channel_list_len = len;
- wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
- "'%c%c(0x%02x)'", data[0], data[1], data[2]);
+ if (has_ctrl_char(data, 2)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: * Channel List: Country String (binary) %02x %02x (0x%02x)",
+ data[0], data[1], data[2]);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: * Channel List: Country String '%c%c(0x%02x)'",
+ data[0], data[1], data[2]);
+ }
wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
msg->channel_list, msg->channel_list_len);
break;
@@ -526,7 +545,9 @@
{
struct ieee802_11_elems elems;
- ieee802_11_parse_elems(data, len, &elems, 0);
+ if (ieee802_11_parse_elems(data, len, &elems, 0) == ParseFailed)
+ return -1;
+
if (elems.ds_params)
msg->ds_params = elems.ds_params;
if (elems.ssid)
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 741b093..f75fd8f 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1856,6 +1856,18 @@
kay->rcvd_keys++;
participant->to_use_sak = true;
+ /*
+ * The key server may not include dist sak and use sak in one packet.
+ * Meanwhile, after dist sak, the current participant (non-key server)
+ * will install SC or SA(s) after decoding the dist sak which may take
+ * few seconds in real physical platforms. Meanwhile, the peer expire
+ * time is always initialized at adding the key server to peer list.
+ * The gap between adding the key server to peer list and processing
+ * next use sak packet may exceed the threshold of MKA_LIFE_TIME (6 s).
+ * It will cause an unexpected cleanup (delete SC and SA(s)), so,
+ * update the expire timeout at dist sak also. */
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+
return 0;
}
@@ -2584,6 +2596,7 @@
struct ieee802_1x_kay_peer *peer, *pre_peer;
struct os_reltime now;
bool lp_changed;
+ bool key_server_removed;
struct receive_sc *rxsc, *pre_rxsc;
struct transmit_sa *txsa, *pre_txsa;
@@ -2610,6 +2623,7 @@
}
lp_changed = false;
+ key_server_removed = false;
dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (now.sec > peer->expire) {
@@ -2625,12 +2639,32 @@
participant, rxsc);
}
}
+ key_server_removed |= peer->is_key_server;
dl_list_del(&peer->list);
os_free(peer);
lp_changed = true;
}
}
+ /* The key server may be removed due to the ingress packets delay.
+ * In this situation, the endpoint of the key server may not be aware
+ * of this participant who has removed the key server from the peer
+ * list. Because the egress traffic is normal, the key server will not
+ * remove this participant from the peer list of the key server. So in
+ * the next MKA message, the key server will not dispatch a new SAK to
+ * this participant. And this participant cannot be aware that that is
+ * a new round of communication so it will not update its MI at
+ * re-adding the key server to its peer list. So we need to update MI
+ * to avoid the failure of the re-establishment MKA session. */
+ if (key_server_removed) {
+ if (!reset_participant_mi(participant))
+ wpa_printf(MSG_WARNING,
+ "KaY: Could not update mi on key server removal");
+ else
+ wpa_printf(MSG_DEBUG,
+ "KaY: Update mi on key server removal");
+ }
+
if (lp_changed) {
if (dl_list_empty(&participant->live_peers)) {
participant->advised_desired = false;
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index 78a9dd7..47be403 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -335,6 +335,8 @@
}
}
+ pasn->pmk_len = pmk_len;
+ os_memcpy(pasn->pmk, pmk, pmk_len);
ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr,
wpabuf_head(secret), wpabuf_len(secret),
&pasn->ptk, pasn->akmp,
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index eb434fa..b2c4809 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -134,6 +134,23 @@
if (!pmksa->sm)
return;
+ if (pmksa->sm->driver_bss_selection) {
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = pmksa->sm->cur_pmksa ?
+ pmksa->sm->cur_pmksa :
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL,
+ NULL, 0);
+ if (entry && wpa_key_mgmt_sae(entry->akmp)) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: remove reauth threshold passed PMKSA from the driver for SAE");
+ entry->sae_reauth_scheduled = true;
+ wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx,
+ entry->aa, entry->pmkid, NULL);
+ return;
+ }
+ }
+
pmksa->sm->cur_pmksa = NULL;
eapol_sm_request_reauth(pmksa->sm->eapol);
}
@@ -180,7 +197,10 @@
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, NULL, 0);
- if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
+ if (entry &&
+ (!wpa_key_mgmt_sae(entry->akmp) ||
+ (pmksa->sm->driver_bss_selection &&
+ !entry->sae_reauth_scheduled))) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
sec = 0;
@@ -224,6 +244,9 @@
if (pmk_len > PMK_LEN_MAX)
return NULL;
+ if (kck_len > WPA_KCK_MAX_LEN)
+ return NULL;
+
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
@@ -232,11 +255,17 @@
if (pmkid) {
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
} else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
- if (kck)
+ if (kck) {
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+ os_memcpy(entry->kck, kck, kck_len);
+ entry->kck_len = kck_len;
+ }
} else if (wpa_key_mgmt_suite_b(akmp)) {
- if (kck)
+ if (kck) {
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+ os_memcpy(entry->kck, kck, kck_len);
+ entry->kck_len = kck_len;
+ }
} else {
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
}
@@ -490,7 +519,7 @@
wpa_key_mgmt_fils(old_entry->akmp))
pmkid = old_entry->pmkid;
new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
- pmkid, NULL, 0,
+ pmkid, old_entry->kck, old_entry->kck_len,
aa, pmksa->sm->own_addr,
old_entry->network_ctx, old_entry->akmp,
old_entry->fils_cache_id_set ?
@@ -613,12 +642,13 @@
* @network_ctx: Network configuration context
* @try_opportunistic: Whether to allow opportunistic PMKSA caching
* @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
+ * @associated: Whether the device is associated
* Returns: 0 if PMKSA was found or -1 if no matching entry was found
*/
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
int try_opportunistic, const u8 *fils_cache_id,
- int akmp)
+ int akmp, bool associated)
{
struct rsn_pmksa_cache *pmksa = sm->pmksa;
wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
@@ -656,13 +686,29 @@
if (wpa_key_mgmt_sae(sm->cur_pmksa->akmp) &&
os_get_reltime(&now) == 0 &&
sm->cur_pmksa->reauth_time < now.sec) {
- wpa_printf(MSG_DEBUG,
- "RSN: Do not allow PMKSA cache entry for "
- MACSTR
- " to be used for SAE since its reauth threshold has passed",
- MAC2STR(sm->cur_pmksa->aa));
- sm->cur_pmksa = NULL;
- return -1;
+ /* Driver-based roaming might have used a PMKSA entry
+ * that is already past the reauthentication threshold.
+ * Remove the related PMKID from the driver to avoid
+ * further uses for this PMKSA, but allow the
+ * association to continue since the PMKSA has not yet
+ * expired. */
+ wpa_sm_remove_pmkid(sm, sm->cur_pmksa->network_ctx,
+ sm->cur_pmksa->aa,
+ sm->cur_pmksa->pmkid, NULL);
+ if (associated) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Associated with " MACSTR
+ " using reauth threshold passed PMKSA cache entry",
+ MAC2STR(sm->cur_pmksa->aa));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Do not allow PMKSA cache entry for "
+ MACSTR
+ " to be used for SAE since its reauth threshold has passed",
+ MAC2STR(sm->cur_pmksa->aa));
+ sm->cur_pmksa = NULL;
+ return -1;
+ }
}
wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 48c9e04..6ba48f7 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -17,6 +17,8 @@
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
+ u8 kck[WPA_KCK_MAX_LEN];
+ size_t kck_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 aa[ETH_ALEN];
@@ -45,6 +47,13 @@
void *network_ctx;
int opportunistic;
bool external;
+
+ /**
+ * This field is used to avoid duplicate pmksa_cache_reauth() calls for
+ * every 10 minutes during the periodic expiration check of the current
+ * PMKSA for SAE.
+ */
+ bool sae_reauth_scheduled;
};
struct rsn_pmksa_cache;
@@ -86,7 +95,7 @@
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
int try_opportunistic, const u8 *fils_cache_id,
- int akmp);
+ int akmp, bool associated);
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
void *network_ctx, const u8 *aa, int akmp);
@@ -164,7 +173,7 @@
void *network_ctx,
int try_opportunistic,
const u8 *fils_cache_id,
- int akmp)
+ int akmp, bool associated)
{
return -1;
}
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 8f86820..1a28884 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -54,7 +54,8 @@
return !!(akmp & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_IEEE8021X_SHA384));
}
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 1531f51..e6f5877 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -139,6 +139,8 @@
struct ieee80211_he_capabilities *he_capabilities;
size_t he_capab_len;
struct ieee80211_he_6ghz_band_cap *he_6ghz_band_capabilities;
+ struct ieee80211_eht_capabilities *eht_capabilities;
+ size_t eht_capab_len;
u8 qos_info;
@@ -157,9 +159,19 @@
/* channel switch currently enabled */
int chan_switch_enabled;
+
+ int mld_link_id;
};
+static const u8 * wpa_tdls_get_link_bssid(struct wpa_sm *sm, int link_id)
+{
+ if (link_id >= 0)
+ return sm->mlo.links[link_id].bssid;
+ return sm->bssid;
+}
+
+
static int wpa_tdls_get_privacy(struct wpa_sm *sm)
{
/*
@@ -245,17 +257,19 @@
static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
- int initiator, const u8 *buf, size_t len)
+ int initiator, const u8 *buf, size_t len,
+ int link_id)
{
return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
status_code, peer_capab, initiator, buf,
- len);
+ len, link_id);
}
static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
u8 dialog_token, u16 status_code, u32 peer_capab,
- int initiator, const u8 *msg, size_t msg_len)
+ int initiator, const u8 *msg, size_t msg_len,
+ int link_id)
{
struct wpa_tdls_peer *peer;
@@ -267,7 +281,7 @@
if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
status_code, peer_capab, initiator, msg,
- msg_len)) {
+ msg_len, link_id)) {
wpa_printf(MSG_INFO, "TDLS: Failed to send message "
"(action_code=%u)", action_code);
return -1;
@@ -364,7 +378,7 @@
peer->sm_tmr.peer_capab,
peer->initiator,
peer->sm_tmr.buf,
- peer->sm_tmr.buf_len)) {
+ peer->sm_tmr.buf_len, -1)) {
wpa_printf(MSG_INFO, "TDLS: Failed to retry "
"transmission");
}
@@ -716,6 +730,8 @@
peer->he_capabilities = NULL;
os_free(peer->he_6ghz_band_capabilities);
peer->he_6ghz_band_capabilities = NULL;
+ os_free(peer->eht_capabilities);
+ peer->eht_capabilities = NULL;
os_free(peer->ext_capab);
peer->ext_capab = NULL;
os_free(peer->supp_channels);
@@ -731,6 +747,7 @@
os_memset(&peer->tpk, 0, sizeof(peer->tpk));
os_memset(peer->inonce, 0, WPA_NONCE_LEN);
os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
+ peer->mld_link_id = -1;
}
@@ -747,7 +764,8 @@
{
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = 3 * ETH_ALEN;
- os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
+ os_memcpy(lnkid->bssid, wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
+ ETH_ALEN);
if (peer->initiator) {
os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
@@ -846,7 +864,8 @@
/* request driver to send Teardown using this FTIE */
wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
- reason_code, 0, peer->initiator, rbuf, pos - rbuf);
+ reason_code, 0, peer->initiator, rbuf, pos - rbuf,
+ -1);
os_free(rbuf);
return 0;
@@ -1041,7 +1060,7 @@
" (action=%u status=%u)",
MAC2STR(dst), tdls_action, status);
return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
- 0, initiator, NULL, 0);
+ 0, initiator, NULL, 0, -1);
}
@@ -1068,6 +1087,7 @@
return NULL;
os_memcpy(peer->addr, addr, ETH_ALEN);
+ peer->mld_link_id = -1;
peer->next = sm->tdls;
sm->tdls = peer;
@@ -1249,7 +1269,8 @@
MAC2STR(peer->addr));
status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
- 1, 0, 0, peer->initiator, rbuf, pos - rbuf);
+ 1, 0, 0, peer->initiator, rbuf, pos - rbuf,
+ -1);
os_free(rbuf);
return status;
@@ -1341,7 +1362,7 @@
skip_ies:
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
dtoken, 0, 0, peer->initiator, rbuf,
- pos - rbuf);
+ pos - rbuf, -1);
os_free(rbuf);
return status;
@@ -1442,7 +1463,7 @@
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
dtoken, 0, peer_capab, peer->initiator,
- rbuf, pos - rbuf);
+ rbuf, pos - rbuf, -1);
os_free(rbuf);
return status;
@@ -1451,7 +1472,7 @@
static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
struct wpa_tdls_peer *peer,
- u8 dialog_token)
+ u8 dialog_token, int link_id)
{
size_t buf_len = 0;
struct wpa_tdls_timeoutie timeoutie;
@@ -1528,13 +1549,46 @@
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
skip_ies:
status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
- dialog_token, 0, 0, 0, rbuf, pos - rbuf);
+ dialog_token, 0, 0, 0, rbuf, pos - rbuf,
+ link_id);
os_free(rbuf);
return status;
}
+static bool wpa_tdls_is_lnkid_bss_valid(struct wpa_sm *sm,
+ const struct wpa_tdls_lnkid *lnkid,
+ int *link_id)
+{
+ *link_id = -1;
+
+ if (!sm->mlo.valid_links) {
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0)
+ return false;
+ } else {
+ int i;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if ((sm->mlo.valid_links & BIT(i)) &&
+ os_memcmp(lnkid->bssid, sm->mlo.links[i].bssid,
+ ETH_ALEN) == 0) {
+ *link_id = i;
+ break;
+ }
+ }
+ if (*link_id < 0) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: MLD link not found for linkid BSS "
+ MACSTR, MAC2STR(lnkid->bssid));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
static int
wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
const u8 *buf, size_t len)
@@ -1545,6 +1599,7 @@
size_t min_req_len = sizeof(struct wpa_tdls_frame) +
1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
u8 dialog_token;
+ int link_id = -1;
wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
MAC2STR(addr));
@@ -1577,17 +1632,19 @@
lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
- " BSS " MACSTR, MAC2STR(lnkid->bssid));
- return -1;
+ if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Discovery Request from different BSS "
+ MACSTR, MAC2STR(lnkid->bssid));
+ return -1;
}
peer = wpa_tdls_add_peer(sm, addr, NULL);
if (peer == NULL)
return -1;
- return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
+ return wpa_tdls_send_discovery_response(sm, peer, dialog_token,
+ link_id);
}
@@ -1599,7 +1656,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
MACSTR, MAC2STR(addr));
return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
- 1, 0, 0, 1, NULL, 0);
+ 1, 0, 0, 1, NULL, 0, -1);
}
@@ -1745,6 +1802,29 @@
}
+static int copy_peer_eht_capab(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->eht_capabilities) {
+ wpa_printf(MSG_DEBUG, "TDLS: No EHT capabilities received");
+ return 0;
+ }
+
+ os_free(peer->eht_capabilities);
+ peer->eht_capab_len = 0;
+ peer->eht_capabilities = os_memdup(kde->eht_capabilities,
+ kde->eht_capab_len);
+ if (!peer->eht_capabilities)
+ return -1;
+
+ peer->eht_capab_len = kde->eht_capab_len;
+ wpa_hexdump(MSG_DEBUG, "TDLS: Peer EHT capabilities",
+ peer->eht_capabilities, peer->eht_capab_len);
+
+ return 0;
+}
+
+
static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
@@ -1838,7 +1918,10 @@
peer->supp_channels,
peer->supp_channels_len,
peer->supp_oper_classes,
- peer->supp_oper_classes_len);
+ peer->supp_oper_classes_len,
+ peer->eht_capabilities,
+ peer->eht_capab_len,
+ peer->mld_link_id);
}
@@ -1878,6 +1961,7 @@
u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
int tdls_prohibited = sm->tdls_prohibited;
int existing_peer = 0;
+ int link_id = -1;
if (len < 3 + 3)
return -1;
@@ -1953,12 +2037,15 @@
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
+
+ if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS "
+ MACSTR, MAC2STR(lnkid->bssid));
status = WLAN_STATUS_REQUEST_DECLINED;
goto error;
}
+ peer->mld_link_id = link_id;
wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
MAC2STR(src_addr));
@@ -1973,6 +2060,9 @@
copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
goto error;
+ if (copy_peer_eht_capab(&kde, peer) < 0)
+ goto error;
+
if (copy_peer_ext_capab(&kde, peer) < 0)
goto error;
@@ -2000,7 +2090,7 @@
peer->initiator = 1;
wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
NULL, NULL, 0, NULL, 0, 0, NULL, 0,
- NULL, 0, NULL, 0);
+ NULL, 0, NULL, 0, NULL, 0, link_id);
if (wpa_tdls_send_tpk_m1(sm, peer) == -2) {
peer = NULL;
goto error;
@@ -2181,7 +2271,11 @@
peer->lifetime = lifetime;
- wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+ if (peer->mld_link_id >= 0)
+ wpa_printf(MSG_DEBUG, "TDLS: Use link ID %u for TPK derivation",
+ peer->mld_link_id);
+ wpa_tdls_generate_tpk(peer, sm->own_addr,
+ wpa_tdls_get_link_bssid(sm, peer->mld_link_id));
skip_rsn_check:
#ifdef CONFIG_TDLS_TESTING
@@ -2366,7 +2460,8 @@
kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ if (os_memcmp(sm->bssid, wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
+ ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
status = WLAN_STATUS_NOT_IN_SAME_BSS;
goto error;
@@ -2383,6 +2478,9 @@
copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
goto error;
+ if (copy_peer_eht_capab(&kde, peer) < 0)
+ goto error;
+
if (copy_peer_ext_capab(&kde, peer) < 0)
goto error;
@@ -2490,7 +2588,11 @@
goto error;
}
- wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+ if (peer->mld_link_id >= 0)
+ wpa_printf(MSG_DEBUG, "TDLS: Use link ID %u for TPK derivation",
+ peer->mld_link_id);
+ wpa_tdls_generate_tpk(peer, sm->own_addr,
+ wpa_tdls_get_link_bssid(sm, peer->mld_link_id));
/* Process MIC check to see if TPK M2 is right */
if (wpa_supplicant_verify_tdls_mic(2, peer, (const u8 *) lnkid,
@@ -2611,7 +2713,8 @@
(u8 *) kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ if (os_memcmp(wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
+ lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
goto error;
}
@@ -2770,7 +2873,7 @@
/* add the peer to the driver as a "setup in progress" peer */
if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
NULL, NULL, 0, NULL, 0, 0, NULL, 0, NULL, 0,
- NULL, 0)) {
+ NULL, 0, NULL, 0, peer->mld_link_id)) {
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
@@ -3080,6 +3183,63 @@
}
+int wpa_tdls_process_discovery_response(struct wpa_sm *sm, const u8 *addr,
+ const u8 *buf, size_t len)
+{
+ struct ieee802_11_elems elems;
+ struct wpa_tdls_lnkid lnkid;
+ struct wpa_tdls_peer *peer;
+ size_t min_req_len = 1 /* Dialog Token */ + 2 /* Capability */ +
+ sizeof(struct wpa_tdls_lnkid);
+ int link_id = -1;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Process Discovery Response from " MACSTR,
+ MAC2STR(addr));
+
+ if (len < min_req_len) {
+ wpa_printf(MSG_DEBUG, "TDLS Discovery Resp is too short: %zu",
+ len);
+ return -1;
+ }
+
+ /* Elements start after the three octets of fixed field (one octet for
+ * the Dialog Token field and two octets for the Capability field. */
+ if (ieee802_11_parse_elems(buf + 3, len - 3, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Failed to parse IEs in Discovery Response");
+ return -1;
+ }
+
+ if (!elems.link_id) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Link Identifier element not found in Discovery Response");
+ return -1;
+ }
+
+ os_memcpy(&lnkid.bssid[0], elems.link_id, sizeof(lnkid) - 2);
+
+ if (!wpa_tdls_is_lnkid_bss_valid(sm, &lnkid, &link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Discovery Response from different BSS "
+ MACSTR, MAC2STR(lnkid.bssid));
+ return -1;
+ }
+
+ peer = wpa_tdls_add_peer(sm, addr, NULL);
+ if (!peer) {
+ wpa_printf(MSG_DEBUG, "TDLS: Could not add peer entry");
+ return -1;
+ }
+
+ peer->mld_link_id = link_id;
+ wpa_printf(MSG_DEBUG, "TDLS: Link identifier BSS: " MACSTR
+ " , link id: %u", MAC2STR(lnkid.bssid), link_id);
+
+ return 0;
+}
+
+
int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
u8 oper_class,
struct hostapd_freq_params *freq_params)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5a28ef5..9f49cf9 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -223,7 +223,7 @@
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
int key_info, ver;
- u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
+ u8 *rbuf, *key_mic, *mic;
if (pairwise && sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
wpa_sm_get_state(sm) == WPA_COMPLETED && !error) {
@@ -243,12 +243,6 @@
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
- if (wpa_sm_get_bssid(sm, bssid) < 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "Failed to read BSSID for EAPOL-Key request");
- return;
- }
-
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
@@ -285,8 +279,8 @@
"WPA: Sending EAPOL-Key Request (error=%d "
"pairwise=%d ptk_set=%d len=%lu)",
error, pairwise, sm->ptk_set, (unsigned long) rlen);
- wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
- key_mic);
+ wpa_eapol_key_send(sm, &sm->ptk, ver, wpa_sm_get_auth_addr(sm),
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
}
@@ -2745,8 +2739,8 @@
#endif /* CONFIG_OCV */
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
- return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
- rbuf, rlen, key_mic);
+ return wpa_eapol_key_send(sm, &sm->ptk, ver, wpa_sm_get_auth_addr(sm),
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
}
@@ -3829,6 +3823,8 @@
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ case WPA_KEY_MGMT_IEEE8021X_SHA384:
+ return RSN_AUTH_KEY_MGMT_802_1X_SHA384;
default:
return 0;
}
@@ -4074,6 +4070,8 @@
os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+ os_memset(&sm->bigtk, 0, sizeof(sm->bigtk));
+ os_memset(&sm->bigtk_wnm_sleep, 0, sizeof(sm->bigtk_wnm_sleep));
sm->tk_set = false;
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
os_memset(&sm->mlo.links[i].gtk, 0,
@@ -4084,6 +4082,10 @@
sizeof(sm->mlo.links[i].igtk));
os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0,
sizeof(sm->mlo.links[i].igtk_wnm_sleep));
+ os_memset(&sm->mlo.links[i].bigtk, 0,
+ sizeof(sm->mlo.links[i].bigtk));
+ os_memset(&sm->mlo.links[i].bigtk_wnm_sleep, 0,
+ sizeof(sm->mlo.links[i].bigtk_wnm_sleep));
}
}
@@ -5414,6 +5416,11 @@
const u8 *g_ap = NULL;
size_t g_ap_len = 0, kdk_len;
struct wpabuf *pub = NULL;
+#ifdef CONFIG_IEEE80211R
+ struct wpa_ft_ies parse;
+
+ os_memset(&parse, 0, sizeof(parse));
+#endif /* CONFIG_IEEE80211R */
os_memcpy(sm->bssid, bssid, ETH_ALEN);
@@ -5492,15 +5499,13 @@
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
- struct wpa_ft_ies parse;
-
if (!elems.mdie || !elems.ftie) {
wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
goto fail;
}
if (wpa_ft_parse_ies(pos, end - pos, &parse,
- sm->key_mgmt) < 0) {
+ sm->key_mgmt, false) < 0) {
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
goto fail;
}
@@ -5704,10 +5709,16 @@
&sm->fils_key_auth_len);
wpabuf_free(pub);
forced_memzero(ick, sizeof(ick));
+#ifdef CONFIG_IEEE80211R
+ wpa_ft_parse_ies_free(&parse);
+#endif /* CONFIG_IEEE80211R */
return res;
fail:
wpabuf_free(pub);
wpabuf_clear_free(dh_ss);
+#ifdef CONFIG_IEEE80211R
+ wpa_ft_parse_ies_free(&parse);
+#endif /* CONFIG_IEEE80211R */
return -1;
}
@@ -6538,3 +6549,11 @@
if (sm)
sm->cur_pmksa = entry;
}
+
+
+void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+ bool driver_bss_selection)
+{
+ if (sm)
+ sm->driver_bss_selection = driver_bss_selection;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 47d2344..d8d0a15 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -64,7 +64,8 @@
int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
- int initiator, const u8 *buf, size_t len);
+ int initiator, const u8 *buf, size_t len,
+ int link_id);
int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid,
u16 capability, const u8 *supp_rates,
@@ -78,7 +79,9 @@
size_t ext_capab_len, const u8 *supp_channels,
size_t supp_channels_len,
const u8 *supp_oper_classes,
- size_t supp_oper_classes_len);
+ size_t supp_oper_classes_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len, int mld_link_id);
int (*tdls_enable_channel_switch)(
void *ctx, const u8 *addr, u8 oper_class,
const struct hostapd_freq_params *params);
@@ -593,6 +596,8 @@
u8 oper_class,
struct hostapd_freq_params *freq_params);
int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_process_discovery_response(struct wpa_sm *sm, const u8 *addr,
+ const u8 *buf, size_t len);
#ifdef CONFIG_TDLS_TESTING
extern unsigned int tdls_testing;
#endif /* CONFIG_TDLS_TESTING */
@@ -626,5 +631,7 @@
void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
struct rsn_pmksa_cache_entry *entry);
const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
+void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+ bool driver_bss_selection);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 56a30c8..3d1dbc6 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -127,11 +127,13 @@
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &ft, sm->key_mgmt) < 0)
+ if (wpa_ft_parse_ies(ies, ies_len, &ft, sm->key_mgmt, false) < 0)
return -1;
- if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
+ if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+ wpa_ft_parse_ies_free(&ft);
return -1;
+ }
wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
ft.mdie, MOBILITY_DOMAIN_ID_LEN);
@@ -179,6 +181,7 @@
sm->assoc_resp_ies, sm->assoc_resp_ies_len);
}
+ wpa_ft_parse_ies_free(&ft);
return 0;
}
@@ -472,6 +475,7 @@
ftie_pos, 2 + *ftie_len,
(u8 *) rsnie, 2 + rsnie->len, ric_ies,
ric_ies_len, rsnxe_len ? rsnxe : NULL, rsnxe_len,
+ NULL,
fte_mic) < 0) {
wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
os_free(buf);
@@ -586,11 +590,13 @@
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
u8 ptk_name[WPA_PMK_NAME_LEN];
- int ret;
+ int ret = -1, res;
const u8 *bssid;
const u8 *kck;
size_t kck_len, kdk_len;
+ os_memset(&parse, 0, sizeof(parse));
+
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
@@ -598,26 +604,27 @@
if (!sm->over_the_ds_in_progress) {
wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
"- drop FT Action Response");
- return -1;
+ goto fail;
}
if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
"with this Target AP - drop FT Action "
"Response");
- return -1;
+ goto fail;
}
}
if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
"enabled for this connection");
- return -1;
+ goto fail;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt,
+ !ft_action) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
- return -1;
+ goto fail;
}
mdie = (struct rsn_mdie *) parse.mdie;
@@ -625,12 +632,12 @@
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- return -1;
+ goto fail;
}
if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTE");
- return -1;
+ goto fail;
}
if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
@@ -639,12 +646,12 @@
parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
- return -1;
+ goto fail;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- return -1;
+ goto fail;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -656,12 +663,12 @@
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- return -1;
+ goto fail;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- return -1;
+ goto fail;
}
if (parse.rsn_pmkid == NULL ||
@@ -669,13 +676,13 @@
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
"RSNIE");
- return -1;
+ goto fail;
}
if (sm->mfp == 2 && !(parse.rsn_capab & WPA_CAPABILITY_MFPC)) {
wpa_printf(MSG_INFO,
"FT: Target AP does not support PMF, but local configuration requires that");
- return -1;
+ goto fail;
}
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
@@ -686,7 +693,7 @@
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
- return -1;
+ goto fail;
sm->pmk_r1_len = sm->pmk_r0_len;
bssid = target_ap;
@@ -706,7 +713,7 @@
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher,
kdk_len) < 0)
- return -1;
+ goto fail;
os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN);
@@ -716,7 +723,7 @@
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
wpa_ltf_keyseed(&sm->ptk, sm->key_mgmt, sm->pairwise_cipher)) {
wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
- return -1;
+ goto fail;
}
#endif /* CONFIG_PASN */
@@ -740,8 +747,8 @@
}
wpa_sm_mark_authenticated(sm, bssid);
- ret = wpa_ft_install_ptk(sm, bssid);
- if (ret) {
+ res = wpa_ft_install_ptk(sm, bssid);
+ if (res) {
/*
* Some drivers do not support key configuration when we are
* not associated with the target AP. Work around this by
@@ -763,7 +770,10 @@
os_memcpy(sm->bssid, target_ap, ETH_ALEN);
}
- return 0;
+ ret = 0;
+fail:
+ wpa_ft_parse_ies_free(&parse);
+ return ret;
}
@@ -1037,13 +1047,16 @@
size_t kck_len;
int own_rsnxe_used;
size_t mic_len;
+ int ret = -1;
+
+ os_memset(&parse, 0, sizeof(parse));
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
"enabled for this connection");
- return -1;
+ goto fail;
}
if (sm->ft_reassoc_completed) {
@@ -1051,9 +1064,9 @@
return 0;
}
- if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt, true) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
- return -1;
+ goto fail;
}
mdie = (struct rsn_mdie *) parse.mdie;
@@ -1061,7 +1074,7 @@
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- return -1;
+ goto fail;
}
if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
@@ -1079,7 +1092,7 @@
wpa_printf(MSG_DEBUG,
"FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
parse.fte_mic_len, mic_len);
- return -1;
+ goto fail;
}
if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
@@ -1088,7 +1101,7 @@
parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->snonce, WPA_NONCE_LEN);
- return -1;
+ goto fail;
}
if (os_memcmp(parse.fte_anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
@@ -1097,12 +1110,12 @@
parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->anonce, WPA_NONCE_LEN);
- return -1;
+ goto fail;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- return -1;
+ goto fail;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -1114,18 +1127,18 @@
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- return -1;
+ goto fail;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- return -1;
+ goto fail;
}
if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
"ReassocResp");
- return -1;
+ goto fail;
}
if (parse.rsn_pmkid == NULL ||
@@ -1133,7 +1146,7 @@
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
- return -1;
+ goto fail;
}
count = 3;
@@ -1145,7 +1158,7 @@
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
parse.fte_elem_count, count);
- return -1;
+ goto fail;
}
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
@@ -1163,9 +1176,10 @@
parse.ric, parse.ric_len,
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+ NULL,
mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
- return -1;
+ goto fail;
}
if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
@@ -1173,13 +1187,13 @@
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
parse.fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
- return -1;
+ goto fail;
}
if (parse.fte_rsnxe_used && !sm->ap_rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that AP uses RSNXE, but RSNXE was not included in Beacon/Probe Response frames");
- return -1;
+ goto fail;
}
if (!sm->ap_rsn_ie) {
@@ -1188,7 +1202,7 @@
if (wpa_sm_get_beacon_ie(sm) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"FT: Could not find AP from the scan results");
- return -1;
+ goto fail;
}
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
"FT: Found the current AP from updated scan results");
@@ -1206,7 +1220,7 @@
"RSNE in FT protocol Reassociation Response frame",
parse.rsn ? parse.rsn - 2 : NULL,
parse.rsn ? parse.rsn_len + 2 : 0);
- return -1;
+ goto fail;
}
own_rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) &&
@@ -1226,7 +1240,7 @@
"RSNXE in FT protocol Reassociation Response frame",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
- return -1;
+ goto fail;
}
#ifdef CONFIG_OCV
@@ -1236,7 +1250,7 @@
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in (Re)Assoc Response");
- return -1;
+ goto fail;
}
if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
@@ -1245,7 +1259,7 @@
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
"addr=" MACSTR " frame=ft-assoc error=%s",
MAC2STR(src_addr), ocv_errorstr);
- return -1;
+ goto fail;
}
}
#endif /* CONFIG_OCV */
@@ -1255,13 +1269,13 @@
if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 ||
wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 ||
wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0)
- return -1;
+ goto fail;
if (sm->set_ptk_after_assoc) {
wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
"are associated");
if (wpa_ft_install_ptk(sm, src_addr) < 0)
- return -1;
+ goto fail;
sm->set_ptk_after_assoc = 0;
}
@@ -1274,7 +1288,10 @@
wpa_printf(MSG_DEBUG, "FT: Completed successfully");
- return 0;
+ ret = 0;
+fail:
+ wpa_ft_parse_ies_free(&parse);
+ return ret;
}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 300ef54..5fe6182 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -222,6 +222,7 @@
struct wpa_sm_mlo mlo;
bool wmm_enabled;
+ bool driver_bss_selection;
};
@@ -380,13 +381,13 @@
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf,
- size_t len)
+ size_t len, int link_id)
{
if (sm->ctx->send_tdls_mgmt)
return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
dialog_token, status_code,
peer_capab, initiator, buf,
- len);
+ len, link_id);
return -1;
}
@@ -410,7 +411,9 @@
u8 qosinfo, int wmm, const u8 *ext_capab,
size_t ext_capab_len, const u8 *supp_channels,
size_t supp_channels_len, const u8 *supp_oper_classes,
- size_t supp_oper_classes_len)
+ size_t supp_oper_classes_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len, int mld_link_id)
{
if (sm->ctx->tdls_peer_addset)
return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
@@ -423,7 +426,9 @@
supp_channels,
supp_channels_len,
supp_oper_classes,
- supp_oper_classes_len);
+ supp_oper_classes_len,
+ eht_capab, eht_capab_len,
+ mld_link_id);
return -1;
}
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 2a6c79b..d1510aa 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -230,6 +230,10 @@
} else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SHA384
+ } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
+#endif /* CONFIG_SHA384 */
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
key_mgmt);
diff --git a/src/utils/common.h b/src/utils/common.h
index 435a9a8..bede21e 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -244,6 +244,18 @@
a[2] = val & 0xff;
}
+static inline u32 WPA_GET_LE24(const u8 *a)
+{
+ return (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE24(u8 *a, u32 val)
+{
+ a[2] = (val >> 16) & 0xff;
+ a[1] = (val >> 8) & 0xff;
+ a[0] = val & 0xff;
+}
+
static inline u32 WPA_GET_BE32(const u8 *a)
{
return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index eb1db80..88d72bd 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -127,6 +127,12 @@
WPA_PUT_LE16(pos, data);
}
+static inline void wpabuf_put_le24(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = (u8 *) wpabuf_put(buf, 3);
+ WPA_PUT_LE24(pos, data);
+}
+
static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
{
u8 *pos = (u8 *) wpabuf_put(buf, 4);
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index fd51635..d364430 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -599,10 +599,15 @@
u16 type, len;
#ifdef WPS_WORKAROUNDS
u16 prev_type = 0;
+ size_t last_nonzero = 0;
+ const u8 *start;
#endif /* WPS_WORKAROUNDS */
os_memset(attr, 0, sizeof(*attr));
pos = wpabuf_head(msg);
+#ifdef WPS_WORKAROUNDS
+ start = pos;
+#endif /* WPS_WORKAROUNDS */
end = pos + wpabuf_len(msg);
while (pos < end) {
@@ -649,9 +654,15 @@
* end of M1. Skip those to avoid interop issues.
*/
int i;
+
+ if (last_nonzero > (size_t) (pos - start))
+ continue;
+
for (i = 0; i < end - pos; i++) {
- if (pos[i])
+ if (pos[i]) {
+ last_nonzero = pos - start + i;
break;
+ }
}
if (i == end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c682f73..8c417b8 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -449,6 +449,34 @@
CFLAGS += -DCONFIG_NO_ROAMING
endif
+ifdef CONFIG_OPENSC_ENGINE_PATH
+CFLAGS += -DCONFIG_OPENSC_ENGINE_PATH=\"$(CONFIG_OPENSC_ENGINE_PATH)\"
+endif
+
+ifdef CONFIG_NO_OPENSC_ENGINE_PATH
+CFLAGS += -DCONFIG_OPENSC_ENGINE_PATH=NULL
+endif
+
+ifdef CONFIG_PKCS11_ENGINE_PATH
+CFLAGS += -DCONFIG_PKCS11_ENGINE_PATH=\"$(CONFIG_PKCS11_ENGINE_PATH)\"
+endif
+
+ifdef CONFIG_NO_PKCS11_ENGINE_PATH
+CFLAGS += -DCONFIG_PKCS11_ENGINE_PATH=NULL
+endif
+
+ifdef CONFIG_PKCS11_MODULE_PATH
+CFLAGS += -DCONFIG_PKCS11_MODULE_PATH=\"$(CONFIG_PKCS11_MODULE_PATH)\"
+endif
+
+ifdef CONFIG_NO_PKCS11_MODULE_PATH
+CFLAGS += -DCONFIG_PKCS11_MODULE_PATH=NULL
+endif
+
+ifdef CONFIG_NO_LOAD_DYNAMIC_EAP
+CFLAGS += -DCONFIG_NO_LOAD_DYNAMIC_EAP
+endif
+
include ../src/drivers/drivers.mak
ifdef CONFIG_AP
OBJS_d += $(DRV_BOTH_OBJS)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 62d2e90..1b94fe5 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -314,8 +314,20 @@
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_ht)
ssid->ht = 0;
+ if (ssid->disable_ht40)
+ ssid->ht40 = 0;
#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+ if (ssid->disable_vht)
+ ssid->vht = 0;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_HE_OVERRIDES
+ if (ssid->disable_he)
+ ssid->he = 0;
+#endif /* CONFIG_HE_OVERRIDES */
+
if (!ssid->ht) {
wpa_printf(MSG_DEBUG,
"HT not enabled in network profile");
@@ -1048,6 +1060,7 @@
return -1;
hapd_iface->owner = wpa_s;
hapd_iface->drv_flags = wpa_s->drv_flags;
+ hapd_iface->drv_flags2 = wpa_s->drv_flags2;
hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
hapd_iface->extended_capa = wpa_s->extended_capa;
hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index 75bdec1..9830c4a 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -305,7 +305,7 @@
}
wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
- if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, false)) {
wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
eloop_register_timeout(data->scan_interval, 0,
bgscan_learn_timeout, data, NULL);
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 5a8f97c..f398b85 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -49,7 +49,7 @@
*/
wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan");
- if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, false)) {
wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
eloop_register_timeout(data->scan_interval, 0,
bgscan_simple_timeout, data, NULL);
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 3204414..c1be660 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -183,9 +183,8 @@
}
-static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s,
- struct wpa_bss *old_bss,
- struct wpa_bss *new_bss)
+static struct wpa_connect_work *
+wpa_bss_check_pending_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
struct wpa_radio_work *work;
struct wpa_connect_work *cwork;
@@ -194,12 +193,19 @@
if (!work)
work = radio_work_pending(wpa_s, "connect");
if (!work)
- return;
+ return NULL;
cwork = work->ctx;
- if (cwork->bss != old_bss)
- return;
+ if (cwork->bss != bss)
+ return NULL;
+ return cwork;
+}
+
+
+static void wpa_bss_update_pending_connect(struct wpa_connect_work *cwork,
+ struct wpa_bss *new_bss)
+{
wpa_printf(MSG_DEBUG,
"Update BSS pointer for the pending connect radio work");
cwork->bss = new_bss;
@@ -211,6 +217,8 @@
void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const char *reason)
{
+ struct wpa_connect_work *cwork;
+
if (wpa_s->last_scan_res) {
unsigned int i;
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
@@ -224,7 +232,9 @@
}
}
}
- wpa_bss_update_pending_connect(wpa_s, bss, NULL);
+ cwork = wpa_bss_check_pending_connect(wpa_s, bss);
+ if (cwork)
+ wpa_bss_update_pending_connect(cwork, NULL);
dl_list_del(&bss->list);
dl_list_del(&bss->list_id);
wpa_s->num_bss--;
@@ -286,6 +296,7 @@
dst->flags = src->flags;
os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
dst->freq = src->freq;
+ dst->max_cw = src->max_cw;
dst->beacon_int = src->beacon_int;
dst->caps = src->caps;
dst->qual = src->qual;
@@ -385,6 +396,9 @@
if (bss == wpa_s->current_bss)
return 1;
+ if (bss == wpa_s->ml_connect_probe_bss)
+ return 1;
+
if (wpa_s->current_bss &&
(bss->ssid_len != wpa_s->current_bss->ssid_len ||
os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
@@ -725,20 +739,34 @@
} else {
struct wpa_bss *nbss;
struct dl_list *prev = bss->list_id.prev;
+ struct wpa_connect_work *cwork;
+ unsigned int i;
+ bool update_current_bss = wpa_s->current_bss == bss;
+ bool update_ml_probe_bss = wpa_s->ml_connect_probe_bss == bss;
+
+ cwork = wpa_bss_check_pending_connect(wpa_s, bss);
+
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpa_s->last_scan_res[i] == bss)
+ break;
+ }
+
dl_list_del(&bss->list_id);
nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
res->beacon_ie_len);
if (nbss) {
- unsigned int i;
- for (i = 0; i < wpa_s->last_scan_res_used; i++) {
- if (wpa_s->last_scan_res[i] == bss) {
- wpa_s->last_scan_res[i] = nbss;
- break;
- }
- }
- if (wpa_s->current_bss == bss)
+ if (i != wpa_s->last_scan_res_used)
+ wpa_s->last_scan_res[i] = nbss;
+
+ if (update_current_bss)
wpa_s->current_bss = nbss;
- wpa_bss_update_pending_connect(wpa_s, bss, nbss);
+
+ if (update_ml_probe_bss)
+ wpa_s->ml_connect_probe_bss = nbss;
+
+ if (cwork)
+ wpa_bss_update_pending_connect(cwork, nbss);
+
bss = nbss;
os_memcpy(bss->ies, res + 1,
res->ie_len + res->beacon_ie_len);
@@ -1194,6 +1222,22 @@
/**
+ * wpa_bss_get_ie_nth - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * @nth: Return the nth element of the requested type (2 returns the second)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the nth matching information element in the BSS
+ * entry.
+ */
+const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth)
+{
+ return get_ie_nth(wpa_bss_ie_ptr(bss), bss->ie_len, ie, nth);
+}
+
+
+/**
* wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
* @bss: BSS table entry
* @ext: Information element extension identifier (WLAN_EID_EXT_*)
@@ -1468,3 +1512,315 @@
return ieee802_11_defrag_mle(&elems, type);
}
+
+
+static void
+wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, u8 mbssid_idx,
+ const struct ieee80211_neighbor_ap_info *ap_info,
+ size_t len, u16 *seen, u16 *missing)
+{
+ const u8 *pos, *end;
+ const u8 *mld_params;
+ u8 count, mld_params_offset;
+ u8 i, type, link_id;
+
+ count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
+ type = ap_info->tbtt_info_hdr & RNR_TBTT_INFO_HDR_TYPE_MSK;
+
+ /* MLD information is at offset 13 or at start */
+ if (type == 0 && ap_info->tbtt_info_len >= RNR_TBTT_INFO_MLD_LEN) {
+ /* MLD info is appended */
+ mld_params_offset = RNR_TBTT_INFO_LEN;
+ } else {
+ /* TODO: Support NSTR AP */
+ return;
+ }
+
+ pos = (const u8 *) ap_info;
+ end = pos + len;
+ pos += sizeof(*ap_info);
+
+ for (i = 0; i < count; i++) {
+ if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
+ return;
+
+ if (end - pos < ap_info->tbtt_info_len)
+ break;
+
+ mld_params = pos + mld_params_offset;
+
+ link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
+
+ if (*mld_params != mbssid_idx) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link not part of MLD");
+ } else if (!(BIT(link_id) & *seen)) {
+ struct wpa_bss *neigh_bss =
+ wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
+
+ *seen |= BIT(link_id);
+ wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+ *mld_params, link_id);
+
+ if (neigh_bss) {
+ struct mld_link *l;
+
+ l = &bss->mld_links[bss->n_mld_links];
+ l->link_id = link_id;
+ os_memcpy(l->bssid, ap_info->data + 1,
+ ETH_ALEN);
+ l->freq = neigh_bss->freq;
+ bss->n_mld_links++;
+ } else {
+ *missing |= BIT(link_id);
+ }
+ }
+
+ pos += ap_info->tbtt_info_len;
+ }
+}
+
+
+/**
+ * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: BSS table entry
+ * @mld_addr: AP MLD address (or %NULL)
+ * @link_info: Array to store link information (or %NULL),
+ * should be initialized and #MAX_NUM_MLD_LINKS elements long
+ * @missing_links: Result bitmask of links that were not discovered (or %NULL)
+ * Returns: 0 on success or -1 for non-MLD or parsing failures
+ *
+ * Parses the Basic Multi-Link element of the BSS into @link_info using the scan
+ * information stored in the wpa_supplicant data to fill in information for
+ * links where possible. The @missing_links out parameter will contain any links
+ * for which no corresponding BSS was found.
+ */
+int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ u8 *ap_mld_addr,
+ u16 *missing_links)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *mlbuf;
+ const struct element *elem;
+ u8 mbssid_idx = 0;
+ u8 ml_ie_len;
+ const struct ieee80211_eht_ml *eht_ml;
+ const struct eht_ml_basic_common_info *ml_basic_common_info;
+ u8 i, link_id;
+ const u16 control_mask =
+ MULTI_LINK_CONTROL_TYPE_MASK |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+ const u16 control =
+ MULTI_LINK_CONTROL_TYPE_BASIC |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+ u16 missing = 0;
+ u16 seen;
+ const u8 *ies_pos = wpa_bss_ie_ptr(bss);
+ size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+ int ret = -1;
+ struct mld_link *l;
+
+ if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) ==
+ ParseFailed) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements");
+ return ret;
+ }
+
+ mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No Multi-Link element");
+ return ret;
+ }
+
+ ml_ie_len = wpabuf_len(mlbuf);
+
+ /*
+ * for ext ID + 2 control + common info len + MLD address +
+ * link info
+ */
+ if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL)
+ goto out;
+
+ eht_ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ if ((le_to_host16(eht_ml->ml_control) & control_mask) != control) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Multi-Link element control=0x%x (mask 0x%x expected 0x%x)",
+ le_to_host16(eht_ml->ml_control), control_mask,
+ control);
+ goto out;
+ }
+
+ ml_basic_common_info =
+ (const struct eht_ml_basic_common_info *) eht_ml->variable;
+
+ /* Common info length should be valid */
+ if (ml_basic_common_info->len < ETH_ALEN + 1UL)
+ goto out;
+
+ /* Get the MLD address and MLD link ID */
+ if (ap_mld_addr)
+ os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
+ ETH_ALEN);
+
+ bss->n_mld_links = 0;
+ l = &bss->mld_links[bss->n_mld_links];
+ link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
+ l->link_id = link_id;
+ os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
+ l->freq = bss->freq;
+
+ seen = BIT(link_id);
+ bss->n_mld_links++;
+
+ /*
+ * The AP MLD ID in the RNR corresponds to the MBSSID index, see
+ * IEEE P802.11be/D4.0, 9.4.2.169.2 (Neighbor AP Information field).
+ *
+ * For the transmitting BSSID it is clear that both the MBSSID index
+ * and the AP MLD ID in the RNR are zero.
+ *
+ * For nontransmitted BSSIDs we will have a BSS generated from the
+ * MBSSID element(s) using inheritance rules. Included in the elements
+ * is the MBSSID Index Element. The RNR is copied from the Beacon/Probe
+ * Response frame that was send by the transmitting BSSID. As such, the
+ * reported AP MLD ID in the RNR will match the value in the MBSSID
+ * Index Element.
+ */
+ elem = (const struct element *)
+ wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX);
+ if (elem && elem->datalen >= 1)
+ mbssid_idx = elem->data[0];
+
+ for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
+ wpa_bss_ie_ptr(bss),
+ bss->ie_len ? bss->ie_len : bss->beacon_ie_len) {
+ const struct ieee80211_neighbor_ap_info *ap_info;
+ const u8 *pos = elem->data;
+ size_t len = elem->datalen;
+
+ /* RNR IE may contain more than one Neighbor AP Info */
+ while (sizeof(*ap_info) <= len) {
+ size_t ap_info_len = sizeof(*ap_info);
+ u8 count;
+
+ ap_info = (const struct ieee80211_neighbor_ap_info *)
+ pos;
+ count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
+ ap_info_len += count * ap_info->tbtt_info_len;
+
+ if (ap_info_len > len)
+ goto out;
+
+ wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
+ ap_info, len, &seen,
+ &missing);
+
+ pos += ap_info_len;
+ len -= ap_info_len;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
+ bss->n_mld_links, missing);
+
+ for (i = 0; i < bss->n_mld_links; i++) {
+ wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
+ bss->mld_links[i].link_id,
+ MAC2STR(bss->mld_links[i].bssid));
+ }
+
+ if (missing_links)
+ *missing_links = missing;
+
+ ret = 0;
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
+/*
+ * wpa_bss_parse_reconf_ml_element - Parse the Reconfiguration ML element
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: BSS table entry
+ * Returns: The bitmap of links that are going to be removed
+ */
+u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *mlbuf;
+ const u8 *pos = wpa_bss_ie_ptr(bss);
+ size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+ const struct ieee80211_eht_ml *ml;
+ u16 removed_links = 0;
+ u8 ml_common_len;
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return 0;
+
+ if (!elems.reconf_mle || !elems.reconf_mle_len)
+ return 0;
+
+ mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_RECONF);
+ if (!mlbuf)
+ return 0;
+
+ ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ len = wpabuf_len(mlbuf);
+
+ if (len < sizeof(*ml))
+ goto out;
+
+ ml_common_len = 1;
+ if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
+ ml_common_len += ETH_ALEN;
+
+ if (len < sizeof(*ml) + ml_common_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)",
+ len, sizeof(*ml) + ml_common_len);
+ goto out;
+ }
+
+ pos = ml->variable + ml_common_len;
+ len -= sizeof(*ml) + ml_common_len;
+
+ while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) {
+ size_t sub_elem_len = *(pos + 1);
+
+ if (2 + sub_elem_len > len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid link info len: %zu %zu",
+ 2 + sub_elem_len, len);
+ goto out;
+ }
+
+ if (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
+ const struct ieee80211_eht_per_sta_profile *sta_prof =
+ (const struct ieee80211_eht_per_sta_profile *)
+ (pos + 2);
+ u16 control = le_to_host16(sta_prof->sta_control);
+ u8 link_id;
+
+ link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
+ removed_links |= BIT(link_id);
+ }
+
+ pos += 2 + sub_elem_len;
+ len -= 2 + sub_elem_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration: removed_links=0x%x",
+ removed_links);
+out:
+ wpabuf_free(mlbuf);
+ return removed_links;
+}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 611da88..042b5d2 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -96,6 +96,8 @@
size_t ssid_len;
/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
int freq;
+ /** The max channel width supported by both the AP and the STA */
+ enum chan_width max_cw;
/** Beacon interval in TUs (host byte order) */
u16 beacon_int;
/** Capability information field in host byte order */
@@ -124,6 +126,15 @@
size_t beacon_ie_len;
/** MLD address of the AP */
u8 mld_addr[ETH_ALEN];
+
+ /** An array of MLD links */
+ u8 n_mld_links;
+ struct mld_link {
+ u8 link_id;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ } mld_links[MAX_NUM_MLD_LINKS];
+
/* followed by ie_len octets of IEs */
/* followed by beacon_ie_len octets of IEs */
u8 ies[];
@@ -160,6 +171,7 @@
struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
unsigned int idf, unsigned int idl);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
+const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth);
const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
@@ -202,5 +214,11 @@
struct os_reltime *update_time);
struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type);
+int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ u8 *ap_mld_addr,
+ u16 *missing_links);
+u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss);
#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index c843cb0..d1d8ca3 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -793,6 +793,10 @@
val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ else if (os_strcmp(start, "WPA-EAP-SHA384") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
val |= WPA_KEY_MGMT_PSK_SHA256;
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
@@ -965,6 +969,18 @@
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA384",
+ pos == buf ? "" : " ");
+ if (os_snprintf_error(end - pos, ret)) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
+
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
pos == buf ? "" : " ");
@@ -3008,9 +3024,15 @@
wpabuf_free(config->wps_vendor_ext[i]);
os_free(config->ctrl_interface);
os_free(config->ctrl_interface_group);
+#ifndef CONFIG_OPENSC_ENGINE_PATH
os_free(config->opensc_engine_path);
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
os_free(config->pkcs11_engine_path);
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
os_free(config->pkcs11_module_path);
+#endif /* CONFIG_PKCS11_MODULE_PATH */
os_free(config->openssl_ciphers);
os_free(config->pcsc_reader);
str_clear_free(config->pcsc_pin);
@@ -4933,6 +4955,7 @@
}
+#ifndef CONFIG_NO_LOAD_DYNAMIC_EAP
static int wpa_config_process_load_dynamic_eap(
const struct global_parse_data *data, struct wpa_config *config,
int line, const char *so)
@@ -4951,6 +4974,7 @@
return 0;
}
+#endif /* CONFIG_NO_LOAD_DYNAMIC_EAP */
#ifdef CONFIG_WPS
@@ -5331,9 +5355,15 @@
#endif /* CONFIG_MESH */
{ INT(disable_scan_offload), 0 },
{ INT(fast_reauth), 0 },
+#ifndef CONFIG_OPENSC_ENGINE_PATH
{ STR(opensc_engine_path), 0 },
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
{ STR(pkcs11_engine_path), 0 },
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
{ STR(pkcs11_module_path), 0 },
+#endif /* CONFIG_PKCS11_MODULE_PATH */
{ STR(openssl_ciphers), 0 },
{ STR(pcsc_reader), 0 },
{ STR(pcsc_pin), 0 },
@@ -5345,7 +5375,9 @@
#ifndef CONFIG_NO_CONFIG_WRITE
{ INT(update_config), 0 },
#endif /* CONFIG_NO_CONFIG_WRITE */
+#ifndef CONFIG_NO_LOAD_DYNAMIC_EAP
{ FUNC_NO_VAR(load_dynamic_eap), 0 },
+#endif /* CONFIG_NO_LOAD_DYNAMIC_EAP */
#ifdef CONFIG_WPS
{ FUNC(uuid), CFG_CHANGED_UUID },
{ INT_RANGE(auto_uuid, 0, 1), 0 },
@@ -5632,6 +5664,7 @@
line);
return -1;
}
+ return ret;
}
if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index e1f30c2..09bcbc8 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -629,6 +629,7 @@
*/
int fast_reauth;
+#ifndef CONFIG_OPENSC_ENGINE_PATH
/**
* opensc_engine_path - Path to the OpenSSL engine for opensc
*
@@ -636,7 +637,9 @@
* engine (engine_opensc.so); if %NULL, this engine is not loaded.
*/
char *opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
/**
* pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
*
@@ -644,7 +647,9 @@
* engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
*/
char *pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
/**
* pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
*
@@ -653,6 +658,7 @@
* module is not loaded.
*/
char *pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
/**
* openssl_ciphers - OpenSSL cipher string
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index c7b7826..ebad5f1 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1127,15 +1127,21 @@
config->disable_scan_offload);
if (config->fast_reauth != DEFAULT_FAST_REAUTH)
fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
+#ifndef CONFIG_OPENSC_ENGINE_PATH
if (config->opensc_engine_path)
fprintf(f, "opensc_engine_path=%s\n",
config->opensc_engine_path);
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
if (config->pkcs11_engine_path)
fprintf(f, "pkcs11_engine_path=%s\n",
config->pkcs11_engine_path);
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
if (config->pkcs11_module_path)
fprintf(f, "pkcs11_module_path=%s\n",
config->pkcs11_module_path);
+#endif /* CONFIG_PKCS11_MODULE_PATH */
if (config->openssl_ciphers)
fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
if (config->pcsc_reader)
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 9abfeb2..a68802e 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2967,6 +2967,16 @@
pos += ret;
}
+#ifdef CONFIG_SHA384
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ ret = os_snprintf(pos, end - pos, "%sEAP-SHA384",
+ pos == start ? "" : "+");
+ if (os_snprintf_error(end - pos, ret))
+ return pos;
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
+
pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
@@ -3609,6 +3619,7 @@
if (os_strcmp(name, "bssid") != 0 &&
os_strcmp(name, "bssid_hint") != 0 &&
+ os_strcmp(name, "scan_freq") != 0 &&
os_strcmp(name, "priority") != 0) {
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
@@ -5686,7 +5697,8 @@
}
#endif /* CONFIG_FILS */
- if (!is_zero_ether_addr(bss->mld_addr)) {
+ if (!is_zero_ether_addr(bss->mld_addr) &&
+ (mask & WPA_BSS_MASK_AP_MLD_ADDR)) {
ret = os_snprintf(pos, end - pos,
"ap_mld_addr=" MACSTR "\n",
MAC2STR(bss->mld_addr));
@@ -11528,7 +11540,16 @@
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
* [classifier_type=<4|10>]
* [classifier params based on classifier type]
- * [tclas_processing=<0|1>] [scs_id=<decimal number>] ...
+ * [tclas_processing=<0|1>]
+ * [qos_characteristics] <up/down/direct> [min_si=<decimal number>]
+ * [max_si=<decimal number>] [min_data_rate=<decimal number>]
+ * [delay_bound=<decimal number>] [max_msdu=<decimal number>]
+ * [service_start_time=<decimal number>]
+ * [service_start_time_link_id=<decimal number>]
+ * [mean_data_rate=<decimal number>] [burst_size=<decimal number>]
+ * [msdu_lifetime=<decimal number>]
+ * [msdu_delivery_info=<decimal number>] [medium_time=<decimal number>]
+ * [scs_id=<decimal number>] ...
*/
pos1 = os_strstr(cmd, "scs_id=");
if (!pos1) {
@@ -11541,9 +11562,10 @@
while (pos1) {
struct scs_desc_elem *n1;
struct active_scs_elem *active_scs_desc;
- char *next_scs_desc;
+ char *next_scs_desc, *pos2;
unsigned int num_tclas_elem = 0;
- bool scsid_active = false;
+ bool scsid_active = false, tclas_present = false;
+ struct qos_characteristics *qos_elem = &desc_elem.qos_char_elem;
desc_elem.scs_id = atoi(pos1 + 7);
pos1 += 7;
@@ -11619,8 +11641,9 @@
pos = os_strstr(pos1, "classifier_type=");
if (!pos) {
wpa_printf(MSG_ERROR, "classifier type empty");
- goto free_scs_desc;
+ goto qos_characteristics;
}
+ tclas_present = true;
while (pos) {
struct tclas_element elem = { 0 }, *n;
@@ -11684,6 +11707,117 @@
desc_elem.tclas_processing = val;
}
+ qos_characteristics:
+ pos1 = os_strstr(pos1, "qos_characteristics");
+ if (!pos1 && !tclas_present)
+ goto free_scs_desc;
+ if (!pos1)
+ goto scs_desc_end;
+
+ qos_elem->available = true;
+ if (os_strstr(pos1, "up ")) {
+ qos_elem->direction = SCS_DIRECTION_UP;
+ if (tclas_present) {
+ wpa_printf(MSG_ERROR,
+ "TCLAS with direction:UP not allowed");
+ goto free_scs_desc;
+ }
+ } else if (os_strstr(pos1, "down ")) {
+ qos_elem->direction = SCS_DIRECTION_DOWN;
+ } else if (os_strstr(pos1, "direct ")) {
+ qos_elem->direction = SCS_DIRECTION_DIRECT;
+ }
+
+ pos1 = os_strstr(pos1, "min_si=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Min SI is required");
+ goto free_scs_desc;
+ }
+ qos_elem->min_si = atoi(pos1 + 7);
+
+ pos1 = os_strstr(pos1, "max_si=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Max SI is required");
+ goto free_scs_desc;
+ }
+ qos_elem->max_si = atoi(pos1 + 7);
+
+ if (qos_elem->min_si && qos_elem->max_si &&
+ qos_elem->max_si < qos_elem->min_si) {
+ wpa_printf(MSG_ERROR, "Invalid Max SI");
+ goto free_scs_desc;
+ }
+
+ pos1 = os_strstr(pos1, "min_data_rate=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Min data rate is required");
+ goto free_scs_desc;
+ }
+ qos_elem->min_data_rate = atoi(pos1 + 14);
+
+ pos1 = os_strstr(pos1, "delay_bound=");
+ if (!pos1) {
+ wpa_printf(MSG_ERROR, "Delay Bound is required");
+ goto free_scs_desc;
+ }
+ qos_elem->delay_bound = atoi(pos1 + 12);
+
+ if (qos_elem->min_data_rate >= BIT(24) ||
+ qos_elem->delay_bound >= BIT(24)) {
+ wpa_printf(MSG_ERROR,
+ "Invalid min_data_rate or delay_bound");
+ goto free_scs_desc;
+ }
+
+ pos2 = os_strstr(pos1, "max_msdu=");
+ if (pos2) {
+ qos_elem->max_msdu_size = atoi(pos2 + 9);
+ qos_elem->mask |= SCS_QOS_BIT_MAX_MSDU_SIZE;
+ }
+
+ pos2 = os_strstr(pos1, "service_start_time=");
+ if (pos2) {
+ qos_elem->service_start_time = atoi(pos2 + 19);
+ qos_elem->mask |= SCS_QOS_BIT_SERVICE_START_TIME;
+ }
+
+ pos2 = os_strstr(pos1, "service_start_time_link_id=");
+ if (pos2) {
+ qos_elem->service_start_time_link_id = atoi(pos2 + 27);
+ qos_elem->mask |= SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
+ }
+
+ pos2 = os_strstr(pos1, "mean_data_rate=");
+ if (pos2) {
+ qos_elem->mean_data_rate = atoi(pos2 + 15);
+ qos_elem->mask |= SCS_QOS_BIT_MEAN_DATA_RATE;
+ }
+
+ pos2 = os_strstr(pos1, "burst_size=");
+ if (pos2) {
+ qos_elem->burst_size = atoi(pos2 + 11);
+ qos_elem->mask |=
+ SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE;
+ }
+
+ pos2 = os_strstr(pos1, "msdu_lifetime=");
+ if (pos2) {
+ qos_elem->msdu_lifetime = atoi(pos2 + 14);
+ qos_elem->mask |= SCS_QOS_BIT_MSDU_LIFETIME;
+ }
+
+ pos2 = os_strstr(pos1, "msdu_delivery_info=");
+ if (pos2) {
+ qos_elem->msdu_delivery_info = atoi(pos2 + 19);
+ qos_elem->mask |= SCS_QOS_BIT_MSDU_DELIVERY_INFO;
+ }
+
+ pos2 = os_strstr(pos1, "medium_time=");
+ if (pos2) {
+ qos_elem->medium_time = atoi(pos2 + 12);
+ qos_elem->mask |= SCS_QOS_BIT_MEDIUM_TIME;
+ }
+
scs_desc_end:
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
sizeof(struct scs_desc_elem));
@@ -11913,6 +12047,80 @@
}
+#ifdef CONFIG_TESTING_OPTIONS
+static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *token, *context = NULL;
+ u8 bssid[ETH_ALEN];
+ int mld_id = -1, link_id = -1;
+ struct wpa_bss *bss;
+ int *freqs;
+
+ os_memset(bssid, 0, sizeof(bssid));
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "bssid=", 6) == 0) {
+ if (hwaddr_aton(token + 6, bssid))
+ return -1;
+ } else if (os_strncmp(token, "mld_id=", 7) == 0) {
+ mld_id = atoi(token + 7);
+ } else if (os_strncmp(token, "link_id=", 8) == 0) {
+ link_id = atoi(token + 8);
+ }
+ }
+
+ if (mld_id < 0 || is_zero_ether_addr(bssid)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed parsing ML probe request arguments");
+ return -1;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unknown BSS for " MACSTR, MAC2STR(bssid));
+ return -1;
+ }
+
+ if (wpa_s->sched_scanning || wpa_s->scanning ||
+ (wpa_s->wpa_state > WPA_SCANNING &&
+ wpa_s->wpa_state != WPA_COMPLETED)) {
+ wpa_printf(MSG_DEBUG,
+ "MLO: Ongoing scan: Reject ML probe request");
+ return -1;
+ }
+
+ freqs = os_malloc(sizeof(int) * 2);
+ if (!freqs)
+ return -1;
+
+ freqs[0] = bss->freq;
+ freqs[1] = 0;
+
+ wpa_s->manual_scan_passive = 0;
+ wpa_s->manual_scan_use_id = 0;
+ wpa_s->manual_scan_only_new = 0;
+ wpa_s->scan_id_count = 0;
+ wpa_s->scan_res_handler = scan_only_handler;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+
+ os_memcpy(wpa_s->ml_probe_bssid, bssid, ETH_ALEN);
+ wpa_s->ml_probe_mld_id = mld_id;
+ if (link_id >= 0)
+ wpa_s->ml_probe_links = BIT(link_id);
+
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
@@ -12718,6 +12926,9 @@
} else if (os_strcmp(buf, "TWT_TEARDOWN") == 0) {
if (wpas_ctrl_iface_send_twt_teardown(wpa_s, ""))
reply_len = -1;
+ } else if (os_strncmp(buf, "ML_PROBE_REQ ", 13) == 0) {
+ if (wpas_ctrl_ml_probe(wpa_s, buf + 13))
+ reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 0e47534..fdf7b12 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -362,6 +362,27 @@
/**
+ * Add a double entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ * wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The double value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+ const char *key,
+ const double value)
+{
+ if (!key)
+ return FALSE;
+ return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE,
+ &value);
+}
+
+
+/**
* Begin an array entry in the dict
*
* @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 44685ea..cc9e26f 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -59,6 +59,10 @@
const char *value,
const dbus_uint32_t value_len);
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+ const char *key,
+ const double value);
+
/* Manual construction and addition of array elements */
dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
const char *key, const char *type,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 67ce970..6ad49a1 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1594,10 +1594,10 @@
}
-static int wpas_dbus_get_scan_allow_roam(DBusMessage *message,
- DBusMessageIter *var,
- dbus_bool_t *allow,
- DBusMessage **reply)
+static int wpas_dbus_get_scan_boolean(DBusMessage *message,
+ DBusMessageIter *var,
+ dbus_bool_t *allow,
+ DBusMessage **reply)
{
if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) {
wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a boolean",
@@ -1629,7 +1629,10 @@
char *key = NULL, *type = NULL;
struct wpa_driver_scan_params params;
size_t i;
- dbus_bool_t allow_roam = 1;
+ dbus_bool_t allow_roam = TRUE;
+ dbus_bool_t non_coloc_6ghz = FALSE;
+ dbus_bool_t scan_6ghz_only = FALSE;
+ bool custom_ies = false;
os_memset(¶ms, 0, sizeof(params));
@@ -1656,15 +1659,28 @@
if (wpas_dbus_get_scan_ies(message, &variant_iter,
¶ms, &reply) < 0)
goto out;
+ custom_ies = true;
} else if (os_strcmp(key, "Channels") == 0) {
if (wpas_dbus_get_scan_channels(message, &variant_iter,
¶ms, &reply) < 0)
goto out;
} else if (os_strcmp(key, "AllowRoam") == 0) {
- if (wpas_dbus_get_scan_allow_roam(message,
- &variant_iter,
- &allow_roam,
- &reply) < 0)
+ if (wpas_dbus_get_scan_boolean(message,
+ &variant_iter,
+ &allow_roam,
+ &reply) < 0)
+ goto out;
+ } else if (os_strcmp(key, "NonColoc6GHz") == 0) {
+ if (wpas_dbus_get_scan_boolean(message,
+ &variant_iter,
+ &non_coloc_6ghz,
+ &reply) < 0)
+ goto out;
+ } else if (os_strcmp(key, "6GHzOnly") == 0) {
+ if (wpas_dbus_get_scan_boolean(message,
+ &variant_iter,
+ &scan_6ghz_only,
+ &reply) < 0)
goto out;
} else {
wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown argument %s",
@@ -1683,6 +1699,13 @@
goto out;
}
+ if (non_coloc_6ghz)
+ params.non_coloc_6ghz = 1;
+
+ if (scan_6ghz_only && !params.freqs)
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms,
+ true, false, false);
+
if (os_strcmp(type, "passive") == 0) {
if (params.num_ssids || params.extra_ies_len) {
wpa_printf(MSG_DEBUG,
@@ -1703,7 +1726,8 @@
if (params.freqs && params.freqs[0]) {
wpa_s->last_scan_req = MANUAL_SCAN_REQ;
if (wpa_supplicant_trigger_scan(wpa_s,
- ¶ms)) {
+ ¶ms,
+ false, false)) {
reply = wpas_dbus_error_scan_error(
message,
"Scan request rejected");
@@ -1729,7 +1753,8 @@
}
wpa_s->last_scan_req = MANUAL_SCAN_REQ;
- if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms, !custom_ies,
+ false)) {
reply = wpas_dbus_error_scan_error(
message, "Scan request rejected");
}
@@ -4318,11 +4343,18 @@
const struct wpa_dbus_property_desc *property_desc,
DBusMessageIter *iter, DBusError *error, void *user_data)
{
+
+#ifndef CONFIG_PKCS11_ENGINE_PATH
struct wpa_supplicant *wpa_s = user_data;
return wpas_dbus_string_property_getter(iter,
wpa_s->conf->pkcs11_engine_path,
error);
+#else /* CONFIG_PKCS11_ENGINE_PATH */
+ return wpas_dbus_string_property_getter(iter,
+ CONFIG_PKCS11_ENGINE_PATH,
+ error);
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
}
@@ -4339,11 +4371,17 @@
const struct wpa_dbus_property_desc *property_desc,
DBusMessageIter *iter, DBusError *error, void *user_data)
{
+#ifndef CONFIG_PKCS11_MODULE_PATH
struct wpa_supplicant *wpa_s = user_data;
return wpas_dbus_string_property_getter(iter,
wpa_s->conf->pkcs11_module_path,
error);
+#else /* CONFIG_PKCS11_MODULE_PATH */
+ return wpas_dbus_string_property_getter(iter,
+ CONFIG_PKCS11_MODULE_PATH,
+ error);
+#endif /* CONFIG_PKCS11_MODULE_PATH */
}
@@ -5278,7 +5316,7 @@
DBusMessageIter iter_dict, variant_iter;
const char *group;
const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
- const char *key_mgmt[18]; /* max 18 key managements may be supported */
+ const char *key_mgmt[19]; /* max 19 key managements may be supported */
int n;
if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -5341,6 +5379,10 @@
#endif /* CONFIG_OWE */
if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
key_mgmt[n++] = "wpa-none";
+#ifdef CONFIG_SHA384
+ if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
+ key_mgmt[n++] = "wpa-eap-sha384";
+#endif /* CONFIG_SHA384 */
if (!wpa_dbus_dict_append_string_array(&iter_dict, "KeyMgmt",
key_mgmt, n))
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 06f74ad..7fb0669 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -1026,6 +1026,23 @@
}
+static double guard_interval_to_double(enum guard_interval value)
+{
+ switch (value) {
+ case GUARD_INTERVAL_0_4:
+ return 0.4;
+ case GUARD_INTERVAL_0_8:
+ return 0.8;
+ case GUARD_INTERVAL_1_6:
+ return 1.6;
+ case GUARD_INTERVAL_3_2:
+ return 3.2;
+ default:
+ return 0;
+ }
+}
+
+
/**
* wpas_dbus_new_from_signal_information - Adds a wpa_signal_info
* to a DBusMessage.
@@ -1149,6 +1166,20 @@
(si->data.avg_ack_signal &&
!wpa_dbus_dict_append_int32(&iter_dict, "avg-ack-rssi",
si->data.avg_ack_signal)) ||
+ (si->data.rx_guard_interval &&
+ !wpa_dbus_dict_append_double(
+ &iter_dict, "rx-guard-interval",
+ guard_interval_to_double(si->data.rx_guard_interval))) ||
+ (si->data.tx_guard_interval &&
+ !wpa_dbus_dict_append_double(
+ &iter_dict, "tx-guard-interval",
+ guard_interval_to_double(si->data.tx_guard_interval))) ||
+ ((si->data.flags & STA_DRV_DATA_RX_HE_DCM) &&
+ !wpa_dbus_dict_append_bool(&iter_dict, "rx-dcm",
+ si->data.rx_dcm)) ||
+ ((si->data.flags & STA_DRV_DATA_TX_HE_DCM) &&
+ !wpa_dbus_dict_append_bool(&iter_dict, "tx-dcm",
+ si->data.tx_dcm)) ||
!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
!dbus_message_iter_close_container(iter, &variant_iter))
return -ENOMEM;
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 95dc4e3..1ae5a9f 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -212,6 +212,9 @@
# Development testing
#CONFIG_EAPOL_TEST=y
+# Support IPv6
+CONFIG_IPV6=y
+
# Select control interface backend for external programs, e.g, wpa_cli:
# unix = UNIX domain sockets (default for Linux/*BSD)
# udp = UDP sockets using localhost (127.0.0.1)
@@ -398,6 +401,22 @@
# amount of memory/flash.
#CONFIG_DYNAMIC_EAP_METHODS=y
+# Dynamic library loading
+
+# Add the ability to configure libraries to load at compile time.
+# If set, these disable dynamic configuration.
+#CONFIG_PKCS11_ENGINE_PATH - pkcs11_engine library location.
+#CONFIG_PKCS11_MODULE_PATH - pkcs11_module library location.
+#CONFIG_OPENSC_ENGINE_PATH - opensc_engine library location.
+#
+# Prevent library loading at runtime
+#CONFIG_NO_PKCS11_ENGINE_PATH=y # prevents loading pkcs11_engine library.
+#CONFIG_NO_PKCS11_MODULE_PATH=y # prevents loading pkcs11_module library.
+# CONFIG_NO_OPENSC_ENGINE_PATH=y # prevents loading opensc_engine library.
+
+# Prevents loading EAP libraries at runtime
+#CONFIG_NO_LOAD_DYNAMIC_EAP=y
+
# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
CONFIG_IEEE80211R=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 895d5fa..4f5be0a 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -441,8 +441,9 @@
}
-void wpas_dpp_connected(struct wpa_supplicant *wpa_s)
+static void wpas_dpp_connected_timeout(void *eloop_ctx, void *timeout_ctx)
{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
struct dpp_authentication *auth = wpa_s->dpp_auth;
if ((auth && auth->conn_status_requested) ||
@@ -450,9 +451,42 @@
wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_OK);
}
+
+void wpas_dpp_connected(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if ((auth && auth->conn_status_requested) ||
+ dpp_tcp_conn_status_requested(wpa_s->dpp)) {
+ /* Report connection result from an eloop timeout to avoid delay
+ * to completing all connection completion steps since this
+ * function is called in a middle of the post 4-way handshake
+ * processing. */
+ eloop_register_timeout(0, 0, wpas_dpp_connected_timeout,
+ wpa_s, NULL);
+ }
+}
+
#endif /* CONFIG_DPP2 */
+static void wpas_dpp_drv_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (auth && auth->waiting_auth_resp) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Call wpas_dpp_auth_init_next() from %s",
+ __func__);
+ wpas_dpp_auth_init_next(wpa_s);
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: %s, but no waiting_auth_resp",
+ __func__);
+ }
+}
+
+
static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
@@ -532,7 +566,12 @@
/* In case of DPP Authentication Request frame, move to
* the next channel immediately. */
offchannel_send_action_done(wpa_s);
- wpas_dpp_auth_init_next(wpa_s);
+ /* Call wpas_dpp_auth_init_next(wpa_s) from driver event
+ * notifying frame wait was completed or from eloop
+ * timeout. */
+ eloop_register_timeout(0, 10000,
+ wpas_dpp_drv_wait_timeout,
+ wpa_s, NULL);
return;
}
if (auth->waiting_auth_conf) {
@@ -695,6 +734,8 @@
unsigned int wait_time, max_wait_time, freq, max_tries, used;
struct os_reltime now, diff;
+ eloop_cancel_timeout(wpas_dpp_drv_wait_timeout, wpa_s, NULL);
+
wpa_s->dpp_in_response_listen = 0;
if (!auth)
return -1;
@@ -1234,8 +1275,19 @@
struct dpp_authentication *auth = wpa_s->dpp_auth;
int freq;
- if (!wpa_s->dpp_gas_server || !auth)
+ if (!wpa_s->dpp_gas_server || !auth) {
+ if (auth && auth->waiting_auth_resp &&
+ eloop_is_timeout_registered(wpas_dpp_drv_wait_timeout,
+ wpa_s, NULL)) {
+ eloop_cancel_timeout(wpas_dpp_drv_wait_timeout,
+ wpa_s, NULL);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Call wpas_dpp_auth_init_next() from %s",
+ __func__);
+ wpas_dpp_auth_init_next(wpa_s);
+ }
return;
+ }
freq = auth->neg_freq > 0 ? auth->neg_freq : auth->curr_freq;
if (wpa_s->dpp_listen_work || (int) wpa_s->dpp_listen_freq == freq)
@@ -1377,7 +1429,7 @@
ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
- WPA_KEY_MGMT_IEEE8021X_SHA256;
+ WPA_KEY_MGMT_IEEE8021X_SHA384;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
if (conf->cacert) {
@@ -4752,6 +4804,7 @@
eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_gas_initial_resp_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_gas_client_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_drv_wait_timeout, wpa_s, NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
@@ -4760,6 +4813,7 @@
eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_build_csr, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_connected_timeout, wpa_s, NULL);
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = NULL;
wpas_dpp_chirp_stop(wpa_s);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d707cf5..dcf5764 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -60,6 +60,9 @@
struct wpa_driver_associate_params *params)
{
if (wpa_s->driver->associate) {
+ if (params)
+ params->freq.link_id = -1;
+
return wpa_s->driver->associate(wpa_s->drv_priv, params);
}
return -1;
@@ -189,7 +192,7 @@
{
if (wpa_s->driver->get_seqnum)
return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv,
- addr, idx, seq);
+ addr, idx, -1, seq);
return -1;
}
@@ -199,7 +202,7 @@
if (wpa_s->driver->sta_deauth) {
return wpa_s->driver->sta_deauth(wpa_s->drv_priv,
wpa_s->own_addr, addr,
- reason_code);
+ reason_code, -1);
}
return -1;
}
@@ -325,7 +328,7 @@
if (wpa_s->driver->send_mlme)
return wpa_s->driver->send_mlme(wpa_s->drv_priv,
data, data_len, noack,
- freq, NULL, 0, 0, wait);
+ freq, NULL, 0, 0, wait, -1);
return -1;
}
@@ -351,8 +354,9 @@
struct hostapd_sta_add_params *params)
{
if (wpa_s->driver->sta_add) {
- /* Set link_id to -1 as it's needed for AP only */
- params->mld_link_id = -1;
+ /* Set link_id to -1 for non-TDLS peers */
+ if (!(params->flags & WPA_STA_TDLS_PEER))
+ params->mld_link_id = -1;
return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
}
return -1;
@@ -374,7 +378,7 @@
if (!wpa_s->driver->tx_control_port)
return -1;
return wpa_s->driver->tx_control_port(wpa_s->drv_priv, dest, proto,
- buf, len, no_encrypt);
+ buf, len, no_encrypt, -1);
}
static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
@@ -385,7 +389,7 @@
if (wpa_s->driver->hapd_send_eapol)
return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
data, data_len, encrypt,
- own_addr, flags);
+ own_addr, flags, -1);
return -1;
}
@@ -582,13 +586,14 @@
const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capab, int initiator,
- const u8 *buf, size_t len)
+ const u8 *buf, size_t len, int link_id)
{
if (wpa_s->driver->send_tdls_mgmt) {
return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
action_code, dialog_token,
status_code, peer_capab,
- initiator, buf, len);
+ initiator, buf, len,
+ link_id);
}
return -1;
}
@@ -1098,6 +1103,10 @@
{
if (!wpa_s->driver->update_connect_params)
return -1;
+
+ if (params)
+ params->freq.link_id = -1;
+
return wpa_s->driver->update_connect_params(wpa_s->drv_priv, params,
mask);
}
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index eefae53..7c23727 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -22,6 +22,7 @@
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "ap/hostapd.h"
+#include "ap/sta_info.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
#include "wnm_sta.h"
@@ -58,12 +59,10 @@
#ifndef CONFIG_NO_SCAN_PROCESSING
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
- int new_scan, int own_request);
+ int new_scan, int own_request,
+ bool trigger_6ghz_scan,
+ union wpa_event_data *data);
#endif /* CONFIG_NO_SCAN_PROCESSING */
-#ifdef CONFIG_OWE
-static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
- const u8 **ret_ssid, size_t *ret_ssid_len);
-#endif /* CONFIG_OWE */
int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
@@ -143,8 +142,13 @@
{
struct wpa_bss *bss = NULL;
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ u8 drv_ssid[SSID_MAX_LEN];
+ int res;
- if (ssid && ssid->ssid_len > 0)
+ res = wpa_drv_get_ssid(wpa_s, drv_ssid);
+ if (res > 0)
+ bss = wpa_bss_get(wpa_s, bssid, drv_ssid, res);
+ if (!bss && ssid && ssid->ssid_len > 0)
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
if (!bss)
bss = wpa_bss_get_bssid(wpa_s, bssid);
@@ -215,14 +219,6 @@
return 0; /* current profile still in use */
#ifdef CONFIG_OWE
- if (wpa_s->current_bss &&
- !(wpa_s->current_bss->flags & WPA_BSS_OWE_TRANSITION)) {
- const u8 *match_ssid;
- size_t match_ssid_len;
-
- owe_trans_ssid(wpa_s, wpa_s->current_bss,
- &match_ssid, &match_ssid_len);
- }
if ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
wpa_s->current_bss &&
(wpa_s->current_bss->flags & WPA_BSS_OWE_TRANSITION) &&
@@ -427,7 +423,8 @@
for (i = 0; i < ie.num_pmkid; i++) {
pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
ie.pmkid + i * PMKID_LEN,
- NULL, NULL, 0, NULL, 0);
+ NULL, NULL, 0, NULL, 0,
+ true);
if (pmksa_set == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
if (authorized)
@@ -1090,7 +1087,6 @@
#ifdef CONFIG_OWE
const u8 *owe, *pos, *end, *bssid;
u8 ssid_len;
- struct wpa_bss *open_bss;
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN))
@@ -1131,52 +1127,30 @@
}
}
}
-
- if (bss->ssid_len > 0)
- return;
-
- open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
- if (!open_bss)
- return;
- if (ssid_len != open_bss->ssid_len ||
- os_memcmp(pos, open_bss->ssid, ssid_len) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "OWE: transition mode SSID mismatch: %s",
- wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len));
- return;
- }
-
- owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE);
- if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "OWE: transition mode open BSS unexpected info");
- return;
- }
-
- pos = owe + 6;
- end = owe + 2 + owe[1];
-
- if (end - pos < ETH_ALEN + 1)
- return;
- if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "OWE: transition mode BSSID mismatch: " MACSTR,
- MAC2STR(pos));
- return;
- }
- pos += ETH_ALEN;
- ssid_len = *pos++;
- if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
- return;
- wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s",
- wpa_ssid_txt(pos, ssid_len));
- os_memcpy(bss->ssid, pos, ssid_len);
- bss->ssid_len = ssid_len;
- bss->flags |= WPA_BSS_OWE_TRANSITION;
#endif /* CONFIG_OWE */
}
+static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+
+{
+ u16 removed_links;
+
+ if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL))
+ return true;
+
+ if (bss->n_mld_links == 0)
+ return true;
+
+ /* Check if the current BSS is going to be removed */
+ removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
+ if (BIT(bss->mld_links[0].link_id) & removed_links)
+ return false;
+
+ return true;
+}
+
+
int disabled_freq(struct wpa_supplicant *wpa_s, int freq)
{
int i, j;
@@ -1576,12 +1550,35 @@
#endif /* CONFIG_SAE_PK */
if (bss->ssid_len == 0) {
+#ifdef CONFIG_OWE
+ const u8 *owe_ssid = NULL;
+ size_t owe_ssid_len = 0;
+
+ owe_trans_ssid(wpa_s, bss, &owe_ssid, &owe_ssid_len);
+ if (owe_ssid && owe_ssid_len &&
+ owe_ssid_len == ssid->ssid_len &&
+ os_memcmp(owe_ssid, ssid->ssid, owe_ssid_len) == 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no SSID in BSS entry for a possible OWE transition mode BSS");
+ int_array_add_unique(&wpa_s->owe_trans_scan_freq,
+ bss->freq);
+ return false;
+ }
+#endif /* CONFIG_OWE */
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no SSID known for the BSS");
return false;
}
+ if (!wpas_valid_ml_bss(wpa_s, bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - ML BSS going to be removed");
+ return false;
+ }
+
/* Matching configuration found */
return true;
}
@@ -1835,6 +1832,82 @@
}
+static bool ml_link_probe_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->ml_connect_probe_ssid || !wpa_s->ml_connect_probe_bss)
+ return false;
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Request association with " MACSTR " after ML probe",
+ MAC2STR(wpa_s->ml_connect_probe_bss->bssid));
+
+ wpa_supplicant_associate(wpa_s, wpa_s->ml_connect_probe_bss,
+ wpa_s->ml_connect_probe_ssid);
+
+ wpa_s->ml_connect_probe_ssid = NULL;
+ wpa_s->ml_connect_probe_bss = NULL;
+
+ return true;
+}
+
+
+static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
+{
+ int *freqs;
+ u16 missing_links = 0, removed_links;
+
+ if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)))
+ return 0;
+
+ /* Try to resolve any missing link information */
+ if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL,
+ &missing_links) || !missing_links)
+ return 0;
+
+ removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
+ missing_links &= ~removed_links;
+
+ if (!missing_links)
+ return 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: Doing an ML probe for missing links 0x%04x",
+ missing_links);
+
+ freqs = os_malloc(sizeof(int) * 2);
+ if (!freqs)
+ return 0;
+
+ wpa_s->ml_connect_probe_ssid = ssid;
+ wpa_s->ml_connect_probe_bss = selected;
+
+ freqs[0] = selected->freq;
+ freqs[1] = 0;
+
+ wpa_s->manual_scan_passive = 0;
+ wpa_s->manual_scan_use_id = 0;
+ wpa_s->manual_scan_only_new = 0;
+ wpa_s->scan_id_count = 0;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = freqs;
+
+ os_memcpy(wpa_s->ml_probe_bssid, selected->bssid, ETH_ALEN);
+ wpa_s->ml_probe_mld_id = -1;
+ wpa_s->ml_probe_links = missing_links;
+
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 1;
+}
+
+
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
@@ -1891,6 +1964,10 @@
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
return 0;
}
+
+ if (wpa_supplicant_connect_ml_missing(wpa_s, selected, ssid))
+ return 0;
+
wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
MAC2STR(selected->bssid));
wpa_supplicant_associate(wpa_s, selected, ssid);
@@ -1966,9 +2043,15 @@
static int wpas_get_snr_signal_info(u32 frequency, int avg_signal, int noise)
{
- if (noise == WPA_INVALID_NOISE)
- noise = IS_5GHZ(frequency) ? DEFAULT_NOISE_FLOOR_5GHZ :
- DEFAULT_NOISE_FLOOR_2GHZ;
+ if (noise == WPA_INVALID_NOISE) {
+ if (IS_5GHZ(frequency)) {
+ noise = DEFAULT_NOISE_FLOOR_5GHZ;
+ } else if (is_6ghz_freq(frequency)) {
+ noise = DEFAULT_NOISE_FLOOR_6GHZ;
+ } else {
+ noise = DEFAULT_NOISE_FLOOR_2GHZ;
+ }
+ }
return avg_signal - noise;
}
@@ -1980,8 +2063,10 @@
int rate = wpa_bss_get_max_rate(bss);
const u8 *ies = wpa_bss_ie_ptr(bss);
size_t ie_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+ enum chan_width max_cw = CHAN_WIDTH_UNKNOWN;
- return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, bss->freq);
+ return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, bss->freq,
+ &max_cw);
}
@@ -1991,11 +2076,17 @@
{
int min_diff, diff;
int to_5ghz, to_6ghz;
- int cur_level;
+ int cur_level, sel_level;
unsigned int cur_est, sel_est;
struct wpa_signal_info si;
int cur_snr = 0;
int ret = 0;
+ const u8 *cur_ies = wpa_bss_ie_ptr(current_bss);
+ const u8 *sel_ies = wpa_bss_ie_ptr(selected);
+ size_t cur_ie_len = current_bss->ie_len ? current_bss->ie_len :
+ current_bss->beacon_ie_len;
+ size_t sel_ie_len = selected->ie_len ? selected->ie_len :
+ selected->beacon_ie_len;
wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
@@ -2016,10 +2107,6 @@
return 1;
}
- cur_level = current_bss->level;
- cur_est = current_bss->est_throughput;
- sel_est = selected->est_throughput;
-
/*
* Try to poll the signal from the driver since this will allow to get
* more accurate values. In some cases, there can be big differences
@@ -2037,8 +2124,15 @@
*/
if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
(si.data.avg_beacon_signal || si.data.avg_signal)) {
+ /*
+ * Normalize avg_signal to the RSSI over 20 MHz, as the
+ * throughput is estimated based on the RSSI over 20 MHz
+ */
cur_level = si.data.avg_beacon_signal ?
- si.data.avg_beacon_signal : si.data.avg_signal;
+ si.data.avg_beacon_signal :
+ (si.data.avg_signal -
+ wpas_channel_width_rssi_bump(cur_ies, cur_ie_len,
+ si.chanwidth));
cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level,
si.current_noise);
@@ -2048,8 +2142,24 @@
wpa_dbg(wpa_s, MSG_DEBUG,
"Using signal poll values for the current BSS: level=%d snr=%d est_throughput=%u",
cur_level, cur_snr, cur_est);
+ } else {
+ /* Level and SNR are measured over 20 MHz channel */
+ cur_level = current_bss->level;
+ cur_snr = current_bss->snr;
+ cur_est = current_bss->est_throughput;
}
+ /* Adjust the SNR of BSSes based on the channel width. */
+ cur_level += wpas_channel_width_rssi_bump(cur_ies, cur_ie_len,
+ current_bss->max_cw);
+ cur_snr = wpas_adjust_snr_by_chanwidth(cur_ies, cur_ie_len,
+ current_bss->max_cw, cur_snr);
+
+ sel_est = selected->est_throughput;
+ sel_level = selected->level +
+ wpas_channel_width_rssi_bump(sel_ies, sel_ie_len,
+ selected->max_cw);
+
if (sel_est > cur_est + 5000) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Allow reassociation - selected BSS has better estimated throughput");
@@ -2061,7 +2171,7 @@
!is_6ghz_freq(current_bss->freq);
if (cur_level < 0 &&
- cur_level > selected->level + to_5ghz * 2 + to_6ghz * 2 &&
+ cur_level > sel_level + to_5ghz * 2 + to_6ghz * 2 &&
sel_est < cur_est * 1.2) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
"signal level");
@@ -2115,7 +2225,7 @@
min_diff -= 2;
if (to_6ghz)
min_diff -= 2;
- diff = selected->level - cur_level;
+ diff = sel_level - cur_level;
if (diff < min_diff) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Skip roam - too small difference in signal level (%d < %d)",
@@ -2134,7 +2244,7 @@
MAC2STR(current_bss->bssid),
current_bss->freq, cur_level, cur_est,
MAC2STR(selected->bssid),
- selected->freq, selected->level, sel_est);
+ selected->freq, sel_level, sel_est);
return ret;
}
@@ -2200,6 +2310,7 @@
struct wpa_scan_results *scan_res = NULL;
int ret = 0;
int ap = 0;
+ bool trigger_6ghz_scan;
#ifndef CONFIG_NO_RANDOM_POOL
size_t i, num;
#endif /* CONFIG_NO_RANDOM_POOL */
@@ -2209,6 +2320,11 @@
ap = 1;
#endif /* CONFIG_AP */
+ trigger_6ghz_scan = wpa_s->crossed_6ghz_dom &&
+ wpa_s->last_scan_all_chan;
+ wpa_s->crossed_6ghz_dom = false;
+ wpa_s->last_scan_all_chan = false;
+
wpa_supplicant_notify_scanning(wpa_s, 0);
scan_res = wpa_supplicant_get_scan_results(wpa_s,
@@ -2312,6 +2428,9 @@
wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
goto scan_work_done;
+ if (ml_link_probe_scan(wpa_s))
+ goto scan_work_done;
+
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
goto scan_work_done;
@@ -2359,7 +2478,8 @@
if (wpa_s->supp_pbc_active && !wpas_wps_partner_link_scan_done(wpa_s))
return ret;
- return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
+ return wpas_select_network_from_last_scan(wpa_s, 1, own_request,
+ trigger_6ghz_scan, data);
scan_work_done:
wpa_scan_results_free(scan_res);
@@ -2372,8 +2492,44 @@
}
+static int wpas_trigger_6ghz_scan(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct wpa_driver_scan_params params;
+ unsigned int j;
+
+ wpa_dbg(wpa_s, MSG_INFO, "Triggering 6GHz-only scan");
+ os_memset(¶ms, 0, sizeof(params));
+ params.non_coloc_6ghz = wpa_s->last_scan_non_coloc_6ghz;
+ for (j = 0; j < data->scan_info.num_ssids; j++)
+ params.ssids[j] = data->scan_info.ssids[j];
+ params.num_ssids = data->scan_info.num_ssids;
+ wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms,
+ true, !wpa_s->last_scan_non_coloc_6ghz, false);
+ if (!wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, true)) {
+ os_free(params.freqs);
+ return 1;
+ }
+ wpa_dbg(wpa_s, MSG_INFO, "Failed to trigger 6GHz-only scan");
+ os_free(params.freqs);
+ return 0;
+}
+
+
+/**
+ * Select a network from the last scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @new_scan: Whether this function was called right after a scan has finished
+ * @own_request: Whether the scan was requested by this interface
+ * @trigger_6ghz_scan: Whether to trigger a 6ghz-only scan when applicable
+ * @data: Scan data from scan that finished if applicable
+ *
+ * See _wpa_supplicant_event_scan_results() for return values.
+ */
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
- int new_scan, int own_request)
+ int new_scan, int own_request,
+ bool trigger_6ghz_scan,
+ union wpa_event_data *data)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
@@ -2393,6 +2549,10 @@
return 0; /* no normal connection on p2p_mgmt interface */
wpa_s->owe_transition_search = 0;
+#ifdef CONFIG_OWE
+ os_free(wpa_s->owe_trans_scan_freq);
+ wpa_s->owe_trans_scan_freq = NULL;
+#endif /* CONFIG_OWE */
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
@@ -2445,6 +2605,10 @@
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else if (own_request) {
+ if (wpa_s->support_6ghz && trigger_6ghz_scan && data &&
+ wpas_trigger_6ghz_scan(wpa_s, data) < 0)
+ return 1;
+
/*
* No SSID found. If SCAN results are as a result of
* own scan request and not due to a scan request on
@@ -2508,6 +2672,13 @@
"OWE: Use shorter wait during transition mode search");
timeout_sec = 0;
timeout_usec = 500000;
+ if (wpa_s->owe_trans_scan_freq) {
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs =
+ wpa_s->owe_trans_scan_freq;
+ wpa_s->owe_trans_scan_freq = NULL;
+ timeout_usec = 100000;
+ }
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
@@ -2593,7 +2764,7 @@
return -1;
}
- return wpas_select_network_from_last_scan(wpa_s, 0, 1);
+ return wpas_select_network_from_last_scan(wpa_s, 0, 1, false, NULL);
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
@@ -2603,7 +2774,7 @@
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
- return wpas_select_network_from_last_scan(wpa_s, 1, 1);
+ return wpas_select_network_from_last_scan(wpa_s, 1, 1, false, NULL);
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
@@ -3178,6 +3349,8 @@
} else {
wpa_s->connection_channel_bandwidth = CHAN_WIDTH_20;
}
+ if (req_elems.rrm_enabled)
+ wpa_s->rrm.rrm_used = 1;
}
}
@@ -3284,6 +3457,7 @@
#ifdef CONFIG_OWE
if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
+ !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA) &&
(!bssid_known ||
owe_process_assoc_resp(wpa_s->wpa,
wpa_s->valid_links ?
@@ -3674,8 +3848,8 @@
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
- data->assoc_info.req_ies_len,
- data->assoc_info.reassoc);
+ data->assoc_info.req_ies_len, NULL, 0,
+ NULL, data->assoc_info.reassoc);
return;
}
#endif /* CONFIG_AP */
@@ -3691,7 +3865,7 @@
int use_sha384 = wpa_key_mgmt_sha384(wpa_s->wpa->key_mgmt);
if (wpa_key_mgmt_ft(wpa_s->key_mgmt) && (wpa_s->key_mgmt == ie.key_mgmt)) {
if (wpa_ft_parse_ies(data->assoc_info.resp_ies,
- data->assoc_info.resp_ies_len, &parse, use_sha384) < 0) {
+ data->assoc_info.resp_ies_len, &parse, use_sha384, false) < 0) {
wpa_printf(MSG_DEBUG, "Failed to parse FT IEs");
return;
}
@@ -4272,7 +4446,7 @@
wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
pairwise = (data && data->michael_mic_failure.unicast);
os_get_reltime(&t);
- if ((wpa_s->last_michael_mic_error.sec &&
+ if ((os_reltime_initialized(&wpa_s->last_michael_mic_error) &&
!os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
wpa_s->pending_mic_error_report) {
if (wpa_s->pending_mic_error_report) {
@@ -4824,12 +4998,17 @@
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
+ bool was_6ghz_enabled;
+
wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
ifs->ifname);
free_hw_features(ifs);
ifs->hw.modes = wpa_drv_get_hw_feature_data(
ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain);
+ was_6ghz_enabled = ifs->is_6ghz_enabled;
+ ifs->is_6ghz_enabled = wpas_is_6ghz_supported(ifs, true);
+
/* Restart PNO/sched_scan with updated channel list */
if (ifs->pno) {
wpas_stop_pno(ifs);
@@ -4838,6 +5017,12 @@
wpa_dbg(ifs, MSG_DEBUG,
"Channel list changed - restart sched_scan");
wpas_scan_restart_sched_scan(ifs);
+ } else if (ifs->scanning && !was_6ghz_enabled &&
+ ifs->is_6ghz_enabled) {
+ /* Look for APs in the 6 GHz band */
+ wpa_dbg(ifs, MSG_INFO,
+ "Channel list changed - trigger 6 GHz-only scan");
+ ifs->crossed_6ghz_dom = true;
}
}
@@ -4916,6 +5101,12 @@
wpa_dbg(wpa_s, MSG_DEBUG,
"TDLS: Received Discovery Response from " MACSTR,
MAC2STR(mgmt->sa));
+ if (wpa_s->valid_links &&
+ wpa_tdls_process_discovery_response(wpa_s->wpa, mgmt->sa,
+ &payload[1], plen - 1))
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "TDLS: Discovery Response process failed for "
+ MACSTR, MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_TDLS */
@@ -5164,7 +5355,7 @@
/* Update the current PMKSA used for this connection */
pmksa_cache_set_current(wpa_s->wpa,
data->assoc_info.fils_pmkid,
- NULL, NULL, 0, NULL, 0);
+ NULL, NULL, 0, NULL, 0, true);
}
}
#endif /* CONFIG_FILS */
@@ -6378,6 +6569,21 @@
break;
#endif /* CONFIG_PASN */
case EVENT_PORT_AUTHORIZED:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) {
+ struct sta_info *sta;
+
+ sta = ap_get_sta(wpa_s->ap_iface->bss[0],
+ data->port_authorized.sta_addr);
+ if (sta)
+ ap_sta_set_authorized(wpa_s->ap_iface->bss[0],
+ sta, 1);
+ else
+ wpa_printf(MSG_DEBUG,
+ "No STA info matching port authorized event found");
+ break;
+ }
+#endif /* CONFIG_AP */
#ifndef CONFIG_NO_WPA
if (data->port_authorized.td_bitmap_len) {
wpa_printf(MSG_DEBUG,
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index c9e14d5..138c013 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -16,6 +16,7 @@
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
+#include "ap/beacon.h"
#include "ap/wpa_auth.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
@@ -561,8 +562,11 @@
int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
if (sta) {
- if (sta->plink_state == PLINK_ESTAB)
+ if (sta->plink_state == PLINK_ESTAB) {
hapd->num_plinks--;
+ wpas_notify_mesh_peer_disconnected(
+ wpa_s, sta->addr, WLAN_REASON_UNSPECIFIED);
+ }
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
@@ -766,7 +770,8 @@
set_disable_ht40(sta->ht_capabilities, 1);
}
- update_ht_state(data, sta);
+ if (update_ht_state(data, sta) > 0)
+ ieee802_11_update_beacons(data->iface);
#ifdef CONFIG_IEEE80211AC
copy_sta_vht_capab(data, sta, elems->vht_capabilities);
@@ -951,11 +956,6 @@
peer_add_timer(wpa_s, NULL);
eloop_cancel_timeout(plink_timer, wpa_s, sta);
- /* Send ctrl event */
- wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
- MAC2STR(sta->addr));
-
- /* Send D-Bus event */
wpas_notify_mesh_peer_connected(wpa_s, sta->addr);
}
@@ -1106,10 +1106,6 @@
" closed with reason %d",
MAC2STR(sta->addr), reason);
- wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
-
- /* Send D-Bus event */
wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr,
reason);
@@ -1428,8 +1424,13 @@
/* called by ap_free_sta */
void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
- if (sta->plink_state == PLINK_ESTAB)
+ struct wpa_supplicant *wpa_s = hapd->iface->owner;
+
+ if (sta->plink_state == PLINK_ESTAB) {
hapd->num_plinks--;
+ wpas_notify_mesh_peer_disconnected(
+ wpa_s, sta->addr, WLAN_REASON_UNSPECIFIED);
+ }
eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
}
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index a20f1c0..c296903 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -463,6 +463,10 @@
wpa_s->last_ssid = NULL;
if (wpa_s->current_ssid == ssid)
wpa_s->current_ssid = NULL;
+ if (wpa_s->ml_connect_probe_ssid == ssid) {
+ wpa_s->ml_connect_probe_ssid = NULL;
+ wpa_s->ml_connect_probe_bss = NULL;
+ }
#if defined(CONFIG_SME) && defined(CONFIG_SAE)
if (wpa_s->sme.ext_auth_wpa_ssid == ssid)
wpa_s->sme.ext_auth_wpa_ssid = NULL;
@@ -1126,6 +1130,8 @@
if (wpa_s->p2p_mgmt)
return;
+ wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
+ MAC2STR(peer_addr));
wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr);
}
@@ -1136,6 +1142,8 @@
if (wpa_s->p2p_mgmt)
return;
+ wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
+ MAC2STR(peer_addr));
wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code);
}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index db99177..c150863 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1088,6 +1088,7 @@
* Likewise, we don't send out network removed signals for such
* network objects.
*/
+ wpas_notify_network_removed(wpa_s, ssid);
wpa_config_remove_network(wpa_s->conf, id);
wpa_supplicant_clear_status(wpa_s);
wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -2827,6 +2828,12 @@
wpa_drv_probe_req_report(wpa_s, 0);
wpas_p2p_listen_work_done(wpa_s);
+
+ if (radio_work_pending(wpa_s, "p2p-listen")) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: p2p-listen is still pending - remove it");
+ radio_remove_works(wpa_s, "p2p-listen", 0);
+ }
}
@@ -6324,7 +6331,7 @@
res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
&size, pref_freq_list);
- if (!is_p2p_allow_6ghz(wpa_s->global->p2p))
+ if (!res && size > 0 && !is_p2p_allow_6ghz(wpa_s->global->p2p))
size = p2p_remove_6ghz_channels(pref_freq_list, size);
if (!res && size > 0) {
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 3aa6675..f266ce4 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -357,7 +357,7 @@
ret = -2;
else {
ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0,
- NULL, 0) ? 0 : -3;
+ NULL, 0, false) ? 0 : -3;
}
test_eapol_clean(&wpa_s);
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index ff648b2..58edd9b 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -98,12 +98,27 @@
}
+static bool tclas_elem_required(const struct qos_characteristics *qos_elem)
+{
+ if (!qos_elem || !qos_elem->available)
+ return true;
+
+ if (qos_elem->direction == SCS_DIRECTION_DOWN)
+ return true;
+
+ return false;
+}
+
+
static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
- struct wpabuf *buf)
+ struct wpabuf *buf,
+ bool allow_scs_traffic_desc)
{
u8 *len, *len1;
struct tclas_element *tclas_elem;
unsigned int i;
+ struct qos_characteristics *qos_elem;
+ u32 control_info = 0;
/* SCS Descriptor element */
wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
@@ -113,6 +128,9 @@
if (desc_elem->request_type == SCS_REQ_REMOVE)
goto end;
+ if (!tclas_elem_required(&desc_elem->qos_char_elem))
+ goto skip_tclas_elem;
+
if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
wpabuf_put_u8(buf, 1);
@@ -165,6 +183,81 @@
wpabuf_put_u8(buf, desc_elem->tclas_processing);
}
+skip_tclas_elem:
+ if (allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
+ qos_elem = &desc_elem->qos_char_elem;
+ /* Element ID, Length, and Element ID Extension */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len1 = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_QOS_CHARACTERISTICS);
+
+ /* Remove invalid mask bits */
+
+ /* Medium Time is applicable only for direct link */
+ if ((qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME) &&
+ qos_elem->direction != SCS_DIRECTION_DIRECT)
+ qos_elem->mask &= ~SCS_QOS_BIT_MEDIUM_TIME;
+
+ /* Service Start Time LinkID is valid only when Service Start
+ * Time is present.
+ */
+ if ((qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID) &&
+ !(qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME))
+ qos_elem->mask &=
+ ~SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
+
+ /* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * Figure 9-1001av (Control Info field format)
+ */
+ control_info = ((u32) qos_elem->direction <<
+ EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET);
+ control_info |= ((u32) desc_elem->intra_access_priority <<
+ EHT_QOS_CONTROL_INFO_TID_OFFSET);
+ control_info |= ((u32) desc_elem->intra_access_priority <<
+ EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET);
+ control_info |= ((u32) qos_elem->mask <<
+ EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET);
+
+ /* Control Info */
+ wpabuf_put_le32(buf, control_info);
+ /* Minimum Service Interval */
+ wpabuf_put_le32(buf, qos_elem->min_si);
+ /* Maximum Service Interval */
+ wpabuf_put_le32(buf, qos_elem->max_si);
+ /* Minimum Data Rate */
+ wpabuf_put_le24(buf, qos_elem->min_data_rate);
+ /* Delay Bound */
+ wpabuf_put_le24(buf, qos_elem->delay_bound);
+
+ /* Maximum MSDU Size */
+ if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
+ wpabuf_put_le16(buf, qos_elem->max_msdu_size);
+ /* Start Service Time */
+ if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME)
+ wpabuf_put_le32(buf, qos_elem->service_start_time);
+ /* Service Start Time LinkID */
+ if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
+ wpabuf_put_u8(buf,
+ qos_elem->service_start_time_link_id);
+ /* Mean Data Rate */
+ if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
+ wpabuf_put_le24(buf, qos_elem->mean_data_rate);
+ /* Delayed Bounded Burst Size */
+ if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
+ wpabuf_put_le32(buf, qos_elem->burst_size);
+ /* MSDU Lifetime */
+ if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
+ wpabuf_put_le16(buf, qos_elem->msdu_lifetime);
+ /* MSDU Delivery Info */
+ if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
+ wpabuf_put_u8(buf, qos_elem->msdu_delivery_info);
+ /* Medium Time */
+ if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME)
+ wpabuf_put_le16(buf, qos_elem->medium_time);
+
+ *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+ }
+
end:
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
return 0;
@@ -274,8 +367,51 @@
}
+static size_t qos_char_len(const struct qos_characteristics *qos_elem)
+{
+ size_t buf_len = 0;
+
+ buf_len += 1 + /* Element ID */
+ 1 + /* Length */
+ 1 + /* Element ID Extension */
+ 4 + /* Control Info */
+ 4 + /* Minimum Service Interval */
+ 4 + /* Maximum Service Interval */
+ 3 + /* Minimum Data Rate */
+ 3; /* Delay Bound */
+
+ if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
+ buf_len += 2; /* Maximum MSDU Size */
+
+ if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME) {
+ buf_len += 4; /* Service Start Time */
+ if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
+ buf_len++; /* Service Start Time LinkID */
+ }
+
+ if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
+ buf_len += 3; /* Mean Data Rate */
+
+ if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
+ buf_len += 4; /* Delayed Bounded Burst Size */
+
+ if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
+ buf_len += 2; /* MSDU Lifetime */
+
+ if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
+ buf_len++; /* MSDU Delivery Info */
+
+ if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME &&
+ qos_elem->direction == SCS_DIRECTION_DIRECT)
+ buf_len += 2; /* Medium Time */
+
+ return buf_len;
+}
+
+
static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
- unsigned int num_scs_desc)
+ unsigned int num_scs_desc,
+ bool allow_scs_traffic_desc)
{
struct wpabuf *buf;
size_t buf_len = 0;
@@ -293,6 +429,13 @@
if (desc_elem->request_type == SCS_REQ_REMOVE)
continue;
+ if (allow_scs_traffic_desc &&
+ desc_elem->qos_char_elem.available)
+ buf_len += qos_char_len(&desc_elem->qos_char_elem);
+
+ if (!tclas_elem_required(&desc_elem->qos_char_elem))
+ continue;
+
if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
buf_len += 3;
@@ -370,8 +513,11 @@
{
struct wpabuf *buf = NULL;
struct scs_desc_elem *desc_elem = NULL;
+ const struct ieee80211_eht_capabilities *eht;
+ const u8 *eht_ie;
int ret = -1;
unsigned int i;
+ bool allow_scs_traffic_desc = false;
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
return -1;
@@ -386,8 +532,28 @@
if (!desc_elem)
return -1;
+ if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss))
+ allow_scs_traffic_desc = true;
+
+ /* Allow SCS Traffic descriptor support for EHT connection */
+ eht_ie = wpa_bss_get_ie_ext(wpa_s->current_bss,
+ WLAN_EID_EXT_EHT_CAPABILITIES);
+ if (wpa_s->connection_eht && eht_ie &&
+ eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) {
+ eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3];
+ if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC)
+ allow_scs_traffic_desc = true;
+ }
+
+ if (!allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "Connection does not support EHT/non-EHT SCS Traffic Description - could not send SCS Request with QoS Characteristics");
+ return -1;
+ }
+
buf = allocate_scs_buf(desc_elem,
- wpa_s->scs_robust_av_req.num_scs_desc);
+ wpa_s->scs_robust_av_req.num_scs_desc,
+ allow_scs_traffic_desc);
if (!buf)
return -1;
@@ -401,7 +567,8 @@
for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
i++, desc_elem++) {
/* SCS Descriptor element */
- if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0)
+ if (wpas_populate_scs_descriptor_ie(desc_elem, buf,
+ allow_scs_traffic_desc) < 0)
goto end;
}
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 238fe68..8e51717 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -1033,7 +1033,7 @@
}
os_get_reltime(&wpa_s->beacon_rep_scan);
if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) ||
- wpa_supplicant_trigger_scan(wpa_s, params))
+ wpa_supplicant_trigger_scan(wpa_s, params, true, false))
wpas_rrm_refuse_request(wpa_s);
params->duration = prev_duration;
}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index c3984a4..bab6a23 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -24,6 +24,8 @@
#include "scan.h"
#include "mesh.h"
+static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s);
+
static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
{
@@ -278,22 +280,49 @@
* wpa_supplicant_trigger_scan - Request driver to start a scan
* @wpa_s: Pointer to wpa_supplicant data
* @params: Scan parameters
+ * @default_ies: Whether or not to use the default IEs in the Probe Request
+ * frames. Note that this will free any existing IEs set in @params, so this
+ * shouldn't be set if the IEs have already been set with
+ * wpa_supplicant_extra_ies(). Otherwise, wpabuf_free() will lead to a
+ * double-free.
+ * @next: Whether or not to perform this scan as the next radio work
* Returns: 0 on success, -1 on failure
*/
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params)
+ struct wpa_driver_scan_params *params,
+ bool default_ies, bool next)
{
struct wpa_driver_scan_params *ctx;
+ struct wpabuf *ies = NULL;
if (wpa_s->scan_work) {
wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
return -1;
}
+ if (default_ies) {
+ if (params->extra_ies_len) {
+ os_free((u8 *) params->extra_ies);
+ params->extra_ies = NULL;
+ params->extra_ies_len = 0;
+ }
+ ies = wpa_supplicant_extra_ies(wpa_s);
+ if (ies) {
+ params->extra_ies = wpabuf_head(ies);
+ params->extra_ies_len = wpabuf_len(ies);
+ }
+ }
ctx = wpa_scan_clone_params(params);
+ if (ies) {
+ wpabuf_free(ies);
+ params->extra_ies = NULL;
+ params->extra_ies_len = 0;
+ }
+ wpa_s->last_scan_all_chan = !params->freqs;
+ wpa_s->last_scan_non_coloc_6ghz = params->non_coloc_6ghz;
if (!ctx ||
- radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
- {
+ radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb,
+ ctx) < 0) {
wpa_scan_free_params(ctx);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
return -1;
@@ -394,29 +423,6 @@
}
-#ifdef CONFIG_P2P
-static bool is_6ghz_supported(struct wpa_supplicant *wpa_s)
-{
- struct hostapd_channel_data *chnl;
- int i, j;
-
- for (i = 0; i < wpa_s->hw.num_modes; i++) {
- if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) {
- chnl = wpa_s->hw.modes[i].channels;
- for (j = 0; j < wpa_s->hw.modes[i].num_channels; j++) {
- if (chnl[j].flag & HOSTAPD_CHAN_DISABLED)
- continue;
- if (is_6ghz_freq(chnl[j].freq))
- return true;
- }
- }
- }
-
- return false;
-}
-#endif /* CONFIG_P2P */
-
-
static void wpa_supplicant_optimize_freqs(
struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
{
@@ -615,7 +621,7 @@
wpa_drv_get_ext_capa(wpa_s, type);
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
- sizeof(ext_capab));
+ sizeof(ext_capab), NULL);
if (ext_capab_len > 0 &&
wpabuf_resize(&default_ies, ext_capab_len) == 0)
wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
@@ -649,6 +655,67 @@
}
+static struct wpabuf * wpa_supplicant_ml_probe_ie(int mld_id, u16 links)
+{
+ struct wpabuf *extra_ie;
+ u16 control = MULTI_LINK_CONTROL_TYPE_PROBE_REQ;
+ size_t len = 3 + 4 + 4 * MAX_NUM_MLD_LINKS;
+ u8 link_id;
+ u8 *len_pos;
+
+ if (mld_id >= 0) {
+ control |= EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID;
+ len++;
+ }
+
+ extra_ie = wpabuf_alloc(len);
+ if (!extra_ie)
+ return NULL;
+
+ wpabuf_put_u8(extra_ie, WLAN_EID_EXTENSION);
+ len_pos = wpabuf_put(extra_ie, 1);
+ wpabuf_put_u8(extra_ie, WLAN_EID_EXT_MULTI_LINK);
+
+ wpabuf_put_le16(extra_ie, control);
+
+ /* common info length and MLD ID (if requested) */
+ if (mld_id >= 0) {
+ wpabuf_put_u8(extra_ie, 2);
+ wpabuf_put_u8(extra_ie, mld_id);
+
+ wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at MLD ID %d",
+ mld_id);
+ } else {
+ wpabuf_put_u8(extra_ie, 1);
+
+ wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at receiving AP");
+ }
+
+ if (!links)
+ wpa_printf(MSG_DEBUG, "MLD: Probing all links");
+ else
+ wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!(links & BIT(link_id)))
+ continue;
+
+ wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
+
+ /* Subelement length includes only the control */
+ wpabuf_put_u8(extra_ie, 2);
+
+ control = link_id | EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK;
+
+ wpabuf_put_le16(extra_ie, control);
+ }
+
+ *len_pos = (u8 *) wpabuf_put(extra_ie, 0) - len_pos - 1;
+
+ return extra_ie;
+}
+
+
static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
{
struct wpabuf *extra_ie = NULL;
@@ -659,6 +726,15 @@
enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
#endif /* CONFIG_WPS */
+ if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
+ extra_ie = wpa_supplicant_ml_probe_ie(wpa_s->ml_probe_mld_id,
+ wpa_s->ml_probe_links);
+
+ /* No other elements should be included in the probe request */
+ wpa_printf(MSG_DEBUG, "MLD: Scan including only ML element");
+ return extra_ie;
+ }
+
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
@@ -667,7 +743,7 @@
wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
- sizeof(ext_capab));
+ sizeof(ext_capab), NULL);
if (ext_capab_len > 0 &&
wpabuf_resize(&extra_ie, ext_capab_len) == 0)
wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
@@ -1114,6 +1190,10 @@
params.ssids[0].ssid = wpa_s->go_params->ssid;
params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
params.num_ssids = 1;
+ params.bssid = wpa_s->go_params->peer_interface_addr;
+ wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID " MACSTR
+ " (peer interface address) for scan",
+ MAC2STR(params.bssid));
goto ssid_list_set;
}
@@ -1124,6 +1204,12 @@
params.ssids[0].ssid_len =
wpa_s->current_ssid->ssid_len;
params.num_ssids = 1;
+ if (wpa_s->current_ssid->bssid_set) {
+ params.bssid = wpa_s->current_ssid->bssid;
+ wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID "
+ MACSTR " for scan",
+ MAC2STR(params.bssid));
+ }
} else {
wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
}
@@ -1397,8 +1483,13 @@
"Scan a previously specified BSSID " MACSTR,
MAC2STR(params.bssid));
}
+ } else if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
+ wpa_printf(MSG_DEBUG, "Scanning for ML probe request");
+ params.bssid = wpa_s->ml_probe_bssid;
+ params.min_probe_req_content = true;
}
+
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_non_coloc_6ghz) {
wpa_dbg(wpa_s, MSG_DEBUG, "Collocated 6 GHz logic is disabled");
@@ -1444,12 +1535,12 @@
}
}
- if (!params.freqs && is_6ghz_supported(wpa_s) &&
+ if (!params.freqs && wpas_is_6ghz_supported(wpa_s, true) &&
(wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
#endif /* CONFIG_P2P */
- ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
+ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params, false, false);
if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
!wpa_s->manual_scan_freqs) {
@@ -1480,6 +1571,10 @@
if (params.bssid)
os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
}
+
+ wpa_s->ml_probe_mld_id = -1;
+ wpa_s->ml_probe_links = 0;
+ os_memset(wpa_s->ml_probe_bssid, 0, sizeof(wpa_s->ml_probe_bssid));
}
@@ -2118,6 +2213,160 @@
}
+static int wpas_channel_width_offset(enum chan_width cw)
+{
+ switch (cw) {
+ case CHAN_WIDTH_40:
+ return 1;
+ case CHAN_WIDTH_80:
+ return 2;
+ case CHAN_WIDTH_80P80:
+ case CHAN_WIDTH_160:
+ return 3;
+ case CHAN_WIDTH_320:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+
+/**
+ * wpas_channel_width_tx_pwr - Calculate the max transmit power at the channel
+ * width
+ * @ies: Information elements
+ * @ies_len: Length of elements
+ * @cw: The channel width
+ * Returns: The max transmit power at the channel width, TX_POWER_NO_CONSTRAINT
+ * if it is not constrained.
+ *
+ * This function is only used to estimate the actual signal RSSI when associated
+ * based on the beacon RSSI at the STA. Beacon frames are transmitted on 20 MHz
+ * channels, while the Data frames usually use higher channel width. Therefore
+ * their RSSIs may be different. Assuming there is a fixed gap between the TX
+ * power limit of the STA defined by the Transmit Power Envelope element and the
+ * TX power of the AP, the difference in the TX power of X MHz and Y MHz at the
+ * STA equals to the difference at the AP, and the difference in the signal RSSI
+ * at the STA. tx_pwr is a floating point number in the standard, but the error
+ * of casting to int is trivial in comparing two BSSes.
+ */
+static int wpas_channel_width_tx_pwr(const u8 *ies, size_t ies_len,
+ enum chan_width cw)
+{
+#define MIN(a, b) (a < b ? a : b)
+ int offset = wpas_channel_width_offset(cw);
+ const struct element *elem;
+ int max_tx_power = TX_POWER_NO_CONSTRAINT, tx_pwr = 0;
+
+ for_each_element_id(elem, WLAN_EID_TRANSMIT_POWER_ENVELOPE, ies,
+ ies_len) {
+ int max_tx_pwr_count;
+ enum max_tx_pwr_interpretation tx_pwr_intrpn;
+ enum reg_6g_client_type client_type;
+
+ if (elem->datalen < 1)
+ continue;
+
+ /*
+ * IEEE Std 802.11ax-2021, 9.4.2.161 (Transmit Power Envelope
+ * element) defines Maximum Transmit Power Count (B0-B2),
+ * Maximum Transmit Power Interpretation (B3-B5), and Maximum
+ * Transmit Power Category (B6-B7).
+ */
+ max_tx_pwr_count = elem->data[0] & 0x07;
+ tx_pwr_intrpn = (elem->data[0] >> 3) & 0x07;
+ client_type = (elem->data[0] >> 6) & 0x03;
+
+ if (client_type != REG_DEFAULT_CLIENT)
+ continue;
+
+ if (tx_pwr_intrpn == LOCAL_EIRP ||
+ tx_pwr_intrpn == REGULATORY_CLIENT_EIRP) {
+ int offs;
+
+ max_tx_pwr_count = MIN(max_tx_pwr_count, 3);
+ offs = MIN(offset, max_tx_pwr_count) + 1;
+ if (elem->datalen <= offs)
+ continue;
+ tx_pwr = (signed char) elem->data[offs];
+ /*
+ * Maximum Transmit Power subfield is encoded as an
+ * 8-bit 2s complement signed integer in the range -64
+ * dBm to 63 dBm with a 0.5 dB step. 63.5 dBm means no
+ * local maximum transmit power constraint.
+ */
+ if (tx_pwr == 127)
+ continue;
+ tx_pwr /= 2;
+ max_tx_power = MIN(max_tx_power, tx_pwr);
+ } else if (tx_pwr_intrpn == LOCAL_EIRP_PSD ||
+ tx_pwr_intrpn == REGULATORY_CLIENT_EIRP_PSD) {
+ if (elem->datalen < 2)
+ continue;
+
+ tx_pwr = (signed char) elem->data[1];
+ /*
+ * Maximum Transmit PSD subfield is encoded as an 8-bit
+ * 2s complement signed integer. -128 indicates that the
+ * corresponding 20 MHz channel cannot be used for
+ * transmission. +127 indicates that no maximum PSD
+ * limit is specified for the corresponding 20 MHz
+ * channel.
+ */
+ if (tx_pwr == 127 || tx_pwr == -128)
+ continue;
+
+ /*
+ * The Maximum Transmit PSD subfield indicates the
+ * maximum transmit PSD for the 20 MHz channel. Suppose
+ * the PSD value is X dBm/MHz, the TX power of N MHz is
+ * X + 10*log10(N) = X + 10*log10(20) + 10*log10(N/20) =
+ * X + 13 + 3*log2(N/20)
+ */
+ tx_pwr = tx_pwr / 2 + 13 + offset * 3;
+ max_tx_power = MIN(max_tx_power, tx_pwr);
+ }
+ }
+
+ return max_tx_power;
+#undef MIN
+}
+
+
+/**
+ * Estimate the RSSI bump of channel width |cw| with respect to 20 MHz channel.
+ * If the TX power has no constraint, it is unable to estimate the RSSI bump.
+ */
+int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len,
+ enum chan_width cw)
+{
+ int max_20mhz_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len,
+ CHAN_WIDTH_20);
+ int max_cw_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len, cw);
+
+ return (max_20mhz_tx_pwr == TX_POWER_NO_CONSTRAINT ||
+ max_cw_tx_pwr == TX_POWER_NO_CONSTRAINT) ?
+ 0 : (max_cw_tx_pwr - max_20mhz_tx_pwr);
+}
+
+
+int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len,
+ enum chan_width max_cw, int snr)
+{
+ int rssi_bump = wpas_channel_width_rssi_bump(ies, ies_len, max_cw);
+ /*
+ * The noise has uniform power spectral density (PSD) across the
+ * frequency band, its power is proportional to the channel width.
+ * Suppose the PSD of noise is X dBm/MHz, the noise power of N MHz is
+ * X + 10*log10(N), and the noise power bump with respect to 20 MHz is
+ * 10*log10(N) - 10*log10(20) = 10*log10(N/20) = 3*log2(N/20)
+ */
+ int noise_bump = 3 * wpas_channel_width_offset(max_cw);
+
+ return snr + rssi_bump - noise_bump;
+}
+
+
/* Compare function for sorting scan results. Return >0 if @b is considered
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
@@ -2129,6 +2378,7 @@
struct wpa_scan_res *wb = *_wb;
int wpa_a, wpa_b;
int snr_a, snr_b, snr_a_full, snr_b_full;
+ size_t ies_len;
/* WPA/WPA2 support preferred */
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -2150,10 +2400,21 @@
return -1;
if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
- snr_a_full = wa->snr;
- snr_a = MIN(wa->snr, GREAT_SNR);
- snr_b_full = wb->snr;
- snr_b = MIN(wb->snr, GREAT_SNR);
+ /*
+ * The scan result estimates SNR over 20 MHz, while Data frames
+ * usually use wider channel width. The TX power and noise power
+ * are both affected by the channel width.
+ */
+ ies_len = wa->ie_len ? wa->ie_len : wa->beacon_ie_len;
+ snr_a_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wa + 1),
+ ies_len, wa->max_cw,
+ wa->snr);
+ snr_a = MIN(snr_a_full, GREAT_SNR);
+ ies_len = wb->ie_len ? wb->ie_len : wb->beacon_ie_len;
+ snr_b_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wb + 1),
+ ies_len, wb->max_cw,
+ wb->snr);
+ snr_b = MIN(snr_b_full, GREAT_SNR);
} else {
/* Level is not in dBm, so we can't calculate
* SNR. Just use raw level (units unknown). */
@@ -2606,11 +2867,17 @@
unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len, int rate,
- int snr, int freq)
+ int snr, int freq, enum chan_width *max_cw)
{
struct hostapd_hw_modes *hw_mode;
unsigned int est, tmp;
const u8 *ie;
+ /*
+ * No need to apply a bump to the noise here because the
+ * minsnr_bitrate_entry tables are based on MCS tables where this has
+ * been taken into account.
+ */
+ int adjusted_snr;
/* Limit based on estimated SNR */
if (rate > 1 * 2 && snr < 1)
@@ -2659,6 +2926,7 @@
if (hw_mode && hw_mode->ht_capab) {
ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
if (ie) {
+ *max_cw = CHAN_WIDTH_20;
tmp = max_ht20_rate(snr, false);
if (tmp > est)
est = tmp;
@@ -2670,7 +2938,11 @@
ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
if (ie && ie[1] >= 2 &&
(ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
- tmp = max_ht40_rate(snr, false);
+ *max_cw = CHAN_WIDTH_40;
+ adjusted_snr = snr +
+ wpas_channel_width_rssi_bump(ies, ies_len,
+ CHAN_WIDTH_40);
+ tmp = max_ht40_rate(adjusted_snr, false);
if (tmp > est)
est = tmp;
}
@@ -2682,6 +2954,8 @@
if (ie) {
bool vht80 = false, vht160 = false;
+ if (*max_cw == CHAN_WIDTH_UNKNOWN)
+ *max_cw = CHAN_WIDTH_20;
tmp = max_ht20_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
@@ -2690,7 +2964,11 @@
if (ie && ie[1] >= 2 &&
(ie[3] &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
- tmp = max_ht40_rate(snr, true) + 1;
+ *max_cw = CHAN_WIDTH_40;
+ adjusted_snr = snr +
+ wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_40);
+ tmp = max_ht40_rate(adjusted_snr, true) + 1;
if (tmp > est)
est = tmp;
}
@@ -2716,7 +2994,11 @@
}
if (vht80) {
- tmp = max_vht80_rate(snr) + 1;
+ *max_cw = CHAN_WIDTH_80;
+ adjusted_snr = snr +
+ wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_80);
+ tmp = max_vht80_rate(adjusted_snr) + 1;
if (tmp > est)
est = tmp;
}
@@ -2725,7 +3007,11 @@
(hw_mode->vht_capab &
(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
- tmp = max_vht160_rate(snr) + 1;
+ *max_cw = CHAN_WIDTH_160;
+ adjusted_snr = snr +
+ wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_160);
+ tmp = max_vht160_rate(adjusted_snr) + 1;
if (tmp > est)
est = tmp;
}
@@ -2758,6 +3044,8 @@
}
}
+ if (*max_cw == CHAN_WIDTH_UNKNOWN)
+ *max_cw = CHAN_WIDTH_20;
tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
if (tmp > est)
est = tmp;
@@ -2767,14 +3055,26 @@
if (cw &
(IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_eht_rate(he40_table, snr, is_eht) + boost;
+ if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+ *max_cw < CHAN_WIDTH_40)
+ *max_cw = CHAN_WIDTH_40;
+ adjusted_snr = snr + wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_40);
+ tmp = max_he_eht_rate(he40_table, adjusted_snr,
+ is_eht) + boost;
if (tmp > est)
est = tmp;
}
if (!IS_2P4GHZ(freq) &&
(cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
- tmp = max_he_eht_rate(he80_table, snr, is_eht) + boost;
+ if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+ *max_cw < CHAN_WIDTH_80)
+ *max_cw = CHAN_WIDTH_80;
+ adjusted_snr = snr + wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_80);
+ tmp = max_he_eht_rate(he80_table, adjusted_snr,
+ is_eht) + boost;
if (tmp > est)
est = tmp;
}
@@ -2782,7 +3082,13 @@
if (!IS_2P4GHZ(freq) &&
(cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
- tmp = max_he_eht_rate(he160_table, snr, is_eht) + boost;
+ if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+ *max_cw < CHAN_WIDTH_160)
+ *max_cw = CHAN_WIDTH_160;
+ adjusted_snr = snr + wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_160);
+ tmp = max_he_eht_rate(he160_table, adjusted_snr,
+ is_eht) + boost;
if (tmp > est)
est = tmp;
}
@@ -2795,7 +3101,12 @@
if (is_6ghz_freq(freq) &&
(eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
- tmp = max_he_eht_rate(eht320_table, snr, true);
+ if (*max_cw == CHAN_WIDTH_UNKNOWN ||
+ *max_cw < CHAN_WIDTH_320)
+ *max_cw = CHAN_WIDTH_320;
+ adjusted_snr = snr + wpas_channel_width_rssi_bump(
+ ies, ies_len, CHAN_WIDTH_320);
+ tmp = max_he_eht_rate(eht320_table, adjusted_snr, true);
if (tmp > est)
est = tmp;
}
@@ -2821,8 +3132,8 @@
if (!ie_len)
ie_len = res->beacon_ie_len;
- res->est_throughput =
- wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, res->freq);
+ res->est_throughput = wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr,
+ res->freq, &res->max_cw);
/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
}
@@ -3045,6 +3356,7 @@
params->relative_adjust_rssi = src->relative_adjust_rssi;
params->p2p_include_6ghz = src->p2p_include_6ghz;
params->non_coloc_6ghz = src->non_coloc_6ghz;
+ params->min_probe_req_content = src->min_probe_req_content;
return params;
failed:
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 30f4395..f1739fa 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -30,6 +30,14 @@
*/
#define GREAT_SNR 25
+/*
+ * IEEE Sts 802.11ax-2021, 9.4.2.161 (Transmit Power Envelope element) indicates
+ * no max TX power limit if Maximum Transmit Power field is 63.5 dBm.
+ * The default TX power if it is not constrained by Transmit Power Envelope
+ * element.
+ */
+#define TX_POWER_NO_CONSTRAINT 64
+
#define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
#define IS_5GHZ(n) (n > 4000 && n < 5895)
@@ -45,7 +53,8 @@
int scanning);
struct wpa_driver_scan_params;
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params);
+ struct wpa_driver_scan_params *params,
+ bool default_ies, bool next);
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
struct scan_info *info, int new_scan);
@@ -87,12 +96,16 @@
struct wpa_scan_res *res);
unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len, int rate,
- int snr, int freq);
+ int snr, int freq, enum chan_width *max_cw);
void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s);
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
struct wpa_driver_scan_params *params,
bool is_6ghz, bool only_6ghz_psc,
bool exclude_radar);
+int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len,
+ enum chan_width cw);
+int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len,
+ enum chan_width max_cw, int snr);
#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 8068f1e..df2c68f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -378,13 +378,111 @@
}
+static void wpas_process_tbtt_info(struct wpa_supplicant *wpa_s, const u8 *data)
+{
+ struct wpa_bss *neigh_bss;
+ const u8 *bssid;
+ u8 bss_params;
+ u8 link_id;
+
+ /* TBTT Information field
+ * Neighbor AP TBTT Offset[1]
+ * BSSID[6]
+ * Short SSID[4]
+ * BSS parameters[1]
+ * 20 MHz PSD[1]
+ * MLD Parameters[3]
+ * B0..B7: AP MLD ID
+ * B7..B11: Link ID
+ * B12..B19: BSS Parameters Change Count
+ * B20: All Updates Included
+ * B21: Disabled Link Indication */
+
+ bssid = data + 1;
+ bss_params = data[1 + ETH_ALEN + 4];
+
+ data += 13; /* MLD Parameters */
+ link_id = *(data + 1) & 0xF;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: mld ID=%u, link ID=%u, bssid=" MACSTR ", bss_params=0x%x",
+ *data, link_id, MAC2STR(bssid), bss_params);
+
+ if (*data) {
+ wpa_printf(MSG_DEBUG, "MLD: Reported link not part of MLD");
+ return;
+ }
+
+ neigh_bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (!neigh_bss) {
+ wpa_printf(MSG_DEBUG, "MLD: Neighbor not found in scan");
+ return;
+ }
+
+ if (!((bss_params & RNR_BSS_PARAM_SAME_SSID) &&
+ (bss_params & RNR_BSS_PARAM_CO_LOCATED)) &&
+ !wpa_scan_res_match(wpa_s, 0, neigh_bss, wpa_s->current_ssid,
+ 1, 0)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Neighbor doesn't match current SSID - skip link");
+ return;
+ }
+
+ wpa_s->valid_links |= BIT(link_id);
+ os_memcpy(wpa_s->links[link_id].bssid, bssid, ETH_ALEN);
+ wpa_s->links[link_id].freq = neigh_bss->freq;
+}
+
+
+static void wpas_process_rnr(struct wpa_supplicant *wpa_s, const u8 *pos,
+ size_t rnr_ie_len)
+{
+ while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
+ const struct ieee80211_neighbor_ap_info *ap_info =
+ (const struct ieee80211_neighbor_ap_info *) pos;
+ /* The first TBTT Information field */
+ const u8 *data = ap_info->data;
+ u8 tbtt_count;
+ size_t len;
+ int tbtt_i;
+
+ if (rnr_ie_len < sizeof(struct ieee80211_neighbor_ap_info))
+ break;
+
+ tbtt_count = (ap_info->tbtt_info_hdr >> 4) + 1;
+ len = sizeof(struct ieee80211_neighbor_ap_info) +
+ ap_info->tbtt_info_len * tbtt_count;
+
+ wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
+ ap_info->op_class, ap_info->channel);
+
+ if (len > rnr_ie_len)
+ break;
+
+ if (ap_info->tbtt_info_len < 16) {
+ rnr_ie_len -= len;
+ pos += len;
+ continue;
+ }
+
+ for (tbtt_i = 0; tbtt_i < tbtt_count; tbtt_i++) {
+ wpas_process_tbtt_info(wpa_s, data);
+ data += ap_info->tbtt_info_len;
+ }
+
+ rnr_ie_len -= len;
+ pos += len;
+ }
+}
+
+
static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
struct wpa_ssid *ssid)
{
struct wpabuf *mlbuf;
- const u8 *rnr_ie, *pos, *rsn_ie;
+ const u8 *rnr_ie, *rsn_ie;
struct wpa_ie_data ie;
- u8 ml_ie_len, rnr_ie_len;
+ u8 ml_ie_len;
const struct ieee80211_eht_ml *eht_ml;
const struct eht_ml_basic_common_info *ml_basic_common_info;
u8 i;
@@ -394,6 +492,7 @@
BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
bool ret = false;
+ int rnr_idx;
if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
return false;
@@ -459,69 +558,22 @@
wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
- rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
- if (!rnr_ie) {
- wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
- ret = true;
- goto out;
- }
+ ret = true;
- rnr_ie_len = rnr_ie[1];
- pos = rnr_ie + 2;
-
- while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
- const struct ieee80211_neighbor_ap_info *ap_info =
- (const struct ieee80211_neighbor_ap_info *) pos;
- const u8 *data = ap_info->data;
- size_t len = sizeof(struct ieee80211_neighbor_ap_info) +
- ap_info->tbtt_info_len;
-
- wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
- ap_info->op_class, ap_info->channel);
-
- if (len > rnr_ie_len)
- break;
-
- if (ap_info->tbtt_info_len < 16) {
- rnr_ie_len -= len;
- pos += len;
- continue;
- }
-
- data += 13;
-
- wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
- *data, *(data + 1) & 0xF);
-
- if (*data) {
- wpa_printf(MSG_DEBUG,
- "MLD: Reported link not part of MLD");
- } else {
- struct wpa_bss *neigh_bss =
- wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
- u8 link_id = *(data + 1) & 0xF;
-
- if (neigh_bss) {
- if (wpa_scan_res_match(wpa_s, 0, neigh_bss,
- wpa_s->current_ssid,
- 1, 0)) {
- wpa_s->valid_links |= BIT(link_id);
- os_memcpy(wpa_s->links[link_id].bssid,
- ap_info->data + 1, ETH_ALEN);
- wpa_s->links[link_id].freq =
- neigh_bss->freq;
- } else {
- wpa_printf(MSG_DEBUG,
- "MLD: Neighbor doesn't match current SSID - skip link");
- }
- } else {
- wpa_printf(MSG_DEBUG,
- "MLD: Neighbor not found in scan");
+ /* Process all Reduced Neighbor Report elements */
+ for (rnr_idx = 1; ; rnr_idx++) {
+ rnr_ie = wpa_bss_get_ie_nth(bss,
+ WLAN_EID_REDUCED_NEIGHBOR_REPORT,
+ rnr_idx);
+ if (!rnr_ie) {
+ if (rnr_idx == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: No RNR element");
+ goto out;
}
+ break;
}
-
- rnr_ie_len -= len;
- pos += len;
+ wpas_process_rnr(wpa_s, rnr_ie + 2, rnr_ie[1]);
}
wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
@@ -534,13 +586,21 @@
i, MAC2STR(wpa_s->links[i].bssid));
}
- ret = true;
out:
wpabuf_free(mlbuf);
return ret;
}
+static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ u16 removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
+
+ wpa_s->valid_links &= ~removed_links;
+}
+
+
static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int ie_offset)
@@ -639,6 +699,7 @@
params.mld = true;
params.mld_link_id = wpa_s->mlo_assoc_link_id;
params.ap_mld_addr = wpa_s->ap_mld_addr;
+ wpas_ml_handle_removed_links(wpa_s, bss);
}
if (wpa_s->sme.ssid_len != params.ssid_len ||
@@ -731,7 +792,7 @@
bss->bssid,
wpa_s->current_ssid,
try_opportunistic, cache_id,
- 0) == 0)
+ 0, false) == 0)
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -920,7 +981,7 @@
wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
- sizeof(ext_capab));
+ sizeof(ext_capab), bss);
if (ext_capab_len > 0) {
u8 *pos = wpa_s->sme.assoc_req_ie;
if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -1041,7 +1102,7 @@
NULL,
wpa_key_mgmt_sae(wpa_s->key_mgmt) ?
wpa_s->key_mgmt :
- (int) WPA_KEY_MGMT_SAE) == 0) {
+ (int) WPA_KEY_MGMT_SAE, false) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
@@ -1139,7 +1200,7 @@
bss->bssid,
ssid, 0,
wpa_bss_get_fils_cache_id(bss),
- 0) == 0)
+ 0, false) == 0)
wpa_printf(MSG_DEBUG,
"SME: Try to use FILS with PMKSA caching");
resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
@@ -3087,7 +3148,7 @@
params.low_priority = 1;
wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
- if (wpa_supplicant_trigger_scan(wpa_s, ¶ms))
+ if (wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, false))
wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
else
wpa_s->sme.sched_obss_scan = 1;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 72a119d..56183ff 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -614,22 +614,6 @@
wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
}
-
-static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
-{
- unsigned int i;
- struct neighbor_report *nei;
-
- for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
- nei = &wpa_s->wnm_neighbor_report_elements[i];
- if (nei->acceptable)
- return wpa_bss_get_bssid(wpa_s, nei->bssid);
- }
-
- return NULL;
-}
-
-
#ifdef CONFIG_MBO
static struct wpa_bss *
get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
@@ -724,6 +708,29 @@
#endif /* CONFIG_MBO */
+static struct wpa_bss * find_better_target(struct wpa_bss *a,
+ struct wpa_bss *b)
+{
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ if (a->est_throughput > b->est_throughput) {
+ wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR
+ " est-tput: %d B: " MACSTR " est-tput: %d",
+ MAC2STR(a->bssid), a->est_throughput,
+ MAC2STR(b->bssid), b->est_throughput);
+ return a;
+ }
+
+ wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR
+ " est-tput: %d B: " MACSTR " est-tput: %d",
+ MAC2STR(a->bssid), a->est_throughput,
+ MAC2STR(b->bssid), b->est_throughput);
+ return b;
+}
+
static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
enum mbo_transition_reject_reason *reason)
@@ -731,6 +738,8 @@
u8 i;
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_bss *target;
+ struct wpa_bss *best_target = NULL;
+ struct wpa_bss *bss_in_list = NULL;
if (!bss)
return NULL;
@@ -817,25 +826,44 @@
}
nei->acceptable = 1;
+
+ best_target = find_better_target(target, best_target);
+ if (target == bss)
+ bss_in_list = bss;
}
#ifdef CONFIG_MBO
if (wpa_s->wnm_mbo_trans_reason_present)
target = get_mbo_transition_candidate(wpa_s, reason);
else
- target = get_first_acceptable(wpa_s);
+ target = best_target;
#else /* CONFIG_MBO */
- target = get_first_acceptable(wpa_s);
+ target = best_target;
#endif /* CONFIG_MBO */
- if (target) {
- wpa_printf(MSG_DEBUG,
- "WNM: Found an acceptable preferred transition candidate BSS "
- MACSTR " (RSSI %d)",
- MAC2STR(target->bssid), target->level);
+ if (!target)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Found an acceptable preferred transition candidate BSS "
+ MACSTR " (RSSI %d, tput: %d bss-tput: %d)",
+ MAC2STR(target->bssid), target->level,
+ target->est_throughput, bss->est_throughput);
+
+ if (!bss_in_list)
+ return target;
+
+ if ((!target->est_throughput && !bss_in_list->est_throughput) ||
+ (target->est_throughput > bss_in_list->est_throughput &&
+ target->est_throughput - bss_in_list->est_throughput >
+ bss_in_list->est_throughput >> 4)) {
+ /* It is more than 100/16 percent better, so switch. */
+ return target;
}
- return target;
+ wpa_printf(MSG_DEBUG,
+ "WNM: Stay with our current BSS, not enough change in estimated throughput to switch");
+ return bss_in_list;
}
@@ -1207,6 +1235,11 @@
const struct neighbor_report *aa = a;
const struct neighbor_report *bb = b;
+ if (aa->disassoc_imminent && !bb->disassoc_imminent)
+ return 1;
+ if (bb->disassoc_imminent && !aa->disassoc_imminent)
+ return -1;
+
if (!aa->preference_present && !bb->preference_present)
return 0;
if (!aa->preference_present)
@@ -1502,9 +1535,8 @@
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
- if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
- /* TODO: mark current BSS less preferred for
- * selection */
+ if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning &&
+ (!wpa_s->current_ssid || !wpa_s->current_ssid->bssid_set)) {
wpa_printf(MSG_DEBUG, "Trying to find another BSS");
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
@@ -1538,6 +1570,12 @@
rep = &wpa_s->wnm_neighbor_report_elements[
wpa_s->wnm_num_neighbor_report];
wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+ if ((wpa_s->wnm_mode &
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT) &&
+ os_memcmp(rep->bssid, wpa_s->bssid,
+ ETH_ALEN) == 0)
+ rep->disassoc_imminent = 1;
+
wpa_s->wnm_num_neighbor_report++;
#ifdef CONFIG_MBO
if (wpa_s->wnm_mbo_trans_reason_present &&
@@ -1564,6 +1602,17 @@
return;
}
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Configuration prevents roaming (BSSID set)");
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, wpa_s->wnm_dialog_token,
+ WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+ NULL);
+ return;
+ }
+
wnm_sort_cand_list(wpa_s);
wnm_dump_cand_list(wpa_s);
valid_ms = valid_int * beacon_int * 128 / 125;
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 29625f8..e4957e4 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -37,6 +37,7 @@
u32 distance; /* valid if bearing_present=1 */
u64 bss_term_tsf; /* valid if bss_term_present=1 */
u16 bss_term_dur; /* valid if bss_term_present=1 */
+ unsigned int disassoc_imminent:1;
unsigned int preference_present:1;
unsigned int tsf_present:1;
unsigned int country_present:1;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index effc7b3..65078ed 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -551,6 +551,13 @@
}
+static int wpa_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DRIVER_FLAGS2");
+}
+
+
static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
@@ -2091,10 +2098,19 @@
#ifdef CONFIG_TESTING_OPTIONS
+
static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "DROP_SA");
}
+
+
+static int wpa_cli_cmd_ml_probe_req(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "ML_PROBE_REQ", 2, argc, argv);
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
@@ -3375,6 +3391,9 @@
{ "driver_flags", wpa_cli_cmd_driver_flags, NULL,
cli_cmd_flag_none,
"= list driver flags" },
+ { "driver_flags2", wpa_cli_cmd_driver_flags2, NULL,
+ cli_cmd_flag_none,
+ "= list driver flags2" },
{ "logon", wpa_cli_cmd_logon, NULL,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logon" },
@@ -3673,6 +3692,8 @@
#ifdef CONFIG_TESTING_OPTIONS
{ "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
"= drop SA without deauth/disassoc (test command)" },
+ { "ml_probe_req", wpa_cli_cmd_ml_probe_req, NULL, cli_cmd_flag_none,
+ "= send Multi-Link Probe request <bssid=addr> <mld_id=id> [link_id=id] (test command)" },
#endif /* CONFIG_TESTING_OPTIONS */
{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
cli_cmd_flag_none,
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a3645d1..517d37f 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -751,6 +751,11 @@
#endif /* CONFIG_PASN */
wpas_scs_deinit(wpa_s);
wpas_dscp_deinit(wpa_s);
+
+#ifdef CONFIG_OWE
+ os_free(wpa_s->owe_trans_scan_freq);
+ wpa_s->owe_trans_scan_freq = NULL;
+#endif /* CONFIG_OWE */
}
@@ -1766,7 +1771,8 @@
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) ||
+ if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) &&
+ !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA)) ||
wpas_is_sae_avoided(wpa_s, ssid, &ie))
sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
@@ -1809,6 +1815,12 @@
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with Suite B");
#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SHA384
+ } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using KEY_MGMT 802.1X with SHA384");
+#endif /* CONFIG_SHA384 */
#ifdef CONFIG_FILS
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
@@ -2094,7 +2106,8 @@
}
-static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
+static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx,
+ struct wpa_bss *bss)
{
bool scs = true, mscs = true;
@@ -2145,6 +2158,13 @@
if (wpa_s->disable_scs_support)
scs = false;
#endif /* CONFIG_TESTING_OPTIONS */
+ if (bss && !wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_SCS)) {
+ /* Drop own SCS capability indication since the AP does
+ * not support it. This is needed to avoid
+ * interoperability issues with APs that get confused
+ * with Extended Capabilities element. */
+ scs = false;
+ }
if (scs)
*pos |= 0x40; /* Bit 54 - SCS */
break;
@@ -2167,6 +2187,13 @@
if (wpa_s->disable_mscs_support)
mscs = false;
#endif /* CONFIG_TESTING_OPTIONS */
+ if (bss && !wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS)) {
+ /* Drop own MSCS capability indication since the AP does
+ * not support it. This is needed to avoid
+ * interoperability issues with APs that get confused
+ * with Extended Capabilities element. */
+ mscs = false;
+ }
if (mscs)
*pos |= 0x20; /* Bit 85 - Mirrored SCS */
break;
@@ -2174,7 +2201,8 @@
}
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf,
+ size_t buflen, struct wpa_bss *bss)
{
u8 *pos = buf;
u8 len = 11, i;
@@ -2190,7 +2218,7 @@
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
- wpas_ext_capab_byte(wpa_s, pos, i);
+ wpas_ext_capab_byte(wpa_s, pos, i, bss);
if (i < wpa_s->extended_capa_len) {
*pos &= ~wpa_s->extended_capa_mask[i];
@@ -2797,8 +2825,8 @@
int i, res;
unsigned int j;
static const int ht40plus[] = {
- 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
- 184, 192
+ 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 149, 157, 165, 173, 184, 192
};
int ht40 = -1;
@@ -3190,6 +3218,29 @@
#endif /* CONFIG_FILS */
+bool wpa_is_non_eht_scs_traffic_desc_supported(struct wpa_bss *bss)
+{
+ const u8 *wfa_capa;
+
+ if (!bss)
+ return false;
+
+ /* Get WFA capability from Beacon or Probe Response frame elements */
+ wfa_capa = wpa_bss_get_vendor_ie(bss, WFA_CAPA_IE_VENDOR_TYPE);
+ if (!wfa_capa)
+ wfa_capa = wpa_bss_get_vendor_ie_beacon(
+ bss, WFA_CAPA_IE_VENDOR_TYPE);
+
+ if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 ||
+ !(wfa_capa[7] & WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC)) {
+ /* AP does not enable QM non EHT traffic description policy */
+ return false;
+ }
+
+ return true;
+}
+
+
static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
u8 *wpa_ie, size_t wpa_ie_len,
@@ -3203,6 +3254,9 @@
if (wpa_s->enable_dscp_policy_capa)
wfa_capa[0] |= WFA_CAPA_QM_DSCP_POLICY;
+ if (wpa_is_non_eht_scs_traffic_desc_supported(bss))
+ wfa_capa[0] |= WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC;
+
if (!wfa_capa[0])
return wpa_ie_len;
@@ -3301,7 +3355,7 @@
#endif /* CONFIG_FILS */
if (pmksa_cache_set_current(wpa_s->wpa, NULL, addr,
ssid, try_opportunistic,
- cache_id, 0) == 0) {
+ cache_id, 0, false) == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
pmksa_cached = 1;
@@ -3486,7 +3540,7 @@
u8 ext_capab[18];
int ext_capab_len;
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
- sizeof(ext_capab));
+ sizeof(ext_capab), bss);
if (ext_capab_len > 0 &&
wpa_ie_len + ext_capab_len <= max_wpa_ie_len) {
u8 *pos = wpa_ie;
@@ -3582,7 +3636,8 @@
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (algs == WPA_AUTH_ALG_OPEN &&
- ssid->key_mgmt == WPA_KEY_MGMT_OWE) {
+ ssid->key_mgmt == WPA_KEY_MGMT_OWE &&
+ !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA)) {
struct wpabuf *owe_ie;
u16 group;
@@ -4265,14 +4320,17 @@
(params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
- params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384))
params.req_handshake_offload = 1;
if (wpa_s->conf->key_mgmt_offload) {
if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
- params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ params.key_mgmt_suite ==
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384)
params.req_key_mgmt_offload =
ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc : ssid->proactive_key_caching;
@@ -4291,13 +4349,25 @@
params.psk = psk;
}
+ if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA) &&
+ wpa_key_mgmt_sae(params.key_mgmt_suite)) {
+ params.auth_alg = WPA_AUTH_ALG_SAE;
+ if (ssid->sae_password) {
+ params.sae_password = ssid->sae_password;
+ params.sae_password_id = ssid->sae_password_id;
+ } else if (ssid->passphrase) {
+ params.passphrase = ssid->passphrase;
+ }
+ }
+
params.drop_unencrypted = use_crypt;
params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
struct wpa_ie_data ie;
- if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
+ if (!wpas_driver_bss_selection(wpa_s) && rsn &&
+ wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
@@ -4462,6 +4532,8 @@
{
struct wpa_ssid *old_ssid;
+ wpa_s->ml_connect_probe_ssid = NULL;
+ wpa_s->ml_connect_probe_bss = NULL;
wpas_connect_work_done(wpa_s);
wpa_clear_keys(wpa_s, addr);
old_ssid = wpa_s->current_ssid;
@@ -4996,10 +5068,14 @@
}
}
+#ifndef CONFIG_PKCS11_ENGINE_PATH
os_free(wpa_s->conf->pkcs11_engine_path);
- os_free(wpa_s->conf->pkcs11_module_path);
wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
+ os_free(wpa_s->conf->pkcs11_module_path);
wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
@@ -5256,8 +5332,9 @@
#ifdef CONFIG_OWE
if (!wpas_network_disabled(wpa_s, entry) &&
- owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
- entry->ssid_len) &&
+ (entry->ssid &&
+ owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
+ entry->ssid_len)) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
@@ -5774,6 +5851,7 @@
dl_list_init(&wpa_s->drv_signal_override);
#endif /* CONFIG_TESTING_OPTIONS */
dl_list_init(&wpa_s->active_scs_ids);
+ wpa_s->ml_probe_mld_id = -1;
return wpa_s;
}
@@ -7121,6 +7199,7 @@
wpa_s->hw_capab == CAPAB_NO_HT_VHT)
wpa_s->hw_capab = CAPAB_HT;
}
+ wpa_s->support_6ghz = wpas_is_6ghz_supported(wpa_s, false);
}
capa_res = wpa_drv_get_capa(wpa_s, &capa);
@@ -7167,6 +7246,9 @@
#ifdef CONFIG_PASN
wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2);
#endif /* CONFIG_PASN */
+ wpa_sm_set_driver_bss_selection(wpa_s->wpa,
+ !!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_BSS_SELECTION));
if (wpa_s->max_remain_on_chan == 0)
wpa_s->max_remain_on_chan = 1000;
@@ -8781,6 +8863,7 @@
wpa_s->disconnected = 1;
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
+ wpas_abort_ongoing_scan(wpa_s);
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
radio_remove_works(wpa_s, "connect", 0);
@@ -9310,3 +9393,25 @@
return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
bssid, data, data_len, no_cck);
}
+
+
+bool wpas_is_6ghz_supported(struct wpa_supplicant *wpa_s, bool only_enabled)
+{
+ struct hostapd_channel_data *chnl;
+ int i, j;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) {
+ chnl = wpa_s->hw.modes[i].channels;
+ for (j = 0; j < wpa_s->hw.modes[i].num_channels; j++) {
+ if (only_enabled &&
+ (chnl[j].flag & HOSTAPD_CHAN_DISABLED))
+ continue;
+ if (is_6ghz_freq(chnl[j].freq))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index a8abf9a..f6e4f83 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -503,12 +503,17 @@
# 0 = use permanent MAC address
# 1 = use random MAC address for each ESS connection
# 2 = like 1, but maintain OUI (with local admin bit set)
+# 3 = use dedicated/pregenerated MAC address (see mac_value)
#
# By default, permanent MAC address is used unless policy is changed by
# the per-network mac_addr parameter. Global mac_addr=1 can be used to
# change this default behavior.
#mac_addr=0
+# Local MAC address to use whenever connecting with this network profile
+# This is used with mac_addr=3.
+#mac_value=02:12:34:56:78:9a
+
# Lifetime of random MAC address in seconds (default: 60)
#rand_addr_lifetime=60
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 01b3148..4b13f3e 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -598,6 +598,40 @@
};
+struct qos_characteristics {
+ bool available;
+
+ /* Control Info Direction */
+ u8 direction;
+ /* Presence Bitmap Of Additional Parameters */
+ u16 mask;
+ /* Minimum Service Interval */
+ u32 min_si;
+ /* Maximum Service Interval */
+ u32 max_si;
+ /* Minimum Data Rate */
+ u32 min_data_rate;
+ /* Delay Bound */
+ u32 delay_bound;
+ /* Maximum MSDU Size */
+ u16 max_msdu_size;
+ /* Service Start Time */
+ u32 service_start_time;
+ /* Service Start Time LinkID */
+ u8 service_start_time_link_id;
+ /* Mean Data Rate */
+ u32 mean_data_rate;
+ /* Delayed Bounded Burst Size */
+ u32 burst_size;
+ /* MSDU Lifetime */
+ u16 msdu_lifetime;
+ /* MSDU Delivery Info */
+ u8 msdu_delivery_info;
+ /* Medium Time */
+ u16 medium_time;
+};
+
+
struct scs_desc_elem {
u8 scs_id;
enum scs_request_type request_type;
@@ -606,6 +640,7 @@
struct tclas_element *tclas_elems;
unsigned int num_tclas_elem;
u8 tclas_processing;
+ struct qos_characteristics qos_char_elem;
};
@@ -886,6 +921,10 @@
unsigned int suitable_network;
unsigned int no_suitable_network;
+ u8 ml_probe_bssid[ETH_ALEN];
+ int ml_probe_mld_id;
+ u16 ml_probe_links;
+
u64 drv_flags;
u64 drv_flags2;
unsigned int drv_enc;
@@ -1553,8 +1592,22 @@
unsigned int enable_dscp_policy_capa:1;
unsigned int connection_dscp:1;
unsigned int wait_for_dscp_req:1;
+ bool is_6ghz_enabled;
+ bool crossed_6ghz_dom;
+ bool last_scan_all_chan;
+ bool last_scan_non_coloc_6ghz;
+ bool support_6ghz;
struct wpa_signal_info last_signal_info;
+
+ struct wpa_ssid *ml_connect_probe_ssid;
+ struct wpa_bss *ml_connect_probe_bss;
+
+#ifdef CONFIG_OWE
+ /* An array of frequencies to scan for OWE transition mode BSSs when
+ * owe_transition_search == 1 */
+ int *owe_trans_scan_freq;
+#endif /* CONFIG_OWE */
};
@@ -1675,8 +1728,10 @@
size_t ssid_len);
void wpas_request_connection(struct wpa_supplicant *wpa_s);
void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen,
+ struct wpa_bss *bss);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
+ enum wpas_mac_addr_style style,
struct wpa_ssid *ssid);
int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
void add_freq(int *freqs, int *num_freqs, int freq);
@@ -1956,5 +2011,8 @@
void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s,
struct pasn_auth *pasn_auth);
void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status);
+bool wpas_is_6ghz_supported(struct wpa_supplicant *wpa_s, bool only_enabled);
+
+bool wpa_is_non_eht_scs_traffic_desc_supported(struct wpa_bss *bss);
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 0047531..9804b91 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -785,12 +785,12 @@
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf,
- size_t len)
+ size_t len, int link_id)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
status_code, peer_capab, initiator, buf,
- len);
+ len, link_id);
}
@@ -811,7 +811,9 @@
const struct ieee80211_he_6ghz_band_cap *he_6ghz_he_capab,
u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len,
const u8 *supp_channels, size_t supp_channels_len,
- const u8 *supp_oper_classes, size_t supp_oper_classes_len)
+ const u8 *supp_oper_classes, size_t supp_oper_classes_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len, int mld_link_id)
{
struct wpa_supplicant *wpa_s = ctx;
struct hostapd_sta_add_params params;
@@ -846,6 +848,9 @@
params.supp_channels_len = supp_channels_len;
params.supp_oper_classes = supp_oper_classes;
params.supp_oper_classes_len = supp_oper_classes_len;
+ params.eht_capab = eht_capab;
+ params.eht_capab_len = eht_capab_len;
+ params.mld_link_id = mld_link_id;
return wpa_drv_sta_add(wpa_s, ¶ms);
}
@@ -1224,9 +1229,15 @@
ctx->get_config_blob = wpa_supplicant_get_config_blob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
ctx->aborted_cached = wpa_supplicant_aborted_cached;
+#ifndef CONFIG_OPENSC_ENGINE_PATH
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
ctx->wps = wpa_s->wps;
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
@@ -1375,7 +1386,8 @@
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_IEEE8021X_SHA256)) &&
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA384)) &&
(ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
(ssid->group_cipher & WPA_CIPHER_TKIP))) {
disable_wpa_wpa2(ssid);