Revert "Revert "[wpa_supplicant] cumilative patch from commit 4b..."
Revert submission 28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR
Fixed the regression issue (ag/28389573)
Bug: 329004037
Reverted changes: /q/submissionid:28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR
Test: Turn ON/OFF SoftAp
Change-Id: Ie7ea1ee7f8b1311fce280907d37a2e321542f547
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 93151f2..8b79dd5 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -224,6 +224,7 @@
"src/common/ieee802_11_common.c",
"src/common/wpa_common.c",
"src/common/hw_features_common.c",
+ "src/common/ptksa_cache.c",
"src/eapol_auth/eapol_auth_sm.c",
"src/eapol_auth/eapol_auth_dump.c",
"src/ap/vlan_init.c",
@@ -315,6 +316,7 @@
"src/ap/ap_list.c",
"src/ap/comeback_token.c",
"src/pasn/pasn_responder.c",
+ "src/pasn/pasn_common.c",
"src/ap/ieee802_11.c",
"src/ap/hw_features.c",
"src/ap/dfs.c",
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index d916488..d5d1190 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -170,6 +170,7 @@
OBJS += src/common/ieee802_11_common.c
OBJS += src/common/wpa_common.c
OBJS += src/common/hw_features_common.c
+OBJS += src/common/ptksa_cache.c
OBJS += src/eapol_auth/eapol_auth_sm.c
@@ -614,7 +615,6 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
endif
ifdef CONFIG_EAP_IKEV2
@@ -667,6 +667,11 @@
OBJS += src/eap_common/chap.c
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+L_CFLAGS += -DCONFIG_RADIUS_TLS
+endif
+
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
@@ -1066,6 +1071,7 @@
OBJS += src/ap/ap_list.c
OBJS += src/ap/comeback_token.c
OBJS += src/pasn/pasn_responder.c
+OBJS += src/pasn/pasn_common.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 274a82d..489922c 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -84,6 +84,7 @@
OBJS += ../src/ap/bss_load.o
OBJS += ../src/ap/neighbor_db.o
OBJS += ../src/ap/rrm.o
+OBJS += ../src/common/ptksa_cache.o
OBJS_c = hostapd_cli.o
OBJS_c += ../src/common/wpa_ctrl.o
@@ -624,7 +625,6 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
endif
ifdef CONFIG_EAP_IKEV2
@@ -686,6 +686,11 @@
OBJS += ../src/eap_common/chap.o
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
+
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
@@ -1196,6 +1201,7 @@
OBJS += ../src/ap/ap_list.o
OBJS += ../src/ap/comeback_token.o
OBJS += ../src/pasn/pasn_responder.o
+OBJS += ../src/pasn/pasn_common.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
diff --git a/hostapd/android.config b/hostapd/android.config
index 9d20c8d..4040d41 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -126,6 +126,9 @@
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
+# Include support fo RADIUS/TLS into the RADIUS client
+#CONFIG_RADIUS_TLS=y
+
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 79cc1c2..76e9249 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1678,6 +1678,8 @@
return 0;
}
+#endif /* CONFIG_INTERWORKING */
+
static int parse_qos_map_set(struct hostapd_bss_config *bss,
char *buf, int line)
@@ -1719,8 +1721,6 @@
return 0;
}
-#endif /* CONFIG_INTERWORKING */
-
#ifdef CONFIG_HS20
static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
@@ -2869,6 +2869,37 @@
os_free(bss->radius->auth_server->shared_secret);
bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->auth_server->shared_secret_len = len;
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_type") == 0) {
+ if (os_strcmp(pos, "UDP") == 0) {
+ bss->radius->auth_server->tls = false;
+#ifdef CONFIG_RADIUS_TLS
+ } else if (os_strcmp(pos, "TLS") == 0) {
+ bss->radius->auth_server->tls = true;
+#endif /* CONFIG_RADIUS_TLS */
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
+ line, pos);
+ return 1;
+ }
+#ifdef CONFIG_RADIUS_TLS
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_ca_cert") == 0) {
+ os_free(bss->radius->auth_server->ca_cert);
+ bss->radius->auth_server->ca_cert = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_client_cert") == 0) {
+ os_free(bss->radius->auth_server->client_cert);
+ bss->radius->auth_server->client_cert = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_private_key") == 0) {
+ os_free(bss->radius->auth_server->private_key);
+ bss->radius->auth_server->private_key = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_private_key_passwd") == 0) {
+ os_free(bss->radius->auth_server->private_key_passwd);
+ bss->radius->auth_server->private_key_passwd = os_strdup(pos);
+#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "acct_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->acct_servers,
@@ -2903,6 +2934,37 @@
os_free(bss->radius->acct_server->shared_secret);
bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->acct_server->shared_secret_len = len;
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_type") == 0) {
+ if (os_strcmp(pos, "UDP") == 0) {
+ bss->radius->acct_server->tls = false;
+#ifdef CONFIG_RADIUS_TLS
+ } else if (os_strcmp(pos, "TLS") == 0) {
+ bss->radius->acct_server->tls = true;
+#endif /* CONFIG_RADIUS_TLS */
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
+ line, pos);
+ return 1;
+ }
+#ifdef CONFIG_RADIUS_TLS
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_ca_cert") == 0) {
+ os_free(bss->radius->acct_server->ca_cert);
+ bss->radius->acct_server->ca_cert = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_client_cert") == 0) {
+ os_free(bss->radius->acct_server->client_cert);
+ bss->radius->acct_server->client_cert = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_private_key") == 0) {
+ os_free(bss->radius->acct_server->private_key);
+ bss->radius->acct_server->private_key = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_private_key_passwd") == 0) {
+ os_free(bss->radius->acct_server->private_key_passwd);
+ bss->radius->acct_server->private_key_passwd = os_strdup(pos);
+#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
bss->radius->retry_primary_interval = atoi(pos);
} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
@@ -4196,10 +4258,10 @@
bss->gas_frag_limit = val;
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
bss->gas_comeback_delay = atoi(pos);
+#endif /* CONFIG_INTERWORKING */
} else if (os_strcmp(buf, "qos_map_set") == 0) {
if (parse_qos_map_set(bss, pos, line) < 0)
return 1;
-#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_RADIUS_TEST
} else if (os_strcmp(buf, "dump_msk_file") == 0) {
os_free(bss->dump_msk_file);
@@ -4604,6 +4666,10 @@
WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+ } else if (os_strcmp(buf, "rrm_link_measurement_report") == 0) {
+ if (atoi(pos))
+ bss->radio_measurements[0] |=
+ WLAN_RRM_CAPS_LINK_MEASUREMENT;
} else if (os_strcmp(buf, "gas_address3") == 0) {
bss->gas_address3 = atoi(pos);
} else if (os_strcmp(buf, "stationary_ap") == 0) {
@@ -4744,6 +4810,36 @@
}
bss->multi_ap = val;
+ } else if (os_strcmp(buf, "multi_ap_profile") == 0) {
+ int val = atoi(pos);
+
+ if (val < MULTI_AP_PROFILE_1 || val > MULTI_AP_PROFILE_MAX) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_profile '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_profile = val;
+ } else if (os_strcmp(buf, "multi_ap_client_disallow") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 3) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_client_allow '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_client_disallow = val;
+ } else if (os_strcmp(buf, "multi_ap_vlanid") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > MAX_VLAN_ID) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_vlan_id '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_vlanid = val;
} else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) {
conf->rssi_reject_assoc_rssi = atoi(pos);
} else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
@@ -4956,8 +5052,6 @@
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",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index a6b16c2..d07567d 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1309,6 +1309,8 @@
hostapd_disassoc_deny_mac(hapd);
} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
hostapd_disassoc_accept_mac(hapd);
+ } else if (os_strcasecmp(cmd, "ssid") == 0) {
+ hostapd_neighbor_sync_own_report(hapd);
} else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
os_strncmp(cmd, "wmm_ac_", 7) == 0) {
hapd->parameter_set_count++;
@@ -1946,7 +1948,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap)
- addr = hapd->mld_addr;
+ addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
hapd->l2_test = l2_packet_init(ifname, addr,
ETHERTYPE_IP, hostapd_data_test_rx,
@@ -2642,6 +2644,8 @@
unsigned int i;
int bandwidth;
u8 chan;
+ unsigned int num_err = 0;
+ int err = 0;
ret = hostapd_parse_csa_settings(pos, &settings);
if (ret)
@@ -2715,6 +2719,7 @@
settings.freq_params.center_freq1);
/* Perform CAC and switch channel */
+ iface->is_ch_switch_dfs = true;
hostapd_switch_channel_fallback(iface, &settings.freq_params);
return 0;
}
@@ -2725,15 +2730,14 @@
hostapd_chan_switch_config(iface->bss[i],
&settings.freq_params);
- ret = hostapd_switch_channel(iface->bss[i], &settings);
- if (ret) {
- /* FIX: What do we do if CSA fails in the middle of
- * submitting multi-BSS CSA requests? */
- return ret;
+ err = hostapd_switch_channel(iface->bss[i], &settings);
+ if (err) {
+ ret = err;
+ num_err++;
}
}
- return 0;
+ return (iface->num_bss == num_err) ? ret : 0;
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
@@ -3207,6 +3211,26 @@
}
+static int hostapd_ctrl_iface_req_link_measurement(struct hostapd_data *hapd,
+ const char *cmd, char *reply,
+ size_t reply_size)
+{
+ u8 addr[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: REQ_LINK_MEASUREMENT: Invalid MAC address");
+ return -1;
+ }
+
+ ret = hostapd_send_link_measurement_req(hapd, addr);
+ if (ret >= 0)
+ ret = os_snprintf(reply, reply_size, "%d", ret);
+ return ret;
+}
+
+
static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
@@ -3477,10 +3501,8 @@
for (i = 0; i < iface->interfaces->count; ++i) {
struct hostapd_iface *h_iface = iface->interfaces->iface[i];
struct hostapd_data *h_hapd = h_iface->bss[0];
- struct hostapd_bss_config *h_conf = h_hapd->conf;
- if (!h_conf->mld_ap ||
- h_conf->mld_id != iface->bss[0]->conf->mld_id)
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
continue;
if (hostapd_enable_iface(h_iface)) {
@@ -3504,7 +3526,6 @@
static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
{
unsigned int i;
- struct hostapd_iface *first_iface = NULL;
if (!iface || !iface->bss[0]->conf->mld_ap) {
wpa_printf(MSG_ERROR,
@@ -3518,32 +3539,19 @@
for (i = 0; i < iface->interfaces->count; ++i) {
struct hostapd_iface *h_iface = iface->interfaces->iface[i];
struct hostapd_data *h_hapd = h_iface->bss[0];
- struct hostapd_bss_config *h_conf = h_hapd->conf;
- if (!h_conf->mld_ap ||
- h_conf->mld_id != iface->bss[0]->conf->mld_id)
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
continue;
- if (!h_hapd->mld_first_bss) {
- first_iface = h_iface;
- continue;
- }
hostapd_disable_iface_bss(iface);
}
- if (first_iface)
- hostapd_disable_iface_bss(first_iface);
-
/* Then, fully disable interfaces */
-
for (i = 0; i < iface->interfaces->count; ++i) {
struct hostapd_iface *h_iface = iface->interfaces->iface[i];
struct hostapd_data *h_hapd = h_iface->bss[0];
- struct hostapd_bss_config *h_conf = h_hapd->conf;
- if (!h_conf->mld_ap ||
- h_conf->mld_id != iface->bss[0]->conf->mld_id ||
- !h_hapd->mld_first_bss)
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
continue;
if (hostapd_disable_iface(h_iface)) {
@@ -3552,11 +3560,6 @@
}
}
- if (first_iface && hostapd_disable_iface(first_iface)) {
- wpa_printf(MSG_ERROR, "Disabling AP MLD failed");
- return -1;
- }
-
return 0;
}
@@ -4206,6 +4209,9 @@
} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
reply, reply_size);
+ } else if (os_strncmp(buf, "REQ_LINK_MEASUREMENT ", 21) == 0) {
+ reply_len = hostapd_ctrl_iface_req_link_measurement(
+ hapd, buf + 21, reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
@@ -5362,7 +5368,7 @@
reply_len = -1;
} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
reply_len = hostapd_global_ctrl_iface_interfaces(
- interfaces, buf + 10, reply, sizeof(buffer));
+ interfaces, buf + 10, reply, reply_size);
} else if (os_strcmp(buf, "TERMINATE") == 0) {
eloop_terminate();
} else {
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 5d769e9..43cf99f 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -141,6 +141,9 @@
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
+# Include support fo RADIUS/TLS into the RADIUS client
+#CONFIG_RADIUS_TLS=y
+
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 1357649..e34d75c 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1071,9 +1071,6 @@
# 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
@@ -1581,6 +1578,16 @@
#acct_server_port=1813
#acct_server_shared_secret=secret2
+# RADIUS/TLS instead of RADIUS/UDP
+#auth_server_addr=127.0.0.1
+#auth_server_port=2083
+#auth_server_type=TLS
+#auth_server_shared_secret=radsec
+#auth_server_ca_cert=<path to trusted CA certificate(s)>
+#auth_server_client_cert=<path to client certificate>
+#auth_server_private_key=<path to private key>
+#auth_server_private_key_passwd=<password for decrypting private key>
+
# Retry interval for trying to return to the primary RADIUS server (in
# seconds). RADIUS client code will automatically try to use the next server
# when the current server is not replying to requests. If this interval is set,
@@ -2555,6 +2562,23 @@
#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#multi_ap_backhaul_wpa_passphrase=secret passphrase
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile (default: 2)
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
+# Multi-AP client disallow
+# Used to disallow profile specific backhaul STA association
+# Bitmap of the disallowed Profile-X profiles
+# 1 = Profile-1 Backhaul STA association disallowed
+# 2 = Profile-2 Backhaul STA association disallowed
+#multi_ap_client_disallow=0
+
+# Multi-AP VLAN ID
+# A valid non-zero VLAN ID will be used to update Default IEEE 802.1Q Setting
+#multi_ap_vlanid=0
+
# WPS UPnP interface
# If set, support for external Registrars is enabled.
#upnp_iface=br0
@@ -3088,6 +3112,9 @@
# Enable neighbor report via radio measurements
#rrm_neighbor_report=1
+# Enable link measurement report via radio measurements
+#rrm_link_measurement_report=1
+
# Enable beacon report via radio measurements
#rrm_beacon_report=1
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 8fb6119..e1fe286 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1215,14 +1215,14 @@
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "ENABLE");
}
static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOAD");
}
@@ -1243,7 +1243,7 @@
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "DISABLE");
}
@@ -1257,19 +1257,26 @@
static int hostapd_cli_cmd_disable_mld(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "DISABLE_MLD");
}
static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
}
+static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "STOP_AP");
+}
+
+
static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
@@ -1598,6 +1605,13 @@
}
+static int hostapd_cli_cmd_req_link_measurement(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "REQ_LINK_MEASUREMENT", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1759,6 +1773,8 @@
"= disable AP MLD to which the interface is affiliated" },
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
"= update Beacon frame contents\n"},
+ { "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ "= stop AP\n"},
{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
"= drop all ERP keys"},
{ "log_level", hostapd_cli_cmd_log_level, NULL,
@@ -1838,6 +1854,8 @@
"<addr> = poll a STA to check connectivity with a QoS null frame" },
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
+ { "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL,
+ "<addr> = send a link measurement report request to a station"},
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" },
#ifdef CONFIG_IEEE80211R_AP
diff --git a/hostapd/main.c b/hostapd/main.c
index 0fe2d74..3a14381 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -177,9 +177,15 @@
continue;
}
- if (!hconf->mld_ap || hconf->mld_id != conf->mld_id) {
+ if (!hconf->mld_ap) {
wpa_printf(MSG_DEBUG,
- "MLD: Skip non matching mld_id");
+ "MLD: Skip non-MLD");
+ continue;
+ }
+
+ if (!hostapd_is_ml_partner(hapd, h_hapd)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip non matching MLD vif name");
continue;
}
@@ -191,6 +197,7 @@
}
hapd->drv_priv = h_hapd->drv_priv;
+ hapd->interface_added = h_hapd->interface_added;
/*
* All interfaces participating in the AP MLD would have
@@ -200,20 +207,15 @@
* 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);
+ os_memcpy(hapd->own_addr, h_hapd->mld->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++;
+ hostapd_mld_add_link(hapd);
goto setup_mld;
}
@@ -290,13 +292,14 @@
* 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++;
+ os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
+
if (!b)
random_mac_addr_keep_oui(hapd->own_addr);
else
os_memcpy(hapd->own_addr, b, ETH_ALEN);
+
+ hostapd_mld_add_link(hapd);
}
setup_mld:
@@ -308,6 +311,7 @@
iface->drv_flags = capa.flags;
iface->drv_flags2 = capa.flags2;
+ iface->drv_rrm_flags = capa.rrm_flags;
iface->probe_resp_offloads = capa.probe_resp_offloads;
/*
* Use default extended capa values from per-radio information
@@ -350,7 +354,7 @@
wpa_printf(MSG_DEBUG,
"MLD: Set link_id=%u, mld_addr=" MACSTR
", own_addr=" MACSTR,
- hapd->mld_link_id, MAC2STR(hapd->mld_addr),
+ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
MAC2STR(hapd->own_addr));
hostapd_drv_link_add(hapd, hapd->mld_link_id,
@@ -758,6 +762,29 @@
}
+static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ size_t i;
+
+ if (!interfaces || !interfaces->mld)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ if (!interfaces->mld[i])
+ continue;
+
+ os_free(interfaces->mld[i]);
+ interfaces->mld[i] = NULL;
+ }
+
+ os_free(interfaces->mld);
+ interfaces->mld = NULL;
+ interfaces->mld_count = 0;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
@@ -1043,6 +1070,8 @@
interfaces.iface = NULL;
interfaces.count = 0;
+ hostapd_global_cleanup_mld(&interfaces);
+
#ifdef CONFIG_DPP
dpp_global_deinit(interfaces.dpp);
#endif /* CONFIG_DPP */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 445d963..9aa61fa 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -163,6 +163,8 @@
/* Default to strict CRL checking. */
bss->check_crl_strict = 1;
+ bss->multi_ap_profile = MULTI_AP_PROFILE_2;
+
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
bss->test_assoc_comeback_type = -1;
@@ -557,6 +559,10 @@
for (i = 0; i < num_servers; i++) {
os_free(servers[i].shared_secret);
+ os_free(servers[i].ca_cert);
+ os_free(servers[i].client_cert);
+ os_free(servers[i].private_key);
+ os_free(servers[i].private_key_passwd);
}
os_free(servers);
}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 69db16d..4f2164d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -800,6 +800,14 @@
#define BACKHAUL_BSS 1
#define FRONTHAUL_BSS 2
int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+ int multi_ap_profile;
+ /* Multi-AP Profile-1 clients not allowed to connect */
+#define PROFILE1_CLIENT_ASSOC_DISALLOW BIT(0)
+ /* Multi-AP Profile-2 clients not allowed to connect */
+#define PROFILE2_CLIENT_ASSOC_DISALLOW BIT(1)
+ unsigned int multi_ap_client_disallow;
+ /* Primary VLAN ID to use in Multi-AP */
+ int multi_ap_vlanid;
#ifdef CONFIG_AIRTIME_POLICY
unsigned int airtime_weight;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 60d66e4..11fe39c 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -265,8 +265,8 @@
}
-static bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
- struct sta_info *sta)
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
{
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta) &&
@@ -572,12 +572,33 @@
}
+#ifdef CONFIG_IEEE80211BE
+int hostapd_if_link_remove(struct hostapd_data *hapd,
+ enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_remove)
+ return -1;
+
+ return hapd->driver->link_remove(hapd->drv_priv, type, ifname,
+ hapd->mld_link_id);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->if_remove == NULL)
return -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ return hostapd_if_link_remove(hapd, type, ifname,
+ hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
}
@@ -629,7 +650,7 @@
&cmode->he_capab[IEEE80211_MODE_AP] : NULL,
cmode ?
&cmode->eht_capab[IEEE80211_MODE_AP] :
- NULL))
+ NULL, hostapd_get_punct_bitmap(hapd)))
return -1;
if (hapd->driver == NULL)
@@ -758,6 +779,8 @@
struct wpa_scan_results * hostapd_driver_get_scan_results(
struct hostapd_data *hapd)
{
+ if (hapd->driver && hapd->driver->get_scan_results)
+ return hapd->driver->get_scan_results(hapd->drv_priv, NULL);
if (hapd->driver && hapd->driver->get_scan_results2)
return hapd->driver->get_scan_results2(hapd->drv_priv);
return NULL;
@@ -840,7 +863,7 @@
link_id = hapd->mld_link_id;
if (ap_sta_is_mld(hapd, sta))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
}
#endif /* CONFIG_IEEE80211BE */
@@ -861,7 +884,7 @@
struct sta_info *sta = ap_get_sta(hapd, addr);
if (ap_sta_is_mld(hapd, sta))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
}
#endif /* CONFIG_IEEE80211BE */
@@ -919,7 +942,7 @@
sta = ap_get_sta(hapd, dst);
if (ap_sta_is_mld(hapd, sta)) {
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
bssid = own_addr;
}
#endif /* CONFIG_IEEE80211BE */
@@ -977,7 +1000,8 @@
center_segment1,
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP],
- &cmode->eht_capab[IEEE80211_MODE_AP])) {
+ &cmode->eht_capab[IEEE80211_MODE_AP],
+ hostapd_get_punct_bitmap(hapd))) {
wpa_printf(MSG_ERROR, "Can't set freq params");
return -1;
}
@@ -999,7 +1023,8 @@
int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
const u8 *qos_map_set, u8 qos_map_set_len)
{
- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
+ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
return 0;
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
qos_map_set_len);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 331b0ea..d7e79c8 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -26,6 +26,8 @@
struct wpabuf *assocresp);
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta);
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
@@ -59,6 +61,9 @@
const char *bridge, int use_existing);
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
+int hostapd_if_link_remove(struct hostapd_data *hapd,
+ enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
struct wpa_bss_params *params);
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
@@ -388,9 +393,15 @@
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
{
+ int link_id = -1;
+
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
return 0;
- return hapd->driver->stop_ap(hapd->drv_priv);
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+ return hapd->driver->stop_ap(hapd->drv_priv, link_id);
}
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
@@ -443,15 +454,28 @@
#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);
+ return hapd->driver->link_add(hapd->drv_priv, link_id, addr, hapd);
}
+
+static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (!hapd->conf->mld_ap || !hapd->driver || !hapd->drv_priv ||
+ !hapd->driver->link_sta_remove)
+ return -1;
+
+ return hapd->driver->link_sta_remove(hapd->drv_priv, hapd->mld_link_id,
+ addr);
+}
+
#endif /* CONFIG_IEEE80211BE */
#endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 1488dcc..6ed4d06 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -107,13 +107,20 @@
struct radius_server_conf srv;
struct hostapd_bss_config *conf = hapd->conf;
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
wpa_printf(MSG_DEBUG,
"MLD: Using RADIUS server of the first BSS");
- hapd->radius_srv = hapd->mld_first_bss->radius_srv;
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
+ hapd->radius_srv = first->radius_srv;
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
@@ -249,18 +256,25 @@
int authsrv_init(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
#ifdef EAP_TLS_FUNCS
- hapd->ssl_ctx = hapd->mld_first_bss->ssl_ctx;
+ hapd->ssl_ctx = first->ssl_ctx;
#endif /* EAP_TLS_FUNCS */
- hapd->eap_cfg = hapd->mld_first_bss->eap_cfg;
+ hapd->eap_cfg = first->eap_cfg;
#ifdef EAP_SIM_DB
- hapd->eap_sim_db_priv = hapd->mld_first_bss->eap_sim_db_priv;
+ hapd->eap_sim_db_priv = first->eap_sim_db_priv;
#endif /* EAP_SIM_DB */
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef EAP_TLS_FUNCS
if (hapd->conf->eap_server &&
@@ -376,7 +390,8 @@
void authsrv_deinit(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
wpa_printf(MSG_DEBUG,
"MLD: Deinit auth_serv of a non-first BSS");
@@ -390,6 +405,7 @@
#endif /* EAP_TLS_FUNCS */
return;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef RADIUS_SERVER
radius_server_deinit(hapd->radius_srv);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index e50f0a0..32865f6 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -960,7 +960,7 @@
* We want to include the AP MLD ID in the response if it was
* included in the request.
*/
- probed_mld_id = mld_id != -1 ? mld_id : hapd->conf->mld_id;
+ probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
for_each_mld_link(link, i, j, hapd->iface->interfaces,
probed_mld_id) {
@@ -2616,7 +2616,8 @@
hostapd_get_oper_centr_freq_seg1_idx(iconf),
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP],
- &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
+ &cmode->eht_capab[IEEE80211_MODE_AP],
+ hostapd_get_punct_bitmap(hapd)) == 0)
params.freq = &freq;
for (i = 0; i < hapd->iface->num_hw_features; i++) {
@@ -2675,8 +2676,7 @@
continue;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == other->bss[0]->conf->mld_id)
+ if (hostapd_is_ml_partner(hapd, other->bss[0]))
mld_ap = true;
#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 5378671..eac0606 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -348,11 +348,15 @@
if (sta->supp_op_classes &&
buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
- len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+ res = os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->supp_op_classes + 1,
sta->supp_op_classes[0]);
- len += os_snprintf(buf + len, buflen - len, "\n");
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
if (sta->power_capab) {
@@ -364,6 +368,34 @@
len += ret;
}
+#ifdef CONFIG_IEEE80211AX
+ if ((sta->flags & WLAN_STA_HE) && sta->he_capab) {
+ res = os_snprintf(buf + len, buflen - len, "he_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->he_capab,
+ sta->he_capab_len);
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if ((sta->flags & WLAN_STA_EHT) && sta->eht_capab) {
+ res = os_snprintf(buf + len, buflen - len, "eht_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->eht_capab,
+ sta->eht_capab_len);
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AC
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
res = os_snprintf(buf + len, buflen - len,
@@ -372,6 +404,16 @@
vht_capabilities_info));
if (!os_snprintf_error(buflen - len, res))
len += res;
+
+ res = os_snprintf(buf + len, buflen - len, "vht_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->vht_capabilities,
+ sizeof(*sta->vht_capabilities));
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
#endif /* CONFIG_IEEE80211AC */
@@ -386,11 +428,15 @@
if (sta->ext_capability &&
buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
- len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+ res = os_snprintf(buf + len, buflen - len, "ext_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->ext_capability + 1,
sta->ext_capability[0]);
- len += os_snprintf(buf + len, buflen - len, "\n");
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
@@ -419,6 +465,21 @@
len += ret;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta) {
+ for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) {
+ if (!sta->mld_info.links[i].valid)
+ continue;
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "peer_addr[%d]=" MACSTR "\n",
+ i, MAC2STR(sta->mld_info.links[i].peer_addr));
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return len;
}
@@ -966,8 +1027,8 @@
"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, MAC2STR(bss->mld->mld_addr),
+ (int) i, hostapd_get_mld_id(bss),
(int) i, bss->mld_link_id);
if (os_snprintf_error(buflen - len, ret))
return len;
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 5e4c810..af9dc16 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -14,6 +14,7 @@
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
+#include "beacon.h"
#include "ap_drv_ops.h"
#include "drivers/driver.h"
#include "dfs.h"
@@ -972,6 +973,7 @@
struct csa_settings csa_settings;
u8 new_vht_oper_chwidth;
unsigned int i;
+ unsigned int num_err = 0;
wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
@@ -1009,7 +1011,8 @@
oper_centr_freq_seg1_idx,
cmode->vht_capab,
&cmode->he_capab[ieee80211_mode],
- &cmode->eht_capab[ieee80211_mode]);
+ &cmode->eht_capab[ieee80211_mode],
+ hostapd_get_punct_bitmap(iface->bss[0]));
if (err) {
wpa_printf(MSG_ERROR,
@@ -1021,10 +1024,10 @@
for (i = 0; i < iface->num_bss; i++) {
err = hostapd_switch_channel(iface->bss[i], &csa_settings);
if (err)
- break;
+ num_err++;
}
- if (err) {
+ if (num_err == iface->num_bss) {
wpa_printf(MSG_WARNING,
"DFS failed to schedule CSA (%d) - trying fallback",
err);
@@ -1141,14 +1144,23 @@
int cf1, int cf2)
{
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
- "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
- success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d",
+ success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
+ iface->radar_detected);
if (success) {
/* Complete iface/ap configuration */
if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
- /* Complete AP configuration for the first bring up. */
- if (iface->state != HAPD_IFACE_ENABLED)
+ /* Complete AP configuration for the first bring up. If
+ * a radar was detected in this channel, interface setup
+ * will be handled in
+ * 1. hostapd_event_ch_switch() if switching to a
+ * non-DFS channel
+ * 2. on next CAC complete event if switching to another
+ * DFS channel.
+ */
+ if (iface->state != HAPD_IFACE_ENABLED &&
+ !iface->radar_detected)
hostapd_setup_interface_complete(iface, 0);
else
iface->cac_started = 0;
@@ -1193,6 +1205,7 @@
hostapd_dfs_update_background_chain(iface);
}
+ iface->radar_detected = false;
return 0;
}
@@ -1436,6 +1449,8 @@
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ iface->radar_detected = true;
+
/* Proceed only if DFS is not offloaded to the driver */
if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
return 0;
@@ -1529,9 +1544,17 @@
iface->radar_background.cac_started = 1;
} else {
/* This is called when the driver indicates that an offloaded
- * DFS has started CAC. */
+ * DFS has started CAC. radar_detected might be set for previous
+ * DFS channel. Clear it for this new CAC process. */
hostapd_set_state(iface, HAPD_IFACE_DFS);
iface->cac_started = 1;
+
+ /* Clear radar_detected in case it is for the previous
+ * frequency. Also remove disabled link's information in RNR
+ * element from other links. */
+ iface->radar_detected = false;
+ if (iface->interfaces && iface->interfaces->count > 1)
+ ieee802_11_set_beacons(iface);
}
/* TODO: How to check CAC time for ETSI weather channels? */
iface->dfs_cac_ms = 60000;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 3f89bc2..d1bffa8 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -3941,6 +3941,7 @@
eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
hapd, NULL);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 533cc54..b0fcd1c 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -517,7 +517,7 @@
if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld->mld_addr,
sta->mld_assoc_link_id,
&sta->mld_info);
}
@@ -910,6 +910,54 @@
}
+static void hostapd_remove_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+ struct sta_info *sta,
+ const u8 *addr)
+{
+ unsigned int link_id, i;
+ struct hostapd_data *tmp_hapd;
+ struct hapd_interfaces *interfaces = assoc_hapd->iface->interfaces;
+
+ /* Remove STA entry in non-assoc links */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sta->mld_info.links[link_id].valid)
+ continue;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ assoc_hapd == tmp_hapd ||
+ assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ tmp_sta = ap_get_sta(tmp_hapd, addr);
+ if (tmp_sta)
+ ap_free_sta(tmp_hapd, tmp_sta);
+ }
+ }
+
+ /* Remove STA in assoc link */
+ hostapd_remove_sta(assoc_hapd, sta);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -931,6 +979,50 @@
HOSTAPD_LEVEL_INFO, "disassociated");
sta = ap_get_sta(hapd, addr);
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_is_mld_ap(hapd)) {
+ struct hostapd_data *assoc_hapd;
+ unsigned int i;
+
+ if (!sta) {
+ /* Find non-MLO cases from any of the affiliated AP
+ * links. */
+ 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;
+
+ sta = ap_get_sta(h_hapd, addr);
+ if (sta) {
+ if (!sta->mld_info.mld_sta) {
+ hapd = h_hapd;
+ goto legacy;
+ }
+ break;
+ }
+ }
+ } else if (!sta->mld_info.mld_sta) {
+ goto legacy;
+ }
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "Disassociation notification for unknown STA "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (sta)
+ hostapd_notif_disassoc_mld(assoc_hapd, sta, addr);
+ return;
+ }
+
+legacy:
+#endif /* CONFIG_IEEE80211BE */
if (sta == NULL) {
wpa_printf(MSG_DEBUG,
"Disassociation notification for unknown STA "
@@ -938,13 +1030,7 @@
return;
}
- ap_sta_set_authorized(hapd, sta, 0);
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- hostapd_set_sta_flags(hapd, sta);
- wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
- sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
- ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
- ap_free_sta(hapd, sta);
+ hostapd_remove_sta(hapd, sta);
}
@@ -1663,6 +1749,34 @@
}
+static struct hostapd_data *
+switch_link_scan(struct hostapd_data *hapd, u64 scan_cookie)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && scan_cookie != 0) {
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h;
+ struct hostapd_data *h_hapd;
+
+ h = hapd->iface->interfaces->iface[i];
+ h_hapd = h->bss[0];
+ if (!hostapd_is_ml_partner(hapd, h_hapd))
+ continue;
+
+ if (h_hapd->scan_cookie == scan_cookie) {
+ h_hapd->scan_cookie = 0;
+ return h_hapd;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd;
+}
+
+
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
@@ -1731,7 +1845,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld_addr, bssid))
+ ether_addr_equal(hapd->mld->mld_addr, bssid))
is_mld = true;
#endif /* CONFIG_IEEE80211BE */
@@ -1803,7 +1917,8 @@
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
} else if (hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld_addr, get_hdr_bssid(hdr, len))) {
+ ether_addr_equal(hapd->mld->mld_addr,
+ get_hdr_bssid(hdr, len))) {
/* AP MLD address match - use hapd pointer as-is */
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -1878,10 +1993,8 @@
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)
+ if (!hostapd_is_ml_partner(h_hapd, hapd))
continue;
h_hapd = hostapd_find_by_sta(h, src, false);
@@ -2292,8 +2405,29 @@
michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
break;
case EVENT_SCAN_RESULTS:
+#ifdef NEED_AP_MLME
+ if (data)
+ hapd = switch_link_scan(hapd,
+ data->scan_info.scan_cookie);
+#endif /* NEED_AP_MLME */
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->iface->scan_cb && hapd->conf->mld_ap) {
+ /* Other links may be waiting for HT scan result */
+ 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];
+
+ if (hostapd_is_ml_partner(hapd, h_hapd) &&
+ h_hapd->iface->scan_cb)
+ h_hapd->iface->scan_cb(h_hapd->iface);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
break;
case EVENT_WPS_BUTTON_PUSHED:
hostapd_wps_button_pushed(hapd, NULL);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index ddbcabc..56bac45 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -184,6 +184,8 @@
hostapd_set_generic_elem(hapd, (u8 *) "", 0);
}
+ hostapd_neighbor_sync_own_report(hapd);
+
ieee802_11_set_beacon(hapd);
hostapd_update_wps(hapd);
@@ -395,25 +397,6 @@
#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;
-}
-
-
#ifdef CONFIG_IEEE80211BE
#ifdef CONFIG_TESTING_OPTIONS
@@ -435,6 +418,7 @@
ieee802_11_set_beacon(hapd);
if (!hapd->eht_mld_link_removal_count) {
+ hostapd_free_link_stas(hapd);
hostapd_disable_iface(hapd->iface);
return;
}
@@ -496,7 +480,8 @@
vlan_deinit(hapd);
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
- if (!hapd->mld_first_bss) {
+ if (hostapd_mld_is_first_bss(hapd)) {
+#ifdef CONFIG_IEEE80211BE
struct hapd_interfaces *ifaces = hapd->iface->interfaces;
size_t i;
@@ -515,6 +500,7 @@
h->radius_das = NULL;
}
}
+#endif /* CONFIG_IEEE80211BE */
radius_client_deinit(hapd->radius);
radius_das_deinit(hapd->radius_das);
}
@@ -548,10 +534,20 @@
* driver wrapper may have removed its internal instance
* and hapd->drv_priv is not valid anymore.
*/
- hostapd_clear_drv_priv(hapd);
+ hapd->drv_priv = NULL;
}
}
+#ifdef CONFIG_IEEE80211BE
+ /* If the interface was not added as well as it is not the first BSS,
+ * at least the link should be removed here since deinit will take care
+ * of only the first BSS. */
+ if (hapd->conf->mld_ap && !hapd->interface_added &&
+ hapd->iface->bss[0] != hapd)
+ hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+ hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
wpabuf_free(hapd->time_adv);
hapd->time_adv = NULL;
@@ -614,6 +610,41 @@
}
+/* hostapd_bss_link_deinit - Per-BSS ML cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to unlink the BSS from the AP MLD.
+ * If the BSS being removed is the first link, the next link becomes the first
+ * link.
+ */
+static void hostapd_bss_link_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf || !hapd->conf->mld_ap)
+ return;
+
+ if (!hapd->mld->num_links)
+ return;
+
+ /* If not started, not yet linked to the MLD. However, the first
+ * BSS is always linked since it is linked during driver_init(), and
+ * hence, need to remove it from the AP MLD.
+ */
+ if (!hapd->started && hapd->iface->bss[0] != hapd)
+ return;
+
+ /* The first BSS can also be only linked when at least driver_init() is
+ * executed. But if previous interface fails, it is not, and hence,
+ * safe to skip.
+ */
+ if (hapd->iface->bss[0] == hapd && !hapd->drv_priv)
+ return;
+
+ hostapd_mld_remove_link(hapd);
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
/**
* hostapd_cleanup - Per-BSS cleanup (deinitialization)
* @hapd: Pointer to BSS data
@@ -654,6 +685,7 @@
void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
#ifdef NEED_AP_MLME
hostapd_stop_setup_timers(iface);
#endif /* NEED_AP_MLME */
@@ -683,7 +715,6 @@
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
NULL);
@@ -1303,7 +1334,7 @@
u8 if_addr[ETH_ALEN];
int flush_old_stations = 1;
- if (hapd->mld_first_bss)
+ if (!hostapd_mld_is_first_bss(hapd))
wpa_printf(MSG_DEBUG,
"MLD: %s: Setting non-first BSS", __func__);
@@ -1465,7 +1496,7 @@
}
#endif /* CONFIG_SQLITE */
- if (!hapd->mld_first_bss) {
+ if (hostapd_mld_is_first_bss(hapd)) {
hapd->radius = radius_client_init(hapd, conf->radius);
if (!hapd->radius) {
wpa_printf(MSG_ERROR,
@@ -1498,10 +1529,17 @@
}
}
} else {
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *f_bss;
+
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;
+ f_bss = hostapd_mld_get_first_bss(hapd);
+ if (!f_bss)
+ return -1;
+ hapd->radius = f_bss->radius;
+ hapd->radius_das = f_bss->radius_das;
+#endif /* CONFIG_IEEE80211BE */
}
#endif /* CONFIG_NO_RADIUS */
@@ -1546,6 +1584,7 @@
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
+#endif /* CONFIG_INTERWORKING */
if (conf->qos_map_set_len &&
hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
@@ -1553,7 +1592,6 @@
wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
return -1;
}
-#endif /* CONFIG_INTERWORKING */
if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
wpa_printf(MSG_ERROR, "BSS Load initialization failed");
@@ -1739,6 +1777,7 @@
static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
{
hostapd_bss_deinit_no_free(bss);
+ hostapd_bss_link_deinit(bss);
hostapd_free_hapd_data(bss);
hostapd_cleanup_iface_partial(bss->iface);
}
@@ -2035,10 +2074,11 @@
} else {
int ret;
- if (iface->conf->acs) {
+ if (iface->conf->acs && !iface->is_ch_switch_dfs) {
iface->freq = 0;
iface->conf->channel = 0;
}
+ iface->is_ch_switch_dfs = false;
ret = configured_fixed_chan_to_freq(iface);
if (ret < 0)
@@ -2797,6 +2837,8 @@
hapd->rad_attr_db = NULL;
}
#endif /* CONFIG_SQLITE */
+
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
}
@@ -2835,6 +2877,40 @@
}
+#ifdef CONFIG_IEEE80211BE
+
+static void hostapd_mld_ref_inc(struct hostapd_mld *mld)
+{
+ if (!mld)
+ return;
+
+ if (mld->refcount == HOSTAPD_MLD_MAX_REF_COUNT) {
+ wpa_printf(MSG_ERROR, "AP MLD %s: Ref count overflow",
+ mld->name);
+ return;
+ }
+
+ mld->refcount++;
+}
+
+
+static void hostapd_mld_ref_dec(struct hostapd_mld *mld)
+{
+ if (!mld)
+ return;
+
+ if (!mld->refcount) {
+ wpa_printf(MSG_ERROR, "AP MLD %s: Ref count underflow",
+ mld->name);
+ return;
+ }
+
+ mld->refcount--;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_interface_free(struct hostapd_iface *iface)
{
size_t j;
@@ -2842,6 +2918,10 @@
for (j = 0; j < iface->num_bss; j++) {
if (!iface->bss)
break;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[j])
+ hostapd_mld_ref_dec(iface->bss[j]->mld);
+#endif /* CONFIG_IEEE80211BE */
wpa_printf(MSG_DEBUG, "%s: free hapd %p",
__func__, iface->bss[j]);
os_free(iface->bss[j]);
@@ -2864,6 +2944,157 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_bss_alloc_link_id(struct hostapd_data *hapd)
+{
+ hapd->mld_link_id = hapd->mld->next_link_id++;
+ wpa_printf(MSG_DEBUG, "AP MLD: %s: Link ID %d assigned.",
+ hapd->mld->name, hapd->mld_link_id);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld *mld, **all_mld;
+ struct hostapd_bss_config *conf;
+ size_t i;
+
+ conf = hapd->conf;
+
+ if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
+ conf->disable_11be)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+
+ if (!mld || os_strcmp(conf->iface, mld->name) != 0)
+ continue;
+
+ hapd->mld = mld;
+ hostapd_mld_ref_inc(mld);
+ hostapd_bss_alloc_link_id(hapd);
+ break;
+ }
+
+ if (hapd->mld)
+ return;
+
+ mld = os_zalloc(sizeof(struct hostapd_mld));
+ if (!mld)
+ goto fail;
+
+ os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
+ dl_list_init(&mld->links);
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
+
+ hapd->mld = mld;
+ hostapd_mld_ref_inc(mld);
+ hostapd_bss_alloc_link_id(hapd);
+
+ all_mld = os_realloc_array(interfaces->mld, interfaces->mld_count + 1,
+ sizeof(struct hostapd_mld *));
+ if (!all_mld)
+ goto fail;
+
+ interfaces->mld = all_mld;
+ interfaces->mld[interfaces->mld_count] = mld;
+ interfaces->mld_count++;
+
+ return;
+fail:
+ if (!mld)
+ return;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: free mld %p", mld->name, mld);
+ os_free(mld);
+ hapd->mld = NULL;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld *mld, **all_mld;
+ size_t i, j, num_mlds;
+ bool forced_remove, remove;
+
+ if (!interfaces->mld)
+ return;
+
+ num_mlds = interfaces->mld_count;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+ if (!mld)
+ continue;
+
+ remove = false;
+ forced_remove = false;
+
+ if (!mld->refcount)
+ remove = true;
+
+ /* If MLD is still being referenced but the number of interfaces
+ * is zero, it is safe to force its deletion. Normally, this
+ * should not happen but even if it does, let us free the
+ * memory.
+ */
+ if (!remove && !interfaces->count)
+ forced_remove = true;
+
+ if (!remove && !forced_remove)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
+ forced_remove ? " (forced)" : "");
+ os_free(mld);
+ interfaces->mld[i] = NULL;
+ num_mlds--;
+ }
+
+ if (!num_mlds) {
+ interfaces->mld_count = 0;
+ os_free(interfaces->mld);
+ interfaces->mld = NULL;
+ return;
+ }
+
+ all_mld = os_zalloc(num_mlds * sizeof(struct hostapd_mld *));
+ if (!all_mld) {
+ wpa_printf(MSG_ERROR,
+ "AP MLD: Failed to re-allocate the MLDs. Expect issues");
+ return;
+ }
+
+ for (i = 0, j = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+ if (!mld)
+ continue;
+
+ all_mld[j++] = mld;
+ }
+
+ /* This should not happen */
+ if (j != num_mlds) {
+ wpa_printf(MSG_DEBUG,
+ "AP MLD: Some error occurred while reallocating MLDs. Expect issues.");
+ os_free(all_mld);
+ return;
+ }
+
+ os_free(interfaces->mld);
+ interfaces->mld = all_mld;
+ interfaces->mld_count = num_mlds;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
/**
* hostapd_init - Allocate and initialize per-interface data
* @config_file: Path to the configuration file
@@ -2907,8 +3138,10 @@
if (hapd == NULL)
goto fail;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
}
+ hapd_iface->is_ch_switch_dfs = false;
return hapd_iface;
fail:
@@ -3028,6 +3261,8 @@
iface->conf->last_bss = bss;
iface->bss[iface->num_bss] = hapd;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
+
bss_idx = iface->num_bss++;
conf->num_bss--;
@@ -3061,6 +3296,37 @@
}
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv, struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!driver || !driver->hapd_deinit || !drv_priv)
+ return;
+
+ /* In case of non-ML operation, de-init. But if ML operation exist,
+ * even if that's the last BSS in the interface, the driver (drv) could
+ * be in use for a different AP MLD. Hence, need to check if drv is
+ * still being used by some other BSS before de-initiallizing. */
+ if (!iface->bss[0]->conf->mld_ap) {
+ driver->hapd_deinit(drv_priv);
+ } else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
+ driver->is_drv_shared &&
+ !driver->is_drv_shared(drv_priv, iface->bss[0])) {
+ driver->hapd_deinit(drv_priv);
+ } else if (hostapd_if_link_remove(iface->bss[0],
+ WPA_IF_AP_BSS,
+ iface->bss[0]->conf->iface,
+ iface->bss[0]->mld_link_id)) {
+ wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+ iface->bss[0]->conf->iface);
+ }
+#else /* CONFIG_IEEE80211BE */
+ driver->hapd_deinit(drv_priv);
+#endif /* CONFIG_IEEE80211BE */
+ iface->bss[0]->drv_priv = NULL;
+}
+
+
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
const struct wpa_driver_ops *driver;
@@ -3077,11 +3343,7 @@
hostapd_interface_deinit(iface);
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
- if (driver && driver->hapd_deinit && drv_priv) {
- if (!iface->bss[0]->mld_first_bss)
- driver->hapd_deinit(drv_priv);
- hostapd_clear_drv_priv(iface->bss[0]);
- }
+ hostapd_cleanup_driver(driver, drv_priv, iface);
hostapd_interface_free(iface);
}
@@ -3094,15 +3356,16 @@
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
+
+ hostapd_cleanup_driver(driver, drv_priv, hapd_iface);
+
if (driver && 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) {
- hostapd_clear_drv_priv(hapd_iface->bss[j]);
+ hapd_iface->bss[j]->drv_priv = NULL;
hapd_iface->extended_capa = NULL;
hapd_iface->extended_capa_mask = NULL;
hapd_iface->extended_capa_len = 0;
@@ -3112,6 +3375,22 @@
}
+static void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ if (!hapd_iface->interfaces || hapd_iface->interfaces->count <= 1)
+ return;
+
+ for (j = 0; j < hapd_iface->interfaces->count; j++) {
+ if (hapd_iface->interfaces->iface[j] == hapd_iface)
+ continue;
+
+ ieee802_11_update_beacons(hapd_iface->interfaces->iface[j]);
+ }
+}
+
+
int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
@@ -3150,6 +3429,8 @@
return -1;
}
+ hostapd_refresh_all_iface_beacons(hapd_iface);
+
return 0;
}
@@ -3207,31 +3488,6 @@
return -1;
}
-#ifdef CONFIG_IEEE80211BE
- if (hapd_iface->bss[0]->conf->mld_ap &&
- !hapd_iface->bss[0]->mld_first_bss) {
- /* Do not allow mld_first_bss disabling before other BSSs */
- for (j = 0; j < hapd_iface->interfaces->count; ++j) {
- struct hostapd_iface *h_iface =
- hapd_iface->interfaces->iface[j];
- struct hostapd_data *h_hapd = h_iface->bss[0];
- struct hostapd_bss_config *h_conf = h_hapd->conf;
-
- if (!h_conf->mld_ap ||
- h_conf->mld_id !=
- hapd_iface->bss[0]->conf->mld_id ||
- h_iface == hapd_iface)
- continue;
-
- if (h_iface->state != HAPD_IFACE_DISABLED) {
- wpa_printf(MSG_INFO,
- "Do not allow disable mld_first_bss first");
- return -1;
- }
- }
- }
-#endif /* CONFIG_IEEE80211BE */
-
wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
driver = hapd_iface->bss[0]->driver;
drv_priv = hapd_iface->bss[0]->drv_priv;
@@ -3249,6 +3505,7 @@
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
+ hostapd_bss_link_deinit(hapd);
hostapd_free_hapd_data(hapd);
}
@@ -3262,6 +3519,7 @@
wpa_printf(MSG_DEBUG, "Interface %s disabled",
hapd_iface->bss[0]->conf->iface);
hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
+ hostapd_refresh_all_iface_beacons(hapd_iface);
return 0;
}
@@ -3370,6 +3628,7 @@
return -1;
}
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, hapd_iface->interfaces);
}
hapd_iface->conf = conf;
@@ -3443,6 +3702,7 @@
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
hostapd_setup_bss(hapd, -1, true))) {
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
hapd_iface->conf->num_bss--;
@@ -3451,6 +3711,9 @@
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
return -1;
}
@@ -3540,7 +3803,11 @@
wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
__func__, hapd_iface->bss[i],
hapd->conf->iface);
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
hapd_iface->bss[i] = NULL;
}
@@ -3550,6 +3817,7 @@
if (new_iface) {
interfaces->count--;
interfaces->iface[interfaces->count] = NULL;
+ hostapd_cleanup_unused_mlds(interfaces);
}
hostapd_cleanup_iface(hapd_iface);
}
@@ -3572,6 +3840,9 @@
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
iface->num_bss--;
@@ -3614,6 +3885,8 @@
k++;
}
interfaces->count--;
+ hostapd_cleanup_unused_mlds(interfaces);
+
return 0;
}
@@ -3913,7 +4186,8 @@
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL,
mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
- NULL))
+ NULL,
+ hostapd_get_punct_bitmap(hapd)))
return -1;
switch (params->bandwidth) {
@@ -4403,6 +4677,7 @@
#ifdef CONFIG_IEEE80211BE
+
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id)
{
@@ -4411,9 +4686,8 @@
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)
+ if (!hostapd_is_ml_partner(hapd, h_hapd))
continue;
if (h_hapd->mld_link_id == link_id)
@@ -4422,4 +4696,141 @@
return NULL;
}
+
+
+bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ struct hostapd_data *hapd2)
+{
+ if (!hapd1->conf->mld_ap || !hapd2->conf->mld_ap)
+ return false;
+
+ return !os_strcmp(hapd1->conf->iface, hapd2->conf->iface);
+}
+
+
+u8 hostapd_get_mld_id(struct hostapd_data *hapd)
+{
+ if (!hapd->conf->mld_ap)
+ return 255;
+
+ /* MLD ID 0 represents self */
+ return 0;
+
+ /* TODO: MLD ID for Multiple BSS cases */
+}
+
+
+int hostapd_mld_add_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_add_tail(&mld->links, &hapd->link);
+ mld->num_links++;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d added. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss)
+ return 0;
+
+ mld->fbss = hapd;
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+int hostapd_mld_remove_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+ struct hostapd_data *next_fbss;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_del(&hapd->link);
+ mld->num_links--;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d removed. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss != hapd)
+ return 0;
+
+ /* If the list is empty, all links are removed */
+ if (dl_list_empty(&mld->links)) {
+ mld->fbss = NULL;
+ } else {
+ next_fbss = dl_list_entry(mld->links.next, struct hostapd_data,
+ link);
+ mld->fbss = next_fbss;
+ }
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return true;
+
+ /* Should not happen */
+ if (!mld)
+ return false;
+
+ /* If fbss is not set, it is safe to assume the caller is the first BSS.
+ */
+ if (!mld->fbss)
+ return true;
+
+ return hapd == mld->fbss;
+}
+
+
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ /* Should not happen */
+ if (!mld)
+ return NULL;
+
+ return mld->fbss;
+}
+
#endif /* CONFIG_IEEE80211BE */
+
+
+u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+{
+ u16 punct_bitmap = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ punct_bitmap = hapd->iconf->punct_bitmap;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!punct_bitmap)
+ punct_bitmap = hapd->conf->eht_oper_puncturing_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+ return punct_bitmap;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index bcf980f..ff29726 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -44,6 +44,7 @@
#endif /* CONFIG_CTRL_IFACE_UDP */
struct hostapd_iface;
+struct hostapd_mld;
struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface);
@@ -93,6 +94,10 @@
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld **mld;
+ size_t mld_count;
+#endif /* CONFIG_IEEE80211BE */
};
enum hostapd_chan_status {
@@ -175,12 +180,6 @@
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 */
@@ -406,8 +405,10 @@
u8 beacon_req_token;
u8 lci_req_token;
u8 range_req_token;
+ u8 link_measurement_req_token;
unsigned int lci_req_active:1;
unsigned int range_req_active:1;
+ unsigned int link_mesr_req_active:1;
int dhcp_sock; /* UDP socket used with the DHCP server */
@@ -472,6 +473,9 @@
#ifdef CONFIG_IEEE80211BE
u8 eht_mld_bss_param_change;
+ struct hostapd_mld *mld;
+ struct dl_list link;
+ u8 mld_link_id;
#ifdef CONFIG_TESTING_OPTIONS
u8 eht_mld_link_removal_count;
#endif /* CONFIG_TESTING_OPTIONS */
@@ -480,6 +484,9 @@
#ifdef CONFIG_NAN_USD
struct nan_de *nan_de;
#endif /* CONFIG_NAN_USD */
+
+ u64 scan_cookie; /* Scan instance identifier for the ongoing HT40 scan
+ */
};
@@ -504,6 +511,29 @@
HAPD_IFACE_ENABLED
};
+#ifdef CONFIG_IEEE80211BE
+/**
+ * struct hostapd_mld - hostapd per-mld data structure
+ */
+struct hostapd_mld {
+ char name[IFNAMSIZ + 1];
+ u8 mld_addr[ETH_ALEN];
+ u8 next_link_id;
+ u8 num_links;
+ /* Number of hostapd_data (hapd) referencing this. num_links cannot be
+ * used since num_links can go to 0 even when a BSS is disabled and
+ * when it is re-enabled, the MLD should exist and hence it cannot be
+ * freed when num_links is 0.
+ */
+ u8 refcount;
+
+ struct hostapd_data *fbss;
+ struct dl_list links; /* List head of all affiliated links */
+};
+
+#define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
+#endif /* CONFIG_IEEE80211BE */
+
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
@@ -551,6 +581,7 @@
u64 drv_flags;
u64 drv_flags2;
+ unsigned int drv_rrm_flags;
/*
* A bitmap of supported protocols for probe response offload. See
@@ -576,6 +607,8 @@
int *basic_rates;
int freq;
+ bool radar_detected;
+
/* Background radar configuration */
struct {
int channel;
@@ -677,6 +710,8 @@
/* Configured freq of interface is NO_IR */
bool is_no_ir;
+
+ bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
};
/* hostapd.c */
@@ -783,8 +818,17 @@
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id);
int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ struct hostapd_data *hapd2);
+u8 hostapd_get_mld_id(struct hostapd_data *hapd);
+int hostapd_mld_add_link(struct hostapd_data *hapd);
+int hostapd_mld_remove_link(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
#ifdef CONFIG_IEEE80211BE
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+
#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
for (_iface_idx = 0; \
_iface_idx < (_ifaces)->count; \
@@ -796,11 +840,21 @@
for (_link = \
(_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
_link && _link->conf->mld_ap && \
- _link->conf->mld_id == _mld_id; \
+ hostapd_get_mld_id(_link) == _mld_id; \
_link = NULL)
+
#else /* CONFIG_IEEE80211BE */
+
+static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ return true;
+}
+
#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
if (false)
+
#endif /* CONFIG_IEEE80211BE */
+u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 596f2f0..c455660 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -107,9 +107,7 @@
*/
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);
+ is_6ghz = iface->current_mode->is_6ghz;
iface->current_mode = NULL;
}
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
@@ -508,6 +506,12 @@
else
ieee80211n_scan_channels_5g(iface, ¶ms);
+ params.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+ params.link_id = iface->bss[0]->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
iface->num_ht40_scan_tries++;
os_free(params.freqs);
@@ -523,6 +527,7 @@
if (ret == 0) {
iface->scan_cb = ieee80211n_check_scan;
+ iface->bss[0]->scan_cookie = params.scan_cookie;
return;
}
@@ -558,6 +563,11 @@
else
ieee80211n_scan_channels_5g(iface, ¶ms);
+ params.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+ params.link_id = iface->bss[0]->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
ret = hostapd_driver_scan(iface->bss[0], ¶ms);
os_free(params.freqs);
@@ -579,6 +589,7 @@
}
iface->scan_cb = ieee80211n_check_scan;
+ iface->bss[0]->scan_cookie = params.scan_cookie;
return 1;
}
@@ -1070,9 +1081,7 @@
return true;
if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 &&
- (mode->mode != HOSTAPD_MODE_IEEE80211A ||
- mode->num_channels == 0 ||
- !is_6ghz_freq(mode->channels[0].freq)))
+ !mode->is_6ghz)
return true;
return false;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 8b8c1f0..85a39d5 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -88,18 +88,31 @@
struct sta_info *sta, int reassoc);
-u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len)
{
- u8 multi_ap_val = 0;
+ struct multi_ap_params multi_ap = { 0 };
if (!hapd->conf->multi_ap)
return eid;
- if (hapd->conf->multi_ap & BACKHAUL_BSS)
- multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
- if (hapd->conf->multi_ap & FRONTHAUL_BSS)
- multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
- return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap.capability |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap.capability |= MULTI_AP_FRONTHAUL_BSS;
+
+ if (hapd->conf->multi_ap_client_disallow &
+ PROFILE1_CLIENT_ASSOC_DISALLOW)
+ multi_ap.capability |=
+ MULTI_AP_PROFILE1_BACKHAUL_STA_DISALLOWED;
+ if (hapd->conf->multi_ap_client_disallow &
+ PROFILE2_CLIENT_ASSOC_DISALLOW)
+ multi_ap.capability |=
+ MULTI_AP_PROFILE2_BACKHAUL_STA_DISALLOWED;
+
+ multi_ap.profile = hapd->conf->multi_ap_profile;
+ multi_ap.vlanid = hapd->conf->multi_ap_vlanid;
+
+ return eid + add_multi_ap_ie(eid, len, &multi_ap);
}
@@ -409,7 +422,7 @@
* the addresses.
*/
if (ap_sta_is_mld(hapd, sta)) {
- sa = hapd->mld_addr;
+ sa = hapd->mld->mld_addr;
ml_resp = hostapd_ml_auth_resp(hapd);
if (!ml_resp)
@@ -610,7 +623,7 @@
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
if (sta->sae->tmp) {
@@ -2390,7 +2403,7 @@
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
fils->anonce, FILS_NONCE_LEN);
- ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce,
+ ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce,
fils->anonce, NULL, 0, pmk, &pmk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
@@ -2400,15 +2413,16 @@
ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
wpabuf_head(pasn->secret),
wpabuf_len(pasn->secret),
- &sta->pasn->ptk, sta->pasn->akmp,
- sta->pasn->cipher, sta->pasn->kdk_len);
+ pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
+ pasn_get_cipher(sta->pasn), sta->pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
goto fail;
}
if (pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ ret = wpa_ltf_keyseed(pasn_get_ptk(pasn), pasn_get_akmp(pasn),
+ pasn_get_cipher(pasn));
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Failed to derive LTF keyseed");
@@ -2554,7 +2568,8 @@
* Calculate pending PMKID here so that we do not need to maintain a
* copy of the EAP-Initiate/Reautt message.
*/
- fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd),
+ fils_pmkid_erp(pasn_get_akmp(pasn),
+ wpabuf_head(fils_wd), wpabuf_len(fils_wd),
fils->erp_pmkid);
wpabuf_free(fils_wd);
@@ -2579,32 +2594,35 @@
{
struct pasn_data *pasn = sta->pasn;
- pasn->cb_ctx = hapd;
- pasn->send_mgmt = hapd_pasn_send_mlme;
+ pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL);
+ pasn_set_bssid(pasn, hapd->own_addr);
+ pasn_set_own_addr(pasn, hapd->own_addr);
+ pasn_set_peer_addr(pasn, sta->addr);
+ pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt);
+ pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise);
pasn->pasn_groups = hapd->conf->pasn_groups;
pasn->noauth = hapd->conf->pasn_noauth;
- pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
- pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
- pasn->derive_kdk = hapd->iface->drv_flags2 &
- WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
+ pasn_enable_kdk_derivation(pasn);
+
#ifdef CONFIG_TESTING_OPTIONS
pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
if (hapd->conf->force_kdk_derivation)
- pasn->derive_kdk = true;
+ pasn_enable_kdk_derivation(pasn);
#endif /* CONFIG_TESTING_OPTIONS */
pasn->use_anti_clogging = use_anti_clogging(hapd);
- pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
- NULL);
+ pasn_set_password(pasn, sae_get_password(hapd, sta, NULL, NULL,
+ &pasn->pt, NULL));
pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
- pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX));
pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
- pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+ pasn_set_responder_pmksa(pasn,
+ wpa_auth_get_pmksa_cache(hapd->wpa_auth));
pasn->comeback_after = hapd->conf->pasn_comeback_after;
pasn->comeback_idx = hapd->comeback_idx;
pasn->comeback_key = hapd->comeback_key;
pasn->comeback_pending_idx = hapd->comeback_pending_idx;
- os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
}
@@ -2652,6 +2670,7 @@
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
#endif /* CONFIG_FILS */
+ int akmp;
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
@@ -2675,10 +2694,12 @@
return;
}
- pasn->akmp = rsn_data.key_mgmt;
- pasn->cipher = rsn_data.pairwise_cipher;
+ pasn_set_akmp(pasn, rsn_data.key_mgmt);
+ pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
- if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+ akmp = pasn_get_akmp(pasn);
+
+ if (wpa_key_mgmt_ft(akmp) && rsn_data.num_pmkid) {
#ifdef CONFIG_IEEE80211R_AP
pasn->pmk_r1_len = 0;
wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
@@ -2689,8 +2710,8 @@
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_FILS
- if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
- pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+ if (akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+ akmp != WPA_KEY_MGMT_FILS_SHA384)
return;
if (!elems.pasn_params ||
wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
@@ -2743,7 +2764,7 @@
return;
}
- sta->pasn = os_zalloc(sizeof(*sta->pasn));
+ sta->pasn = pasn_data_init();
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to allocate PASN context");
@@ -2773,13 +2794,14 @@
if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
sta->addr, mgmt, len) == 0) {
ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
- sta->pasn->cipher, 43200,
- &sta->pasn->ptk, NULL, NULL,
- sta->pasn->akmp);
+ pasn_get_cipher(sta->pasn), 43200,
+ pasn_get_ptk(sta->pasn), NULL, NULL,
+ pasn_get_akmp(sta->pasn));
pasn_set_keys_from_cache(hapd, hapd->own_addr,
- sta->addr, sta->pasn->cipher,
- sta->pasn->akmp);
+ sta->addr,
+ pasn_get_cipher(sta->pasn),
+ pasn_get_akmp(sta->pasn));
}
ap_free_sta(hapd, sta);
} else {
@@ -2806,7 +2828,9 @@
u16 seq_ctrl;
struct radius_sta rad_info;
const u8 *dst, *sa, *bssid;
+#ifdef CONFIG_IEEE80211BE
bool mld_sta = false;
+#endif /* CONFIG_IEEE80211BE */
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -2924,15 +2948,17 @@
goto fail;
}
+#ifdef CONFIG_IEEE80211BE
if (mld_sta &&
(ether_addr_equal(sa, hapd->own_addr) ||
- ether_addr_equal(sa, hapd->mld_addr))) {
+ ether_addr_equal(sa, hapd->mld->mld_addr))) {
wpa_printf(MSG_INFO,
"Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
+#endif /* CONFIG_IEEE80211BE */
if (hapd->conf->no_auth_if_seen_on) {
struct hostapd_data *other;
@@ -3032,15 +3058,6 @@
seq_ctrl);
return;
}
-#ifdef CONFIG_MESH
- if ((hapd->conf->mesh & MESH_ENABLED) &&
- sta->plink_state == PLINK_BLOCKED) {
- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
- " is blocked - drop Authentication frame",
- MAC2STR(sa));
- return;
- }
-#endif /* CONFIG_MESH */
#ifdef CONFIG_PASN
if (auth_alg == WLAN_AUTH_PASN &&
(sta->flags & WLAN_STA_ASSOC)) {
@@ -3078,7 +3095,12 @@
}
#ifdef CONFIG_IEEE80211BE
- if (auth_transaction == 1) {
+ /* Set the non-AP MLD information based on the initial Authentication
+ * frame. Once the STA entry has been added to the driver, the driver
+ * will translate addresses in the frame and we need to avoid overriding
+ * peer_addr based on mgmt->sa which would have been translated to the
+ * MLD MAC address. */
+ if (!sta->added_unassoc && auth_transaction == 1) {
ap_sta_free_sta_profile(&sta->mld_info);
os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
@@ -3250,7 +3272,7 @@
*/
if (ap_sta_is_mld(hapd, sta)) {
dst = sta->addr;
- bssid = hapd->mld_addr;
+ bssid = hapd->mld->mld_addr;
}
#endif /* CONFIG_IEEE80211BE */
@@ -3410,37 +3432,58 @@
static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *multi_ap_ie, size_t multi_ap_len)
{
- u8 multi_ap_value = 0;
+ struct multi_ap_params multi_ap;
+ u16 status;
sta->flags &= ~WLAN_STA_MULTI_AP;
if (!hapd->conf->multi_ap)
return WLAN_STATUS_SUCCESS;
- if (multi_ap_ie) {
- const u8 *multi_ap_subelem;
-
- multi_ap_subelem = get_ie(multi_ap_ie + 4,
- multi_ap_len - 4,
- MULTI_AP_SUB_ELEM_TYPE);
- if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
- multi_ap_value = multi_ap_subelem[2];
- } else {
+ if (!multi_ap_ie) {
+ if (!(hapd->conf->multi_ap & FRONTHAUL_BSS)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "Multi-AP IE has missing or invalid Multi-AP subelement");
- return WLAN_STATUS_INVALID_IE;
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
+
+ return WLAN_STATUS_SUCCESS;
}
- if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+ status = check_multi_ap_ie(multi_ap_ie + 4, multi_ap_len - 4,
+ &multi_ap);
+ if (status != WLAN_STATUS_SUCCESS)
+ return status;
+
+ if (multi_ap.capability && multi_ap.capability != MULTI_AP_BACKHAUL_STA)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Multi-AP IE with unexpected value 0x%02x",
- multi_ap_value);
+ multi_ap.capability);
- if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+ if (multi_ap.profile == MULTI_AP_PROFILE_1 &&
+ (hapd->conf->multi_ap_client_disallow &
+ PROFILE1_CLIENT_ASSOC_DISALLOW)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP Profile-1 clients not allowed");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (multi_ap.profile >= MULTI_AP_PROFILE_2 &&
+ (hapd->conf->multi_ap_client_disallow &
+ PROFILE2_CLIENT_ASSOC_DISALLOW)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP Profile-2 clients not allowed");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (!(multi_ap.capability & MULTI_AP_BACKHAUL_STA)) {
if (hapd->conf->multi_ap & FRONTHAUL_BSS)
return WLAN_STATUS_SUCCESS;
@@ -3740,7 +3783,7 @@
}
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta))
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld->mld_addr,
sta->mld_assoc_link_id, &sta->mld_info);
#endif /* CONFIG_IEEE80211BE */
rsn_ie -= 2;
@@ -4025,7 +4068,7 @@
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
wpa_auth_set_ml_info(sta->wpa_sm,
- hapd->mld_addr,
+ hapd->mld->mld_addr,
sta->mld_assoc_link_id,
info);
}
@@ -4559,8 +4602,7 @@
if (hapd->iface == iface)
continue;
- if (iface->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
+ if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
i == iface->bss[0]->mld_link_id)
break;
}
@@ -4787,7 +4829,7 @@
* MLD MAC address.
*/
if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
- sa = hapd->mld_addr;
+ sa = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
os_memcpy(reply->da, addr, ETH_ALEN);
@@ -4991,7 +5033,7 @@
#endif /* CONFIG_WPS */
if (sta && (sta->flags & WLAN_STA_MULTI_AP))
- p = hostapd_eid_multi_ap(hapd, p);
+ p = hostapd_eid_multi_ap(hapd, p, buf + buflen - p);
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
@@ -5775,8 +5817,7 @@
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)
+ if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
continue;
for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
@@ -6206,7 +6247,7 @@
#endif /* CONFIG_MESH */
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
+ ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
!ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
@@ -6229,7 +6270,7 @@
stype != WLAN_FC_STYPE_ACTION) &&
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
+ ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_NAN_USD
!ether_addr_equal(mgmt->da, nan_network_id) &&
@@ -6443,8 +6484,7 @@
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)
+ if (!hostapd_is_ml_partner(tmp_hapd, hapd))
continue;
for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
@@ -7407,12 +7447,12 @@
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)
+ if (hostapd_is_ml_partner(hapd, iface->bss[0]))
ap_mld = true;
#endif /* CONFIG_IEEE80211BE */
if (iface == hapd->iface ||
+ iface->state != HAPD_IFACE_ENABLED ||
!(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
continue;
@@ -7580,11 +7620,10 @@
#ifdef CONFIG_IEEE80211BE
u8 param_ch = hapd->eht_mld_bss_param_change;
- if (reporting_hapd->conf->mld_ap &&
- bss->conf->mld_id == reporting_hapd->conf->mld_id)
+ if (hostapd_is_ml_partner(bss, reporting_hapd))
*eid++ = 0;
else
- *eid++ = hapd->conf->mld_id;
+ *eid++ = hostapd_get_mld_id(hapd);
*eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
*eid = (param_ch >> 4) & 0xF;
@@ -7682,12 +7721,12 @@
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)
+ if (hostapd_is_ml_partner(hapd, iface->bss[0]))
ap_mld = true;
#endif /* CONFIG_IEEE80211BE */
if (iface == hapd->iface ||
+ iface->state != HAPD_IFACE_ENABLED ||
!(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
continue;
@@ -7754,6 +7793,27 @@
}
+static size_t hostapd_mbssid_ext_capa(struct hostapd_data *bss,
+ struct hostapd_data *tx_bss, u8 *buf)
+{
+ u8 ext_capa_tx[20], *ext_capa_tx_end, ext_capa[20], *ext_capa_end;
+ size_t ext_capa_len, ext_capa_tx_len;
+
+ ext_capa_tx_end = hostapd_eid_ext_capab(tx_bss, ext_capa_tx,
+ true);
+ ext_capa_tx_len = ext_capa_tx_end - ext_capa_tx;
+ ext_capa_end = hostapd_eid_ext_capab(bss, ext_capa, true);
+ ext_capa_len = ext_capa_end - ext_capa;
+ if (ext_capa_tx_len != ext_capa_len ||
+ os_memcmp(ext_capa_tx, ext_capa, ext_capa_len) != 0) {
+ os_memcpy(buf, ext_capa, ext_capa_len);
+ return ext_capa_len;
+ }
+
+ return 0;
+}
+
+
static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
u32 frame_type, size_t *bss_index,
const u8 *known_bss,
@@ -7761,6 +7821,7 @@
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
size_t len, i;
+ u8 ext_capa[20];
/* Element ID: 1 octet
* Length: 1 octet
@@ -7806,6 +7867,10 @@
if (rsnx)
nontx_profile_len += 2 + rsnx[1];
}
+
+ nontx_profile_len += hostapd_mbssid_ext_capa(bss, tx_bss,
+ ext_capa);
+
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
ie_count++;
if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
@@ -7955,6 +8020,9 @@
eid += 2 + rsnx[1];
}
}
+
+ eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
+
/* List of Element ID values in increasing order */
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
non_inherit_ie[ie_count++] = WLAN_EID_RSN;
@@ -8062,73 +8130,4 @@
return eid;
}
-
-static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
-{
- u8 first_chan = *seg0 - 6, sec_chan;
-
- switch (bitmap) {
- case 0x6:
- *seg0 = 0;
- return;
- case 0x8:
- case 0x4:
- case 0x2:
- case 0x1:
- case 0xC:
- case 0x3:
- if (pri_chan < *seg0)
- *seg0 -= 4;
- else
- *seg0 += 4;
- break;
- }
-
- if (pri_chan < *seg0)
- sec_chan = pri_chan + 4;
- else
- sec_chan = pri_chan - 4;
-
- if (bitmap & BIT((sec_chan - first_chan) / 4))
- *seg0 = 0;
-}
-
-
-static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
- enum oper_chan_width *width, u8 *seg0)
-{
- if (pri < *seg0) {
- *seg0 -= 8;
- if (bitmap & 0x0F) {
- *width = 0;
- punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
- }
- } else {
- *seg0 += 8;
- if (bitmap & 0xF0) {
- *width = 0;
- punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
- seg0);
- }
- }
-}
-
-
-void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
- u8 *seg0, u8 *seg1)
-{
- if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
- *width = CONF_OPER_CHWIDTH_USE_HT;
- punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
- }
-
- if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
- *width = CONF_OPER_CHWIDTH_80MHZ;
- *seg1 = 0;
- punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
- }
-
- /* TODO: 320 MHz */
-}
-
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 5fd380a..a35486d 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -248,8 +248,6 @@
u8 **elem_offset,
const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
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,
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index 840fc20..638546e 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -206,19 +206,11 @@
enum oper_chan_width chwidth;
size_t elen = 1 + 4;
bool eht_oper_info_present;
- u16 punct_bitmap = conf->punct_bitmap;
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
if (!hapd->iface->current_mode)
return eid;
-#ifdef CONFIG_TESTING_OPTIONS
- if (!punct_bitmap && hapd->conf->eht_oper_puncturing_override) {
- wpa_printf(MSG_DEBUG, "EHT: Puncturing mask override=0x%x",
- hapd->conf->eht_oper_puncturing_override);
- punct_bitmap = hapd->conf->eht_oper_puncturing_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
if (is_6ghz_op_class(conf->op_class))
chwidth = op_class_to_ch_width(conf->op_class);
else
@@ -458,6 +450,8 @@
size_t len, slice_len;
u8 link_id;
u8 common_info_len;
+ u16 mld_cap;
+ u8 max_simul_links, active_links;
/*
* As the Multi-Link element can exceed the size of 255 bytes need to
@@ -495,7 +489,7 @@
wpabuf_put_u8(buf, common_info_len);
/* Own MLD MAC Address */
- wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+ wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
/* Own Link ID */
wpabuf_put_u8(buf, hapd->mld_link_id);
@@ -507,14 +501,31 @@
hapd->iface->mld_eml_capa);
wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
+ mld_cap = hapd->iface->mld_mld_capa;
+ max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+ active_links = hapd->mld->num_links - 1;
+
+ if (active_links > max_simul_links) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Error in max simultaneous links, advertised: 0x%x current: 0x%x",
+ max_simul_links, active_links);
+ active_links = max_simul_links;
+ }
+
+ mld_cap &= ~EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+ mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+
+ /* TODO: Advertise T2LM based on driver support as well */
+ mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
+
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);
+ mld_cap);
+ wpabuf_put_le16(buf, mld_cap);
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);
+ hostapd_get_mld_id(hapd));
+ wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
}
if (!mld_info)
@@ -578,7 +589,8 @@
wpabuf_put_le64(buf, 0);
/* DTIM Info */
- wpabuf_put_le16(buf, link_bss->conf->dtim_period);
+ wpabuf_put_u8(buf, 0); /* DTIM Count */
+ wpabuf_put_u8(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
@@ -812,7 +824,7 @@
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);
+ wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
return buf;
}
@@ -1047,8 +1059,7 @@
if (hapd == other_hapd)
continue;
- if (other_hapd->conf->mld_ap &&
- other_hapd->conf->mld_id == hapd->conf->mld_id &&
+ if (hostapd_is_ml_partner(hapd, other_hapd) &&
link_id == other_hapd->mld_link_id)
break;
}
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 4b693a7..a2deda6 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
@@ -224,10 +225,11 @@
u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
u8 control;
-
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->punct_bitmap) {
- punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+
+ if (punct_bitmap) {
+ punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 0c38483..85790c7 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -121,7 +121,7 @@
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -219,7 +219,7 @@
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -1138,13 +1138,11 @@
u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len)
{
-#ifdef CONFIG_INTERWORKING
/* check for QoS Map support */
if (ext_capab_ie_len >= 5) {
if (ext_capab_ie[4] & 0x01)
sta->qos_map_enabled = 1;
}
-#endif /* CONFIG_INTERWORKING */
if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index db615a3..4dc325c 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
@@ -79,6 +80,9 @@
hostapd_get_oper_chwidth(hapd->iconf);
u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
+#ifdef CONFIG_IEEE80211BE
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+#endif /* CONFIG_IEEE80211BE */
if (is_6ghz_op_class(hapd->iconf->op_class))
return eid;
@@ -90,8 +94,8 @@
os_memset(oper, 0, sizeof(*oper));
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->punct_bitmap) {
- punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ if (punct_bitmap) {
+ punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f13c60a..8e98b65 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -172,8 +172,7 @@
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)
+ if (!hostapd_is_ml_partner(hapd, tmp_hapd))
continue;
for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
@@ -2540,13 +2539,20 @@
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
wpa_printf(MSG_DEBUG,
"MLD: Using IEEE 802.1X state machine of the first BSS");
- hapd->eapol_auth = hapd->mld_first_bss->eapol_auth;
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
+ hapd->eapol_auth = first->eapol_auth;
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
dl_list_init(&hapd->erp_keys);
@@ -2632,13 +2638,15 @@
void ieee802_1x_deinit(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
wpa_printf(MSG_DEBUG,
"MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
hapd->eapol_auth = NULL;
return;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_WEP
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 788c12f..bc1eb62 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -61,6 +61,7 @@
dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
list) {
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+ dl_list_del(&ip6addr->list);
os_free(ip6addr);
}
}
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 2a25ae2..f7a7d83 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -99,7 +99,10 @@
nr->civic = NULL;
os_memset(nr->bssid, 0, sizeof(nr->bssid));
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+ os_memset(&nr->lci_date, 0, sizeof(nr->lci_date));
nr->stationary = 0;
+ nr->short_ssid = 0;
+ nr->bss_parameters = 0;
}
@@ -165,6 +168,14 @@
}
+static void hostapd_neighbor_free(struct hostapd_neighbor_entry *nr)
+{
+ hostapd_neighbor_clear_entry(nr);
+ dl_list_del(&nr->list);
+ os_free(nr);
+}
+
+
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid)
{
@@ -174,9 +185,7 @@
if (!nr)
return -1;
- hostapd_neighbor_clear_entry(nr);
- dl_list_del(&nr->list);
- os_free(nr);
+ hostapd_neighbor_free(nr);
return 0;
}
@@ -188,9 +197,7 @@
dl_list_for_each_safe(nr, prev, &hapd->nr_db,
struct hostapd_neighbor_entry, list) {
- hostapd_neighbor_clear_entry(nr);
- dl_list_del(&nr->list);
- os_free(nr);
+ hostapd_neighbor_free(nr);
}
}
@@ -325,3 +332,35 @@
wpabuf_free(nr);
#endif /* NEED_AP_MLME */
}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_get_diff_short_ssid(struct hostapd_data *hapd, const u8 *bssid)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (ether_addr_equal(bssid, nr->bssid) &&
+ nr->short_ssid != hapd->conf->ssid.short_ssid)
+ return nr;
+ }
+ return NULL;
+}
+
+
+int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ nr = hostapd_neighbor_get_diff_short_ssid(hapd, hapd->own_addr);
+ if (!nr)
+ return -1;
+
+ /* Clear old entry due to SSID change */
+ hostapd_neighbor_free(nr);
+
+ hostapd_neighbor_set_own_report(hapd);
+
+ return 0;
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index 992671b..53f7142 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -20,6 +20,7 @@
const struct wpabuf *civic, int stationary,
u8 bss_parameters);
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
+int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
void hostapd_free_neighbor_db(struct hostapd_data *hapd);
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index f2d5cd1..fbcddf3 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -334,6 +334,53 @@
}
+static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "RRM: Link measurement request (token %u) timed out",
+ hapd->link_measurement_req_token);
+ hapd->link_mesr_req_active = 0;
+}
+
+
+static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ const struct rrm_link_measurement_report *report;
+ const u8 *pos, *end;
+ char report_msg[2 * 8 + 1];
+
+ end = buf + len;
+ pos = mgmt->u.action.u.rrm.variable;
+ report = (const struct rrm_link_measurement_report *) (pos - 1);
+ if (end - (const u8 *) report < (int) sizeof(*report))
+ return;
+
+ if (!hapd->link_mesr_req_active ||
+ (hapd->link_measurement_req_token != report->dialog_token)) {
+ wpa_printf(MSG_INFO,
+ "Unexpected Link measurement report, token %u",
+ report->dialog_token);
+ return;
+ }
+
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
+
+ report_msg[0] = '\0';
+ if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
+ pos, end - pos) < 0)
+ return;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
+ MAC2STR(mgmt->sa), report->dialog_token, report_msg);
+}
+
+
void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
@@ -356,6 +403,9 @@
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
hostapd_handle_nei_report_req(hapd, buf, len);
break;
+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ hostapd_handle_link_mesr_report(hapd, buf, len);
+ break;
default:
wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
mgmt->u.action.u.rrm.action);
@@ -563,6 +613,7 @@
hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
}
@@ -672,3 +723,73 @@
" %u ack=%d", MAC2STR(mgmt->da),
mgmt->u.action.u.rrm.dialog_token, ok);
}
+
+
+int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
+ MAC2STR(addr));
+
+ if (!(hapd->iface->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: the driver does not support TX power insertion");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: specied STA is not connected");
+ return -1;
+ }
+
+ if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: destination STA does not support link measurement");
+ return -1;
+ }
+
+ if (hapd->link_mesr_req_active) {
+ wpa_printf(MSG_DEBUG,
+ "Request Link Measurement: request already in process - overriding");
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
+ hapd, NULL);
+ }
+
+ /* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
+ buf = wpabuf_alloc(5);
+ if (!buf)
+ return -1;
+
+ hapd->link_measurement_req_token++;
+ if (!hapd->link_measurement_req_token)
+ hapd->link_measurement_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->link_measurement_req_token);
+ /* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
+ * Power */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_u8(buf, 0);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret < 0)
+ return ret;
+
+ hapd->link_mesr_req_active = 1;
+
+ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+ hostapd_link_mesr_rep_timeout_handler, hapd,
+ NULL);
+
+ return hapd->link_measurement_req_token;
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
index 02cd522..17751e0 100644
--- a/src/ap/rrm.h
+++ b/src/ap/rrm.h
@@ -29,5 +29,7 @@
void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok);
+int hostapd_send_link_measurement_req(struct hostapd_data *hapd,
+ const u8 *addr);
#endif /* RRM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 2178d65..32944ed 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -180,13 +180,26 @@
sta->pasn->fils.erp_resp = NULL;
#endif /* CONFIG_FILS */
- bin_clear_free(sta->pasn, sizeof(*sta->pasn));
+ pasn_data_deinit(sta->pasn);
sta->pasn = NULL;
}
}
#endif /* CONFIG_PASN */
+
+static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_sta_is_link_sta(hapd, sta) &&
+ !hostapd_drv_link_sta_remove(hapd, sta->addr))
+ return;
+#endif /* CONFIG_IEEE80211BE */
+
+ hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_beacon = 0;
@@ -209,7 +222,7 @@
if (!hapd->iface->driver_ap_teardown &&
!(sta->flags & WLAN_STA_PREAUTH)) {
- hostapd_drv_sta_remove(hapd, sta->addr);
+ __ap_free_sta(hapd, sta);
sta->added_unassoc = 0;
}
@@ -454,6 +467,27 @@
}
+#ifdef CONFIG_IEEE80211BE
+void hostapd_free_link_stas(struct hostapd_data *hapd)
+{
+ struct sta_info *sta, *prev;
+
+ sta = hapd->sta_list;
+ while (sta) {
+ prev = sta;
+ sta = sta->next;
+
+ if (!hostapd_sta_is_link_sta(hapd, prev))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "Removing link station from MLD " MACSTR,
+ MAC2STR(prev->addr));
+ ap_free_sta(hapd, prev);
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
/**
* ap_handle_timer - Per STA timer handler
* @eloop_ctx: struct hostapd_data *
@@ -970,16 +1004,15 @@
interfaces = assoc_hapd->iface->interfaces;
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
for (i = 0; i < interfaces->count; i++) {
struct sta_info *tmp_sta;
- if (!assoc_sta->mld_info.links[link_id].valid)
- continue;
-
tmp_hapd = interfaces->iface[i]->bss[0];
- if (!tmp_hapd->conf->mld_ap ||
- assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ if (!hostapd_is_ml_partner(tmp_hapd, assoc_hapd))
continue;
for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
@@ -1433,7 +1466,7 @@
u8 addr[ETH_ALEN];
u8 ip_addr_buf[4];
#endif /* CONFIG_P2P */
- u8 *ip_ptr = NULL;
+ const u8 *ip_ptr = NULL;
#ifdef CONFIG_P2P
if (hapd->p2p_group == NULL) {
@@ -1731,7 +1764,7 @@
unsigned int i, j;
for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
- hapd->conf->mld_id) {
+ hostapd_get_mld_id(hapd)) {
struct sta_info *tmp_sta;
if (hapd == tmp_hapd)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index b136ff7..153e4a0 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -440,4 +440,6 @@
void ap_sta_free_sta_profile(struct mld_info *info);
+void hostapd_free_link_stas(struct hostapd_data *hapd);
+
#endif /* STA_INFO_H */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index b77e21b..af8ccca 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -51,7 +51,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap && (!sta || ap_sta_is_mld(hapd, sta)))
- own_addr = hapd->mld_addr;
+ own_addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
return own_addr;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3002d91..4bf8d79 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3345,10 +3345,7 @@
}
/* 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;
-
+ for_each_link(kde->valid_mlo_links, i) {
/*
* Each entry should contain the link information and the MAC
* address.
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index b286a77..34de45c 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1559,8 +1559,7 @@
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 ||
+ if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
link_id != iface->bss[0]->mld_link_id ||
!iface->bss[0]->wpa_auth)
continue;
@@ -1602,8 +1601,7 @@
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 ||
+ if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
link_id != iface->bss[0]->mld_link_id ||
!iface->bss[0]->wpa_auth)
continue;
diff --git a/src/build.rules b/src/build.rules
index acda884..c756ccb 100644
--- a/src/build.rules
+++ b/src/build.rules
@@ -80,7 +80,7 @@
_DIRS := $(BUILDDIR)/$(PROJ)
.PHONY: _make_dirs
_make_dirs:
- @mkdir -p $(_DIRS)
+ @mkdir -p $(sort $(_DIRS))
$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index f45d2e9..2c47bf8 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -381,6 +381,75 @@
}
+static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
+{
+ u8 first_chan = *seg0 - 6, sec_chan;
+
+ switch (bitmap) {
+ case 0x6:
+ *seg0 = 0;
+ return;
+ case 0x8:
+ case 0x4:
+ case 0x2:
+ case 0x1:
+ case 0xC:
+ case 0x3:
+ if (pri_chan < *seg0)
+ *seg0 -= 4;
+ else
+ *seg0 += 4;
+ break;
+ }
+
+ if (pri_chan < *seg0)
+ sec_chan = pri_chan + 4;
+ else
+ sec_chan = pri_chan - 4;
+
+ if (bitmap & BIT((sec_chan - first_chan) / 4))
+ *seg0 = 0;
+}
+
+
+static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
+ enum oper_chan_width *width, u8 *seg0)
+{
+ if (pri < *seg0) {
+ *seg0 -= 8;
+ if (bitmap & 0x0F) {
+ *width = 0;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+ } else {
+ *seg0 += 8;
+ if (bitmap & 0xF0) {
+ *width = 0;
+ punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
+ seg0);
+ }
+ }
+}
+
+
+void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
+ u8 *seg0, u8 *seg1)
+{
+ if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
+ *width = CONF_OPER_CHWIDTH_USE_HT;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+
+ if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
+ *width = CONF_OPER_CHWIDTH_80MHZ;
+ *seg1 = 0;
+ punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
+ }
+
+ /* TODO: 320 MHz */
+}
+
+
int hostapd_set_freq_params(struct hostapd_freq_params *data,
enum hostapd_hw_mode mode,
int freq, int channel, int enable_edmg,
@@ -391,8 +460,12 @@
int center_segment0,
int center_segment1, u32 vht_caps,
struct he_capabilities *he_cap,
- struct eht_capabilities *eht_cap)
+ struct eht_capabilities *eht_cap,
+ u16 punct_bitmap)
{
+ enum oper_chan_width oper_chwidth_legacy;
+ u8 seg0_legacy, seg1_legacy;
+
if (!he_cap || !he_cap->he_supported)
he_enabled = 0;
if (!eht_cap || !eht_cap->eht_supported)
@@ -578,6 +651,14 @@
break;
}
+ oper_chwidth_legacy = oper_chwidth;
+ seg0_legacy = center_segment0;
+ seg1_legacy = center_segment1;
+ if (punct_bitmap)
+ punct_update_legacy_bw(punct_bitmap, channel,
+ &oper_chwidth_legacy,
+ &seg0_legacy, &seg1_legacy);
+
if (data->eht_enabled || data->he_enabled ||
data->vht_enabled) switch (oper_chwidth) {
case CONF_OPER_CHWIDTH_USE_HT:
@@ -602,7 +683,8 @@
/* fall through */
case CONF_OPER_CHWIDTH_80MHZ:
data->bandwidth = 80;
- if (!sec_channel_offset) {
+ if (!sec_channel_offset &&
+ oper_chwidth_legacy != CONF_OPER_CHWIDTH_USE_HT) {
wpa_printf(MSG_ERROR,
"80/80+80 MHz: no second channel offset");
return -1;
@@ -660,7 +742,8 @@
"160 MHz: center segment 1 should not be set");
return -1;
}
- if (!sec_channel_offset) {
+ if (!sec_channel_offset &&
+ oper_chwidth_legacy != CONF_OPER_CHWIDTH_USE_HT) {
wpa_printf(MSG_ERROR,
"160 MHz: second channel offset not set");
return -1;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 82e0282..e791c33 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -35,6 +35,8 @@
int check_40mhz_2g4(struct hostapd_hw_modes *mode,
struct wpa_scan_results *scan_res, int pri_chan,
int sec_chan);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+ enum oper_chan_width *width, u8 *seg0, u8 *seg1);
int hostapd_set_freq_params(struct hostapd_freq_params *data,
enum hostapd_hw_mode mode,
int freq, int channel, int edmg, u8 edmg_channel,
@@ -45,7 +47,8 @@
int center_segment0,
int center_segment1, u32 vht_caps,
struct he_capabilities *he_caps,
- struct eht_capabilities *eht_cap);
+ struct eht_capabilities *eht_cap,
+ u16 punct_bitmap);
void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
int disabled);
int ieee80211ac_cap_check(u32 hw, u32 conf);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 08ba45b..4de88d1 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -2050,6 +2050,13 @@
}
+bool is_80plus_op_class(u8 op_class)
+{
+ /* Operating classes with "80+" behavior indication in Table E-4 */
+ return op_class == 130 || op_class == 135;
+}
+
+
static int is_11b(u8 rate)
{
return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16
@@ -2417,9 +2424,17 @@
* channel center frequency index value, but it happens to be a 20 MHz
* channel and the channel number in the channel set would match the
* value in for the frequency center.
+ *
+ * Operating class value pair 128 and 130 is used to describe a 80+80
+ * MHz channel on the 5 GHz band. 130 is identified with "80+", so this
+ * is encoded with two octets 130 and 128. Similarly, operating class
+ * value pair 133 and 135 is used to describe a 80+80 MHz channel on
+ * the 6 GHz band (135 being the one with "80+" indication). All other
+ * operating classes listed here are used as 1-octet values.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, P2P_SUPP },
@@ -2427,6 +2442,9 @@
{ HOSTAPD_MODE_IEEE80211A, 135, 1, 233, 16, BW80P80, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 136, 2, 2, 4, BW20, NO_P2P_SUPP },
+ /* IEEE P802.11be/D5.0, Table E-4 (Global operating classes) */
+ { HOSTAPD_MODE_IEEE80211A, 137, 31, 191, 32, BW320, NO_P2P_SUPP },
+
/*
* IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
* Class 180 has the legacy channels 1-6. Classes 181-183 include
@@ -2437,11 +2455,6 @@
{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
- /* Keep the operating class 130 as the last entry as a workaround for
- * the OneHundredAndThirty Delimiter value used in the Supported
- * Operating Classes element to indicate the end of the Operating
- * Classes field. */
- { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
};
@@ -2569,21 +2582,141 @@
}
-size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
+u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
+ struct multi_ap_params *multi_ap)
+{
+ const struct element *elem;
+ bool ext_present = false;
+ unsigned int vlan_id;
+
+ os_memset(multi_ap, 0, sizeof(*multi_ap));
+
+ /* Default profile is 1, when Multi-AP profile subelement is not
+ * present in the element. */
+ multi_ap->profile = 1;
+
+ for_each_element(elem, multi_ap_ie, multi_ap_len) {
+ u8 id = elem->id, elen = elem->datalen;
+ const u8 *pos = elem->data;
+
+ switch (id) {
+ case MULTI_AP_SUB_ELEM_TYPE:
+ if (elen >= 1) {
+ multi_ap->capability = *pos;
+ ext_present = true;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ break;
+ case MULTI_AP_PROFILE_SUB_ELEM_TYPE:
+ if (elen < 1) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE invalid Multi-AP profile subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ multi_ap->profile = *pos;
+ if (multi_ap->profile > MULTI_AP_PROFILE_MAX) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE with invalid profile 0x%02x",
+ multi_ap->profile);
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+ break;
+ case MULTI_AP_VLAN_SUB_ELEM_TYPE:
+ if (multi_ap->profile < MULTI_AP_PROFILE_2) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE invalid profile to read VLAN IE");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ if (elen < 2) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE invalid Multi-AP VLAN subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ vlan_id = WPA_GET_LE16(pos);
+ if (vlan_id < 1 || vlan_id > 4094) {
+ wpa_printf(MSG_INFO,
+ "Multi-AP IE invalid Multi-AP VLAN ID %d",
+ vlan_id);
+ return WLAN_STATUS_INVALID_IE;
+ }
+ multi_ap->vlanid = vlan_id;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Ignore unknown subelement %u in Multi-AP IE",
+ id);
+ break;
+ }
+ }
+
+ if (!for_each_element_completed(elem, multi_ap_ie, multi_ap_len)) {
+ wpa_printf(MSG_DEBUG, "Multi AP IE parse failed @%d",
+ (int) (multi_ap_ie + multi_ap_len -
+ (const u8 *) elem));
+ wpa_hexdump(MSG_MSGDUMP, "IEs", multi_ap_ie, multi_ap_len);
+ }
+
+ if (!ext_present) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP element without Multi-AP Extension subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+size_t add_multi_ap_ie(u8 *buf, size_t len,
+ const struct multi_ap_params *multi_ap)
{
u8 *pos = buf;
+ u8 *len_ptr;
- if (len < 9)
+ if (len < 6)
return 0;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
+ len_ptr = pos; /* Length field to be set at the end */
+ pos++;
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = MULTI_AP_OUI_TYPE;
+
+ /* Multi-AP Extension subelement */
+ if (buf + len - pos < 3)
+ return 0;
*pos++ = MULTI_AP_SUB_ELEM_TYPE;
*pos++ = 1; /* len */
- *pos++ = value;
+ *pos++ = multi_ap->capability;
+
+ /* Add Multi-AP Profile subelement only for R2 or newer configuration */
+ if (multi_ap->profile >= MULTI_AP_PROFILE_2) {
+ if (buf + len - pos < 3)
+ return 0;
+ *pos++ = MULTI_AP_PROFILE_SUB_ELEM_TYPE;
+ *pos++ = 1;
+ *pos++ = multi_ap->profile;
+ }
+
+ /* Add Multi-AP Default 802.1Q Setting subelement only for backhaul BSS
+ */
+ if (multi_ap->vlanid &&
+ multi_ap->profile >= MULTI_AP_PROFILE_2 &&
+ (multi_ap->capability & MULTI_AP_BACKHAUL_BSS)) {
+ if (buf + len - pos < 4)
+ return 0;
+ *pos++ = MULTI_AP_VLAN_SUB_ELEM_TYPE;
+ *pos++ = 2;
+ WPA_PUT_LE16(pos, multi_ap->vlanid);
+ pos += 2;
+ }
+
+ *len_ptr = pos - len_ptr - 1;
return pos - buf;
}
@@ -2748,6 +2881,8 @@
case BW80P80:
case BW160:
return 160;
+ case BW320:
+ return 320;
case BW2160:
return 2160;
default:
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 60260e5..56eb0df 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -30,6 +30,12 @@
u8 nof_ies;
};
+struct multi_ap_params {
+ u8 capability;
+ u8 profile;
+ u16 vlanid;
+};
+
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
@@ -235,6 +241,7 @@
int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes);
int is_dfs_global_op_class(u8 op_class);
+bool is_80plus_op_class(u8 op_class);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
@@ -253,7 +260,7 @@
u8 max_chan;
u8 inc;
enum { BW20, BW40PLUS, BW40MINUS, BW40, BW80, BW2160, BW160, BW80P80,
- BW4320, BW6480, BW8640} bw;
+ BW320, BW4320, BW6480, BW8640} bw;
enum { P2P_SUPP, NO_P2P_SUPP } p2p;
};
@@ -266,7 +273,10 @@
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
-size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
+u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
+ struct multi_ap_params *multi_ap);
+size_t add_multi_ap_ie(u8 *buf, size_t len,
+ const struct multi_ap_params *multi_ap);
struct country_op_class {
u8 country_op_class;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 619aa19..644bebd 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1453,11 +1453,20 @@
#define WFA_CAPA_OUI_TYPE 0x23
#define MULTI_AP_SUB_ELEM_TYPE 0x06
+#define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07
+#define MULTI_AP_VLAN_SUB_ELEM_TYPE 0x08
+
+#define MULTI_AP_PROFILE2_BACKHAUL_STA_DISALLOWED BIT(2)
+#define MULTI_AP_PROFILE1_BACKHAUL_STA_DISALLOWED BIT(3)
#define MULTI_AP_TEAR_DOWN BIT(4)
#define MULTI_AP_FRONTHAUL_BSS BIT(5)
#define MULTI_AP_BACKHAUL_BSS BIT(6)
#define MULTI_AP_BACKHAUL_STA BIT(7)
+#define MULTI_AP_PROFILE_1 1
+#define MULTI_AP_PROFILE_2 2
+#define MULTI_AP_PROFILE_MAX 6
+
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index c00f288..918a1cc 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -19,6 +19,8 @@
unsigned int n_ptksa;
};
+#ifdef CONFIG_PTKSA_CACHE
+
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
@@ -342,3 +344,44 @@
return entry;
}
+
+#else /* CONFIG_PTKSA_CACHE */
+
+struct ptksa_cache * ptksa_cache_init(void)
+{
+ return (struct ptksa_cache *) 1;
+}
+
+
+void ptksa_cache_deinit(struct ptksa_cache *ptksa)
+{
+}
+
+
+struct ptksa_cache_entry *
+ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+ return NULL;
+}
+
+
+int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+struct ptksa_cache_entry *
+ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
+ u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
+ void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
+{
+ return NULL;
+}
+
+
+void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+}
+
+#endif /* CONFIG_PTKSA_CACHE */
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index 6182215..dd5e7db 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -29,7 +29,6 @@
u32 akmp;
};
-#ifdef CONFIG_PTKSA_CACHE
struct ptksa_cache;
@@ -48,41 +47,4 @@
void *ctx, u32 akmp);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
-#else /* CONFIG_PTKSA_CACHE */
-
-static inline struct ptksa_cache * ptksa_cache_init(void)
-{
- return (struct ptksa_cache *) 1;
-}
-
-static inline void ptksa_cache_deinit(struct ptksa_cache *ptksa)
-{
-}
-
-static inline struct ptksa_cache_entry *
-ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
-{
- return NULL;
-}
-
-static inline int ptksa_cache_list(struct ptksa_cache *ptksa,
- char *buf, size_t len)
-{
- return -1;
-}
-
-static inline struct ptksa_cache_entry *
-ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
- u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
- void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
-{
- return NULL;
-}
-
-static inline void ptksa_cache_flush(struct ptksa_cache *ptksa,
- const u8 *addr, u32 cipher)
-{
-}
-
-#endif /* CONFIG_PTKSA_CACHE */
#endif /* PTKSA_CACHE_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index a5bbc78..2a4086b 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1048,6 +1048,15 @@
* to user space to disassociate with a peer based on the peer MAC address
* provided. Specify the peer MAC address in
* QCA_WLAN_VENDOR_ATTR_MAC_ADDR. For MLO, MLD MAC address is provided.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER: This vendor command is used to
+ * adjust transmit power. The attributes used with this subcommand are
+ * defined in enum qca_wlan_vendor_attr_adjust_tx_power.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE: Event indication from the
+ * driver to notify user application about the spectral scan completion.
+ * The attributes used with this subcommand are defined in
+ * enum qca_wlan_vendor_attr_spectral_scan_complete.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1272,6 +1281,8 @@
QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT = 238,
QCA_NL80211_VENDOR_SUBCMD_FLOW_POLICY = 239,
QCA_NL80211_VENDOR_SUBCMD_DISASSOC_PEER = 240,
+ QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER = 241,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE = 242,
};
/* Compatibility defines for previously used subcmd names.
@@ -3361,6 +3372,14 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_QCA_PEER = 106,
+ /* 8-bit unsigned value to configure BTM support.
+ *
+ * The attribute is applicable only for STA interface. Uses enum
+ * qca_wlan_btm_support values. This configuration is not allowed in
+ * connected state.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BTM_SUPPORT = 107,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -7749,6 +7768,20 @@
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_RECAPTURE = 31,
/* Attribute used for padding for 64-bit alignment */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PAD = 32,
+ /* Spectral data transport mode. u32 attribute. It uses values
+ * defined in enum qca_wlan_vendor_spectral_data_transport_mode.
+ * This is an optional attribute. If this attribute is not populated,
+ * the driver should configure the default transport mode to netlink.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_DATA_TRANSPORT_MODE = 33,
+ /* Spectral scan completion timeout. u32 attribute. This
+ * attribute is used to configure a timeout value (in us). The
+ * timeout value would be from the beginning of a spectral
+ * scan. This is an optional attribute. If this attribute is
+ * not populated, the driver would internally derive the
+ * timeout value.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETION_TIMEOUT = 34,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
@@ -12055,6 +12088,14 @@
* %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM or based on
* regulatory/SAE limits if %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM
* is not provided.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFINDEX: u32 attribute, optional.
+ * This specifies the interface index (netdev) for which the corresponding
+ * configurations are applied. If the interface index is not specified, the
+ * configurations are applied based on
+ * %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK.
+ * %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK along with this
+ * attribute shall have the matching nl80211_iftype.
*/
enum qca_wlan_vendor_attr_avoid_frequency_ext {
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_INVALID = 0,
@@ -12063,6 +12104,7 @@
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END = 3,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM = 4,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK = 5,
+ QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFINDEX = 6,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX =
@@ -14263,7 +14305,11 @@
* @QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP: binary, rate mask bitmap.
* A bit value of 1 represents rate is enabled and a value of 0
* represents rate is disabled.
- * For HE targets, 12 bits correspond to one NSS setting.
+ * For EHT targets,
+ * b0-1 => NSS1, MCS 14-15
+ * b2-15 => NSS1, MCS 0-13
+ * b16-29 => NSS2, MCS 0-13
+ * For HE targets, 14 bits correspond to one NSS setting.
* b0-13 => NSS1, MCS 0-13
* b14-27 => NSS2, MCS 0-13 and so on for other NSS.
* For VHT targets, 10 bits correspond to one NSS setting.
@@ -14273,12 +14319,18 @@
* b0-7 => NSS1, MCS 0-7
* b8-15 => NSS2, MCS 0-7 and so on for other NSS.
* For OFDM/CCK targets, 8 bits correspond to one NSS setting.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LINK_ID: u8, used to specify the
+ * MLO link ID of a link to be configured. Optional attribute.
+ * No need of this attribute in non-MLO cases. If the attribute is
+ * not provided, ratemask will be applied for setup link.
*/
enum qca_wlan_vendor_attr_ratemask_params {
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST = 1,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE = 2,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP = 3,
+ QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LINK_ID = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_AFTER_LAST,
@@ -16794,4 +16846,200 @@
QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_LAST - 1,
};
+/**
+ * enum qca_wlan_btm_support: BTM support configuration
+ *
+ * @QCA_WLAN_BTM_SUPPORT_DEFAULT: Restore default BTM support policy. The driver
+ * follows the BSS Transition bit in the Extended Capabilities element from the
+ * connect request IEs with the default BTM support policy.
+ *
+ * @QCA_WLAN_BTM_SUPPORT_DISABLE: Disable BTM support for the subsequent
+ * (re)association attempts. The driver shall restore the default BTM support
+ * policy during the first disconnection after successful association. When this
+ * configuration is enabled, the driver shall overwrite the BSS Transition bit
+ * as zero in the Extended Capabilities element while sending (Re)Association
+ * Request frames. Also, the driver shall drop the BTM frames from userspace and
+ * the connected AP when this configuration is enabled.
+ */
+enum qca_wlan_btm_support {
+ QCA_WLAN_BTM_SUPPORT_DEFAULT = 0,
+ QCA_WLAN_BTM_SUPPORT_DISABLE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_data_rate_type - Represents the possible values for
+ * attribute %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_DATA_RATE_TYPE_LEGACY: Data rate type is a legacy rate code
+ * used in OFDM/CCK.
+ *
+ * @QCA_WLAN_VENDOR_DATA_RATE_TYPE_MCS: Data rate type is an MCS index.
+ *
+ */
+enum qca_wlan_vendor_data_rate_type {
+ QCA_WLAN_VENDOR_DATA_RATE_TYPE_LEGACY = 0,
+ QCA_WLAN_VENDOR_DATA_RATE_TYPE_MCS = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_rate - Definition
+ * of data rate related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE: u8 data rate type.
+ * For this attribute, valid values are enumerated in enum
+ * %qca_wlan_vendor_data_rate_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_VALUE: u8 value.
+ * This attribute value is interpreted according to the value of attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE. For legacy config
+ * type, this attribute value is defined in the units of 0.5 Mbps.
+ * For non legacy config type, this attribute carries the MCS index number.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_POWER_VALUE: u8 value in dBm.
+ * Usually the target computes a final transmit power that is the maximum
+ * power level that doesn't exceed the limits enforced by various sources
+ * like chip-specific conformance test limits (CTL), Specific Absorption
+ * Rate (SAR), Transmit Power Control (TPC), wiphy-specific limits, STA-specific
+ * limits, channel avoidance limits, Automated Frequency Coordination (AFC),
+ * and others. In some cases it may be desirable to use a power level that is
+ * lower than the maximum power level allowed by all of these limits, so this
+ * attribute provides an additional limit that can be used to reduce the
+ * transmit power level.
+ *
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_rate {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_VALUE = 2,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_POWER_VALUE = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_chain_config - Definition
+ * of chain related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INDEX: u8 value.
+ * Represents a particular chain for which transmit power adjustment needed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG: A nested
+ * attribute containing data rate related information to adjust transmit
+ * power. The attributes used inside this nested attributes are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_rate.
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_chain_config {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_band_config - Definition
+ * of band related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INDEX: u8 value to
+ * indicate band for which configuration applies. Valid values are enumerated
+ * in enum %nl80211_band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG: A nested
+ * attribute containing per chain related information to adjust transmit
+ * power. The attributes used inside this nested attribute are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_chain_config.
+ *
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_band_config {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power - Definition of attributes
+ * for %QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER subcommand.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG: A nested attribute
+ * containing per band related information to adjust transmit power.
+ * The attributes used inside this nested attributes are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_band_config.
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_spectral_data_transport_mode - Attribute
+ * values for QCA_WLAN_VENDOR_ATTR_SPECTRAL_DATA_TRANSPORT_MODE.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_NETLINK: Use netlink to
+ * send spectral data to userspace applications.
+ * @QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_RELAY: Use relay interface
+ * to send spectral data to userspace applications.
+ */
+enum qca_wlan_vendor_spectral_data_transport_mode {
+ QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_NETLINK = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_RELAY = 1,
+};
+
+/* enum qca_wlan_vendor_spectral_scan_complete_status - Attribute
+ * values for QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS to
+ * indicate the completion status for a spectral scan.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_SUCCESSFUL:
+ * Indicates a successful completion of the scan.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_TIMEOUT: Indicates
+ * a timeout has occured while processing the spectral reports.
+ */
+enum qca_wlan_vendor_spectral_scan_complete_status {
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_SUCCESSFUL = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_TIMEOUT = 1,
+};
+
+/* enum qca_wlan_vendor_attr_spectral_scan_complete - Definition of
+ * attributes for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE
+ * to indicate scan status and samples received from hardware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_INVALID: Invalid attribute
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS: u32 attribute.
+ * Indicates completion status, either the scan is successful or a timeout
+ * is issued by the driver.
+ * See enum qca_wlan_vendor_spectral_scan_complete_status.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_RECEIVED_SAMPLES: u32
+ * attribute. Number of spectral samples received after the scan has started.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_complete {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS = 1,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_RECEIVED_SAMPLES = 2,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index c82fd0e..6ea3311 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1104,7 +1104,7 @@
link_id = pos[2] & 0x0f;
wpa_printf(MSG_DEBUG, "FT: MLO GTK (Link ID %u)",
link_id);
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_gtks |= BIT(link_id);
parse->mlo_gtk[link_id] = pos;
@@ -1119,7 +1119,7 @@
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)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_igtks |= BIT(link_id);
parse->mlo_igtk[link_id] = pos;
@@ -1134,7 +1134,7 @@
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)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_bigtks |= BIT(link_id);
parse->mlo_bigtk[link_id] = pos;
@@ -1353,7 +1353,7 @@
/* 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++) {
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
if (parse->mlo_gtk[link_id]) {
if (parse->rsn)
prot_ie_count--;
@@ -3551,7 +3551,7 @@
selector == RSN_KEY_DATA_MLO_GTK) {
link_id = (p[0] & RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK) >>
RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_gtks |= BIT(link_id);
@@ -3569,7 +3569,7 @@
selector == RSN_KEY_DATA_MLO_IGTK) {
link_id = (p[8] & RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK) >>
RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_igtks |= BIT(link_id);
@@ -3587,7 +3587,7 @@
selector == RSN_KEY_DATA_MLO_BIGTK) {
link_id = (p[8] & RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK) >>
RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_bigtks |= BIT(link_id);
@@ -3605,7 +3605,7 @@
selector == RSN_KEY_DATA_MLO_LINK) {
link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >>
RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_links |= BIT(link_id);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index a2c7033..01efeea 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -9,6 +9,8 @@
#ifndef WPA_COMMON_H
#define WPA_COMMON_H
+#include "common/defs.h"
+
/* IEEE 802.11i */
#define PMKID_LEN 16
#define PMK_LEN 32
@@ -557,8 +559,6 @@
const u8 *ie2, size_t ie2len);
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace);
-#define MAX_NUM_MLO_LINKS 15
-
struct wpa_ft_ies {
const u8 *mdie;
size_t mdie_len;
@@ -596,14 +596,14 @@
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];
+ const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLD_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];
+ const u8 *mlo_igtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLD_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];
+ const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
struct wpabuf *fte_buf;
};
@@ -700,17 +700,17 @@
const u8 *wmm;
size_t wmm_len;
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];
+ const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_igtks; /* bitmap of valid link IGTK KDEs */
- const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
- size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_igtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK KDEs */
- const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
- size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
- const u8 *mlo_link[MAX_NUM_MLO_LINKS];
- size_t mlo_link_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_link[MAX_NUM_MLD_LINKS];
+ size_t mlo_link_len[MAX_NUM_MLD_LINKS];
};
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index c5bb9ab..f614250 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -413,6 +413,9 @@
/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
#define BEACON_RESP_RX "BEACON-RESP-RX "
+/* parameters: <STA address> <dialog token> <link measurement report> */
+#define LINK_MSR_RESP_RX "LINK-MSR-RESP-RX "
+
/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 427677d..2d8ff60 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1835,6 +1835,7 @@
ret = 0;
fail:
EVP_MAC_CTX_free(ctx);
+ EVP_MAC_free(emac);
return ret;
#else /* OpenSSL version >= 3.0 */
CMAC_CTX *ctx;
@@ -3932,9 +3933,10 @@
group = EC_GROUP_new_by_curve_name(nid);
prime = BN_new();
if (!group || !prime)
- return -1;
+ goto fail;
if (EC_GROUP_get_curve(group, prime, NULL, NULL, NULL) == 1)
prime_len = BN_num_bytes(prime);
+fail:
EC_GROUP_free(group);
BN_free(prime);
return prime_len;
@@ -4880,7 +4882,7 @@
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
if (!hmac)
- return -1;
+ goto fail;
params[0] = OSSL_PARAM_construct_utf8_string(
"digest",
@@ -4889,7 +4891,7 @@
#else /* OpenSSL version >= 3.0 */
hctx = HMAC_CTX_new();
if (!hctx)
- return -1;
+ goto fail;
#endif /* OpenSSL version >= 3.0 */
while (left > 0) {
@@ -4898,7 +4900,7 @@
EVP_MAC_CTX_free(hctx);
hctx = EVP_MAC_CTX_new(hmac);
if (!hctx)
- return -1;
+ goto fail;
if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
goto fail;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index a55e8e3..069e741 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -43,6 +43,7 @@
#define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
#define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
+#define HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL 0x00002000
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
@@ -247,6 +248,11 @@
enum hostapd_hw_mode mode;
/**
+ * is_6ghz - Whether the mode information is for the 6 GHz band
+ */
+ bool is_6ghz;
+
+ /**
* num_channels - Number of entries in the channels array
*/
int num_channels;
@@ -695,6 +701,14 @@
*/
unsigned int min_probe_req_content:1;
+ /**
+ * link_id - Specify the link that is requesting the scan on an MLD
+ *
+ * This is set when operating as an AP MLD and doing an OBSS scan.
+ * -1 indicates that no particular link ID is set.
+ */
+ s8 link_id;
+
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -3371,6 +3385,17 @@
size_t ies_len);
/**
+ * get_scan_results - Fetch the latest scan results
+ * @priv: Private driver interface data
+ * @bssid: Return results only for the specified BSSID, %NULL for all
+ *
+ * Returns: Allocated buffer of scan results (caller is responsible for
+ * freeing the data structure) on success, NULL on failure
+ */
+ struct wpa_scan_results * (*get_scan_results)(void *priv,
+ const u8 *bssid);
+
+ /**
* get_scan_results2 - Fetch the latest scan results
* @priv: private driver interface data
*
@@ -4554,13 +4579,14 @@
/**
* stop_ap - Removes beacon from AP
* @priv: Private driver interface data
+ * @link_id: Link ID of the specified link; -1 for non-MLD
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This optional function can be used to disable AP mode related
* configuration. Unlike deinit_ap, it does not change to station
* mode.
*/
- int (*stop_ap)(void *priv);
+ int (*stop_ap)(void *priv, int link_id);
/**
* get_survey - Retrieve survey data
@@ -5140,9 +5166,44 @@
* @priv: Private driver interface data
* @link_id: The link ID
* @addr: The MAC address to use for the link
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
* Returns: 0 on success, negative value on failure
*/
- int (*link_add)(void *priv, u8 link_id, const u8 *addr);
+ int (*link_add)(void *priv, u8 link_id, const u8 *addr, void *bss_ctx);
+
+ /**
+ * link_remove - Remove a link from the AP MLD interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name of the virtual interface from where the link
+ * is to be removed.
+ * @link_id: Valid link ID to remove
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*link_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id);
+
+ /**
+ * is_drv_shared - Check whether the driver interface is shared
+ * @priv: Private driver interface data from init()
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ *
+ * Checks whether the driver interface is being used by other partner
+ * BSS(s) or not. This is used to decide whether the driver interface
+ * needs to be deinitilized when one interface is getting deinitialized.
+ *
+ * Returns: true if it is being used or else false.
+ */
+ bool (*is_drv_shared)(void *priv, void *bss_ctx);
+
+ /**
+ * link_sta_remove - Remove a link STA from an MLD STA
+ * @priv: Private driver interface data
+ * @link_id: The link ID which the link STA is using
+ * @addr: The MLD MAC address of the MLD STA
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr);
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
@@ -6352,6 +6413,8 @@
* (if available).
* @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
* is set.
+ * @scan_cookie: Unique identification representing the corresponding
+ * scan request. 0 if no unique identification is available.
*/
struct scan_info {
int aborted;
@@ -6363,6 +6426,7 @@
int nl_scan_event;
u64 scan_start_tsf;
u8 scan_start_tsf_bssid[ETH_ALEN];
+ u64 scan_cookie;
} scan_info;
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 9b50b6f..194a5cd 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -18,9 +18,6 @@
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
-#ifdef CONFIG_LIBNL3_ROUTE
-#include <netlink/route/neighbour.h>
-#endif /* CONFIG_LIBNL3_ROUTE */
#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/errqueue.h>
@@ -2295,7 +2292,6 @@
{
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
- unsigned int i;
char path[128], buf[200], *pos;
ssize_t len;
int ret;
@@ -2395,16 +2391,12 @@
}
/*
- * Set the default link to be the first one, and set its address to that
- * of the interface.
+ * Use link ID 0 for the single "link" of a non-MLD.
*/
+ bss->valid_links = 0;
bss->flink = &bss->links[0];
- bss->n_links = 1;
os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
return bss;
failed:
@@ -3079,31 +3071,31 @@
static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
- struct i802_link *link)
+ int link_id)
{
struct nl_msg *msg;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
if (!link->beacon_set)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
- drv->ifindex);
+ bss->ifindex);
link->beacon_set = 0;
link->freq = 0;
nl80211_put_wiphy_data_ap(bss);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
if (!msg)
return -ENOBUFS;
- if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+ if (link_id != NL80211_DRV_LINK_ID_NA) {
wpa_printf(MSG_DEBUG,
"nl80211: MLD: stop beaconing on link=%u",
- link->link_id);
+ link_id);
- if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
- link->link_id)) {
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -3115,10 +3107,10 @@
static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
{
- unsigned int i;
+ int link_id;
- for (i = 0; i < bss->n_links; i++)
- wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+ for_each_link_default(bss->valid_links, link_id, NL80211_DRV_LINK_ID_NA)
+ wpa_driver_nl80211_del_beacon(bss, link_id);
}
@@ -4258,14 +4250,11 @@
struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
{
- unsigned int i;
+ if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+ return bss->flink;
- for (i = 0; i < bss->n_links; i++) {
- if (bss->links[i].link_id != link_id)
- continue;
-
- return &bss->links[i];
- }
+ if (BIT(link_id) & bss->valid_links)
+ return &bss->links[link_id];
return bss->flink;
}
@@ -4282,9 +4271,9 @@
static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
bool bss_freq_debug)
{
- size_t i;
+ u8 i;
- for (i = 0; i < bss->n_links; i++) {
+ for_each_link(bss->valid_links, i) {
if (ether_addr_equal(bss->links[i].addr, addr)) {
wpa_printf(MSG_DEBUG,
"nl80211: Use link freq=%d for address "
@@ -5125,20 +5114,18 @@
#endif /* CONFIG_MESH */
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);
+ if (!nl80211_link_valid(bss->valid_links,
+ params->mld_link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Link ID=%u invalid (valid: 0x%04x)",
+ params->mld_link_id, bss->valid_links);
return -EINVAL;
}
+
+ link = nl80211_get_link(bss, params->mld_link_id);
+ } else if (bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLD configuration expected");
+ return -EINVAL;
}
beacon_set = params->reenable ? 0 : link->beacon_set;
@@ -5179,13 +5166,12 @@
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))
+ params->mld_link_id))
goto fail;
- nl80211_link_set_freq(bss, params->mld_link_id,
- params->freq->freq);
+ if (params->freq)
+ nl80211_link_set_freq(bss, params->mld_link_id,
+ params->freq->freq);
}
if (params->proberesp && params->proberesp_len) {
@@ -5393,6 +5379,9 @@
nla_nest_end(msg, ftm);
}
+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+ goto fail;
+
#ifdef CONFIG_IEEE80211AX
if (params->he_spr_ctrl) {
struct nlattr *spr;
@@ -5427,9 +5416,6 @@
nla_nest_end(msg, spr);
}
- if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
- goto fail;
-
if (params->freq && params->freq->he_enabled &&
nl80211_attr_supported(drv, NL80211_ATTR_HE_BSS_COLOR)) {
struct nlattr *bss_color;
@@ -5548,27 +5534,6 @@
}
-static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
-{
- unsigned int i;
-
- if (link_id < 0)
- return false;
-
- 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;
-
- if (bss->links[i].link_id == link_id)
- return true;
- }
-
- return false;
-}
-
-
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan)
{
@@ -5589,7 +5554,7 @@
return -1;
}
- if (nl80211_link_valid(bss, freq->link_id)) {
+ if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
freq->link_id);
@@ -5955,26 +5920,25 @@
static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_addr;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->ifindex,
+ .ndm_family = AF_BRIDGE,
+ };
+ struct nl_msg *msg;
int err;
- rn = rtnl_neigh_alloc();
- if (!rn)
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
return;
- rtnl_neigh_set_family(rn, AF_BRIDGE);
- rtnl_neigh_set_ifindex(rn, bss->ifindex);
- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
- if (!nl_addr) {
- rtnl_neigh_put(rn);
- return;
- }
- rtnl_neigh_set_lladdr(rn, nl_addr);
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr) ||
+ nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
+ goto errout;
- err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+ err = nl_wait_for_ack(drv->rtnl_sk);
if (err < 0) {
wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
@@ -5984,9 +5948,8 @@
MACSTR, MAC2STR(addr));
}
- nl_addr_put(nl_addr);
- rtnl_neigh_put(rn);
-#endif /* CONFIG_LIBNL3_ROUTE */
+errout:
+ nlmsg_free(msg);
}
@@ -6783,7 +6746,6 @@
if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
struct wpa_driver_mld_params *mld_params = ¶ms->mld_params;
struct nlattr *links, *attr;
- int i;
u8 link_id;
wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR,
@@ -6799,31 +6761,8 @@
if (!links)
return -1;
- attr = nla_nest_start(msg, 0);
- if (!attr)
- return -1;
-
- /* First add the association link ID */
- link_id = mld_params->assoc_link_id;
- if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
- mld_params->mld_links[link_id].bssid) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
- mld_params->mld_links[link_id].freq))
- return -1;
-
- os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
- mld_params->mld_links[link_id].bssid, ETH_ALEN);
-
- nla_nest_end(msg, attr);
-
- for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
- link_id++) {
- if (!(mld_params->valid_links & BIT(link_id)) ||
- link_id == mld_params->assoc_link_id)
- continue;
-
- attr = nla_nest_start(msg, i);
+ for_each_link(mld_params->valid_links, link_id) {
+ attr = nla_nest_start(msg, 0);
if (!attr)
return -1;
@@ -6833,11 +6772,11 @@
mld_params->mld_links[link_id].bssid) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
mld_params->mld_links[link_id].freq) ||
- (mld_params->mld_links[i].disabled &&
+ (mld_params->mld_links[link_id].disabled &&
nla_put_flag(msg,
NL80211_ATTR_MLO_LINK_DISABLED)) ||
(mld_params->mld_links[link_id].ies &&
- mld_params->mld_links[i].ies_len &&
+ mld_params->mld_links[link_id].ies_len &&
nla_put(msg, NL80211_ATTR_IE,
mld_params->mld_links[link_id].ies_len,
mld_params->mld_links[link_id].ies)))
@@ -6847,7 +6786,6 @@
mld_params->mld_links[link_id].bssid,
ETH_ALEN);
nla_nest_end(msg, attr);
- i++;
}
nla_nest_end(msg, links);
@@ -7424,10 +7362,7 @@
/* Error and force TEST_FAIL checking for each link */
ret = -EINVAL;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(params->mld_params.valid_links & BIT(i)))
- continue;
-
+ for_each_link(params->mld_params.valid_links, i) {
if (TEST_FAIL_TAG("link"))
err_info.link_id = i;
}
@@ -8762,7 +8697,6 @@
(params->num_bridge == 0 || !params->bridge[0]))
add_ifidx(drv, br_ifindex, drv->ifindex);
-#ifdef CONFIG_LIBNL3_ROUTE
if (bss->added_if_into_bridge || bss->already_in_bridge) {
int err;
@@ -8779,7 +8713,6 @@
goto failed;
}
}
-#endif /* CONFIG_LIBNL3_ROUTE */
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
wpa_printf(MSG_DEBUG,
@@ -9004,7 +8937,6 @@
if (type == WPA_IF_AP_BSS && setup_ap) {
struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
- unsigned int i;
if (new_bss == NULL) {
if (added)
@@ -9012,10 +8944,6 @@
return -1;
}
- /* Initialize here before any failure path */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
if (bridge &&
i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
@@ -9040,7 +8968,7 @@
new_bss->drv = drv;
new_bss->next = drv->first_bss->next;
new_bss->flink = &new_bss->links[0];
- new_bss->n_links = 1;
+ new_bss->valid_links = 0;
os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
new_bss->flink->freq = drv->first_bss->flink->freq;
@@ -9120,6 +9048,7 @@
tbss->next = bss->next;
/* Unsubscribe management frames */
nl80211_teardown_ap(bss);
+ nl80211_remove_links(bss);
nl80211_destroy_bss(bss);
if (!bss->added_if)
i802_set_iface_flags(bss, 0);
@@ -9134,6 +9063,7 @@
} else {
wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
nl80211_teardown_ap(bss);
+ nl80211_remove_links(bss);
if (!bss->added_if && !drv->first_bss->next)
wpa_driver_nl80211_del_beacon_all(bss);
nl80211_destroy_bss(bss);
@@ -9142,6 +9072,7 @@
if (drv->first_bss->next) {
drv->first_bss = drv->first_bss->next;
drv->ctx = drv->first_bss->ctx;
+ drv->ifindex = drv->first_bss->ifindex;
os_free(bss);
} else {
wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
@@ -9382,10 +9313,7 @@
return 0;
/* First try to pick a link that uses the same band */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo->valid_links, i) {
if (any_valid_link_id == -1)
any_valid_link_id = i;
@@ -9580,59 +9508,81 @@
}
-static void nl80211_remove_links(struct i802_bss *bss)
+static int nl80211_remove_link(struct i802_bss *bss, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_link *link;
struct nl_msg *msg;
+ size_t i;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)",
+ bss->ifindex, link_id);
+
+ if (!(bss->valid_links & BIT(link_id))) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: remove link: Link not found");
+ return -1;
+ }
+
+ link = &bss->links[link_id];
+
+ wpa_driver_nl80211_del_beacon(bss, link_id);
+
+ /* First remove the link locally */
+ bss->valid_links &= ~BIT(link_id);
+ os_memset(link->addr, 0, ETH_ALEN);
+
+ /* Choose new deflink if we are removing that link */
+ if (bss->flink == link) {
+ for_each_link_default(bss->valid_links, i, 0) {
+ bss->flink = &bss->links[i];
+ break;
+ }
+ }
+
+ /* If this was the last link, reset default link */
+ if (!bss->valid_links) {
+ /* TODO: Does keeping freq/bandwidth make sense? */
+ if (bss->flink != link)
+ os_memcpy(bss->flink, link, sizeof(*link));
+
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+ }
+
+ /* Remove the link from the kernel */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed", link_id);
+ return -1;
+ }
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed. ret=%d (%s)",
+ link_id, ret, strerror(-ret));
+
+ return ret;
+}
+
+
+static void nl80211_remove_links(struct i802_bss *bss)
+{
int ret;
u8 link_id;
- if (bss->n_links == 0)
- return;
-
- while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
- struct i802_link *link = &bss->links[0];
-
- wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
- link->link_id);
-
- wpa_driver_nl80211_del_beacon(bss, link);
-
- link_id = link->link_id;
-
- /* First remove the link locally */
- if (bss->n_links == 1) {
- bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
- os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
- } else {
- struct i802_link *other = &bss->links[bss->n_links - 1];
-
- os_memcpy(link, other, sizeof(*link));
- other->link_id = NL80211_DRV_LINK_ID_NA;
- os_memset(other->addr, 0, ETH_ALEN);
-
- bss->n_links--;
- }
-
- /* Remove the link from the kernel */
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
- if (!msg ||
- nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
- nlmsg_free(msg);
- wpa_printf(MSG_ERROR,
- "nl80211: remove link (%d) failed",
- link_id);
- return;
- }
-
- ret = send_and_recv_cmd(drv, msg);
- if (ret) {
- wpa_printf(MSG_ERROR,
- "nl80211: remove link (%d) failed. ret=%d (%s)",
- link_id, ret, strerror(-ret));
- return;
- }
+ for_each_link(bss->valid_links, link_id) {
+ ret = nl80211_remove_link(bss, link_id);
+ if (ret)
+ break;
}
+
+ if (bss->flink)
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
}
@@ -9645,7 +9595,7 @@
return -1;
/* Stop beaconing */
- wpa_driver_nl80211_del_beacon(bss, bss->flink);
+ wpa_driver_nl80211_del_beacon(bss, NL80211_DRV_LINK_ID_NA);
nl80211_remove_links(bss);
@@ -9660,7 +9610,7 @@
}
-static int wpa_driver_nl80211_stop_ap(void *priv)
+static int wpa_driver_nl80211_stop_ap(void *priv, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9668,9 +9618,17 @@
if (!is_ap_interface(drv->nlmode))
return -1;
- wpa_driver_nl80211_del_beacon_all(bss);
+ if (link_id == -1) {
+ wpa_driver_nl80211_del_beacon_all(bss);
+ return 0;
+ }
- return 0;
+ if (nl80211_link_valid(bss->valid_links, link_id)) {
+ wpa_driver_nl80211_del_beacon(bss, link_id);
+ return 0;
+ }
+
+ return -1;
}
@@ -9823,10 +9781,7 @@
if (!sinfo[NL80211_SURVEY_INFO_NOISE])
return NL_SKIP;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_sig->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_sig->valid_links, i) {
if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
mlo_sig->links[i].frequency)
continue;
@@ -9919,10 +9874,7 @@
os_memset(mlo_si, 0, sizeof(*mlo_si));
mlo_si->valid_links = drv->sta_mlo_info.valid_links;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_si->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_si->valid_links, i) {
res = nl80211_get_link_signal(drv,
drv->sta_mlo_info.links[i].bssid,
&mlo_si->links[i].data);
@@ -10846,6 +10798,73 @@
}
+#ifdef CONFIG_IEEE80211BE
+
+static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (type != WPA_IF_AP_BSS ||
+ !nl80211_link_valid(bss->valid_links, link_id))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Teardown AP(%s) link %d (type=%d ifname=%s links=0x%x)",
+ bss->ifname, link_id, type, ifname, bss->valid_links);
+
+ nl80211_remove_link(bss, link_id);
+
+ bss->ctx = bss->flink->ctx;
+
+ if (drv->first_bss == bss && !bss->valid_links)
+ drv->ctx = bss->ctx;
+
+ if (!bss->valid_links) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: No more links remaining, so remove interface");
+ return wpa_driver_nl80211_if_remove(bss, type, ifname);
+ }
+
+ return 0;
+}
+
+
+static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int num_bss = 0;
+
+ /* If any other BSS exist, someone else is using this since at this
+ * time, we would have removed all BSSs created by this driver and only
+ * this BSS should be remaining if the driver is not shared by anyone.
+ */
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ num_bss++;
+ if (num_bss > 1)
+ return true;
+ }
+
+ /* This is the only BSS present */
+ bss = priv;
+
+ /* If only one/no link is there no one is sharing */
+ if (bss->valid_links <= 1)
+ return false;
+
+ /* More than one link means someone is still using. To check if
+ * only 1 bit is set, power of 2 condition can be checked. */
+ if (!(bss->valid_links & (bss->valid_links - 1)))
+ return false;
+
+ return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
unsigned int freq,
@@ -11114,10 +11133,7 @@
return pos - buf;
pos += res;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo->valid_links, i) {
res = os_snprintf(pos, end - pos,
"link_addr[%u]=" MACSTR "\n"
"link_bssid[%u]=" MACSTR "\n"
@@ -12248,13 +12264,14 @@
const u8 *ipaddr, int prefixlen,
const u8 *addr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_ipaddr = NULL;
- struct nl_addr *nl_lladdr = NULL;
- int family, addrsize;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
int res;
if (!ipaddr || prefixlen == 0 || !addr)
@@ -12273,85 +12290,62 @@
}
if (version == 4) {
- family = AF_INET;
+ nhdr.ndm_family = AF_INET;
addrsize = 4;
} else if (version == 6) {
- family = AF_INET6;
+ nhdr.ndm_family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
}
- rn = rtnl_neigh_alloc();
- if (rn == NULL)
+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+ if (!msg)
return -ENOMEM;
- /* set the destination ip address for neigh */
- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
- if (nl_ipaddr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
- res = -ENOMEM;
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_DST, addrsize, (void *) ipaddr) ||
+ nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr))
goto errout;
- }
- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: neigh set destination addr failed");
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
goto errout;
- }
- /* set the corresponding lladdr for neigh */
- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
- if (nl_lladdr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
- res = -ENOMEM;
- goto errout;
- }
- rtnl_neigh_set_lladdr(rn, nl_lladdr);
-
- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
- rtnl_neigh_set_state(rn, NUD_PERMANENT);
-
- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+ res = nl_wait_for_ack(drv->rtnl_sk);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Adding bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
- if (nl_lladdr)
- nl_addr_put(nl_lladdr);
- if (nl_ipaddr)
- nl_addr_put(nl_ipaddr);
- if (rn)
- rtnl_neigh_put(rn);
+ nlmsg_free(msg);
return res;
-#else /* CONFIG_LIBNL3_ROUTE */
- return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
}
static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
const u8 *ipaddr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_ipaddr;
- int family, addrsize;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
int res;
if (!ipaddr)
return -EINVAL;
if (version == 4) {
- family = AF_INET;
+ nhdr.ndm_family = AF_INET;
addrsize = 4;
} else if (version == 6) {
- family = AF_INET6;
+ nhdr.ndm_family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
@@ -12369,41 +12363,28 @@
return -1;
}
- rn = rtnl_neigh_alloc();
- if (rn == NULL)
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
return -ENOMEM;
- /* set the destination ip address for neigh */
- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
- if (nl_ipaddr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
- res = -ENOMEM;
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_DST, addrsize, (void *) ipaddr))
goto errout;
- }
- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: neigh set destination addr failed");
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
goto errout;
- }
- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-
- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+ res = nl_wait_for_ack(drv->rtnl_sk);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Deleting bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
- if (nl_ipaddr)
- nl_addr_put(nl_ipaddr);
- if (rn)
- rtnl_neigh_put(rn);
+ nlmsg_free(msg);
return res;
-#else /* CONFIG_LIBNL3_ROUTE */
- return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
}
@@ -13986,12 +13967,12 @@
}
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ void *bss_ctx)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- unsigned int idx, i;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
@@ -14004,32 +13985,24 @@
return -EINVAL;
}
- if (bss->n_links >= MAX_NUM_MLD_LINKS) {
- wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
- bss->n_links);
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: invalid link_id=%u", link_id);
return -EINVAL;
}
- for (i = 0; i < bss->n_links; i++) {
- if (bss->links[i].link_id == link_id &&
- bss->links[i].beacon_set) {
- wpa_printf(MSG_DEBUG,
- "nl80211: MLD: link already set");
- return -EINVAL;
- }
+ if (bss->valid_links & BIT(link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: Link %u already set", link_id);
+ return -EINVAL;
}
- /* try using the first link entry, assuming it is not beaconing yet */
- if (bss->n_links == 1 &&
- bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+ if (!bss->valid_links) {
+ /* Becoming MLD, verify we were not beaconing */
if (bss->flink->beacon_set) {
wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
return -EINVAL;
}
-
- idx = 0;
- } else {
- idx = bss->n_links;
}
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
@@ -14047,16 +14020,51 @@
return ret;
}
- bss->links[idx].link_id = link_id;
- os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+ os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN);
- bss->n_links = idx + 1;
+ /* The new link is the first one, make it the default */
+ if (!bss->valid_links)
+ bss->flink = &bss->links[link_id];
- wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+ bss->valid_links |= BIT(link_id);
+ bss->links[link_id].ctx = bss_ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
+ bss->valid_links);
return 0;
}
+#ifdef CONFIG_IEEE80211BE
+static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
+ const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!(bss->valid_links & BIT(link_id)))
+ return -ENOLINK;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK_STA)) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, addr) ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_cmd(drv, msg);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: link_sta_remove -> REMOVE_LINK_STA on link_id %u from MLD STA "
+ MACSTR ", from %s --> %d (%s)",
+ link_id, MAC2STR(addr), bss->ifname, ret, strerror(-ret));
+
+ return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
#ifdef CONFIG_TESTING_OPTIONS
static int testing_nl80211_register_frame(void *priv, u16 type,
@@ -14111,7 +14119,7 @@
.scan2 = driver_nl80211_scan2,
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
- .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .get_scan_results = wpa_driver_nl80211_get_scan_results,
.abort_scan = wpa_driver_nl80211_abort_scan,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
@@ -14251,6 +14259,11 @@
#endif /* CONFIG_DPP */
.get_sta_mlo_info = nl80211_get_sta_mlo_info,
.link_add = nl80211_link_add,
+#ifdef CONFIG_IEEE80211BE
+ .link_remove = driver_nl80211_link_remove,
+ .is_drv_shared = nl80211_is_drv_shared,
+ .link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
.radio_disable = testing_nl80211_radio_disable,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 048c9a3..d2c1ffa 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -55,7 +55,6 @@
struct i802_link {
unsigned int beacon_set:1;
- s8 link_id;
int freq;
int bandwidth;
u8 addr[ETH_ALEN];
@@ -66,7 +65,7 @@
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
- size_t n_links;
+ u16 valid_links;
struct i802_link links[MAX_NUM_MLD_LINKS];
struct i802_link *flink;
@@ -292,6 +291,21 @@
void *ack_data,
struct nl80211_err_info *err_info);
+// This function is not used in supplicant anymore. But keeping this wrapper
+// functions for libraries outside wpa_supplicant to build (For eg: lib_driver_cmd_XX)
+static inline int
+send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data)
+{
+ return send_and_recv(drv->global, drv->global->nl, msg,
+ valid_handler, valid_data,
+ ack_handler_custom, ack_data, NULL);
+}
+
static inline int
send_and_recv_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg)
@@ -357,6 +371,18 @@
void nl80211_restore_ap_mode(struct i802_bss *bss);
struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
+static inline bool nl80211_link_valid(u16 links, s8 link_id)
+{
+ if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+ return false;
+
+ if (links & BIT(link_id))
+ return true;
+
+ return false;
+}
+
+
static inline bool
nl80211_attr_supported(struct wpa_driver_nl80211_data *drv, unsigned int attr)
{
@@ -394,7 +420,8 @@
int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params);
int wpa_driver_nl80211_stop_sched_scan(void *priv);
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
+ const u8 *bssid);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 0b6f511..d8375cc 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -888,6 +888,10 @@
nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
}
+ wpa_printf(MSG_DEBUG,
+ "nl80211: EML Capability: 0x%x MLD Capability: 0x%x",
+ capa->eml_capa, capa->mld_capa_and_ops);
+
drv->num_iface_capa++;
if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
break;
@@ -2165,6 +2169,9 @@
for (m = 0; m < *num_modes; m++) {
if (!modes[m].num_channels)
continue;
+
+ modes[m].is_6ghz = false;
+
if (modes[m].channels[0].freq < 2000) {
modes[m].num_channels = 0;
continue;
@@ -2176,10 +2183,14 @@
break;
}
}
- } else if (modes[m].channels[0].freq > 50000)
+ } else if (modes[m].channels[0].freq > 50000) {
modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
- else
+ } else if (is_6ghz_freq(modes[m].channels[0].freq)) {
modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+ modes[m].is_6ghz = true;
+ } else {
+ modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+ }
}
/* Remove unsupported bands */
@@ -2407,6 +2418,57 @@
}
+static void nl80211_set_6ghz_mode(struct hostapd_hw_modes *mode, int start,
+ int end, int max_bw)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->freq - 10 < start || chan->freq + 10 > end)
+ continue;
+
+ if (max_bw >= 80)
+ chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
+
+ if (max_bw >= 160)
+ chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
+
+ if (max_bw >= 320)
+ chan->flag |= HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL;
+ }
+}
+
+
+static void nl80211_reg_rule_6ghz(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (!tb[NL80211_ATTR_FREQ_RANGE_START] ||
+ !tb[NL80211_ATTR_FREQ_RANGE_END] ||
+ !tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+ if (max_bw < 80)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (results->modes[m].num_channels == 0 ||
+ !is_6ghz_freq(results->modes[m].channels[0].freq))
+ continue;
+
+ nl80211_set_6ghz_mode(&results->modes[m], start, end, max_bw);
+ }
+}
+
+
static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
u8 *dfs_domain)
{
@@ -2525,6 +2587,13 @@
nl80211_reg_rule_vht(tb_rule, results);
}
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_6ghz(tb_rule, results);
+ }
+
return NL_SKIP;
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 4163f79..9ce73c6 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -476,10 +476,7 @@
* links when the link used for (re)association is removed.
*/
if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(drv->sta_mlo_info.valid_links & BIT(i)))
- continue;
-
+ for_each_link(drv->sta_mlo_info.valid_links, i) {
os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
ETH_ALEN);
drv->sta_mlo_info.assoc_link_id = i;
@@ -701,10 +698,7 @@
}
/* Get MLO links info for rejected links */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
- continue;
-
+ for_each_link((mlo->req_links & ~mlo->valid_links), i) {
os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
}
@@ -874,9 +868,7 @@
wpa_printf(MSG_DEBUG,
"nl80211: TID-to-link: Received uplink %x downlink %x",
uplink, downlink);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(drv->sta_mlo_info.valid_links & BIT(i)))
- continue;
+ for_each_link(drv->sta_mlo_info.valid_links, i) {
if (uplink & BIT(i))
event.t2l_map_info.t2lmap[i].uplink |=
BIT(tidnum);
@@ -1268,14 +1260,23 @@
if (cf2)
data.ch_switch.cf2 = nla_get_u32(cf2);
- if (finished)
- bss->flink->freq = data.ch_switch.freq;
-
if (link)
data.ch_switch.link_id = nla_get_u8(link);
else
data.ch_switch.link_id = NL80211_DRV_LINK_ID_NA;
+ if (finished) {
+ if (data.ch_switch.link_id != NL80211_DRV_LINK_ID_NA) {
+ struct i802_link *mld_link;
+
+ mld_link = nl80211_get_link(bss,
+ data.ch_switch.link_id);
+ mld_link->freq = data.ch_switch.freq;
+ } else {
+ bss->flink->freq = data.ch_switch.freq;
+ }
+ }
+
if (link && is_sta_interface(drv->nlmode)) {
u8 link_id = data.ch_switch.link_id;
@@ -1618,18 +1619,17 @@
}
-static struct i802_link *
-nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
+static s8
+nl80211_get_link_id_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];
+ for_each_link(bss->valid_links, i) {
+ if ((unsigned int) bss->links[i].freq == freq)
+ return i;
}
- return NULL;
+ return NL80211_DRV_LINK_ID_NA;
}
@@ -1663,12 +1663,12 @@
/* Determine the MLD link either by an explicitly provided link id or
* finding a match based on the frequency. */
if (link)
- mld_link = nl80211_get_link(bss, nla_get_u8(link));
+ link_id = nla_get_u8(link);
else if (freq)
- mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
+ link_id = nl80211_get_link_id_by_freq(bss, nla_get_u32(freq));
- if (mld_link)
- link_id = mld_link->link_id;
+ if (nl80211_link_valid(bss->valid_links, link_id))
+ mld_link = nl80211_get_link(bss, link_id);
data = nla_data(frame);
len = nla_len(frame);
@@ -1920,7 +1920,7 @@
os_memset(&data, 0, sizeof(data));
addr = nla_data(tb[NL80211_ATTR_MAC]);
- if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+ if (!bss->valid_links &&
(tb[NL80211_ATTR_MLO_LINK_ID] ||
tb[NL80211_ATTR_MLD_ADDR])) {
wpa_printf(MSG_ERROR,
@@ -2184,7 +2184,7 @@
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 &&
+ if (!bss->valid_links &&
(tb[NL80211_ATTR_MLO_LINK_ID] ||
tb[NL80211_ATTR_MLD_ADDR])) {
wpa_printf(MSG_ERROR,
@@ -2452,7 +2452,6 @@
{
union wpa_event_data data;
enum nl80211_radar_event event_type;
- struct i802_link *mld_link = NULL;
if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
return;
@@ -2462,11 +2461,13 @@
data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
- if (data.dfs_event.freq) {
- mld_link = nl80211_get_mld_link_by_freq(drv->first_bss,
- data.dfs_event.freq);
- if (mld_link)
- data.dfs_event.link_id = mld_link->link_id;
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ data.dfs_event.link_id =
+ nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ } else if (data.dfs_event.freq) {
+ data.dfs_event.link_id =
+ nl80211_get_link_id_by_freq(drv->first_bss,
+ data.dfs_event.freq);
}
/* Check HT params */
@@ -2831,7 +2832,6 @@
{
union wpa_event_data data;
struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct i802_link *mld_link = NULL;
wpa_printf(MSG_DEBUG,
"nl80211: DFS offload radar vendor event received");
@@ -2850,11 +2850,13 @@
data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
- if (data.dfs_event.freq) {
- mld_link = nl80211_get_mld_link_by_freq(drv->first_bss,
- data.dfs_event.freq);
- if (mld_link)
- data.dfs_event.link_id = mld_link->link_id;
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ data.dfs_event.link_id =
+ nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ } else if (data.dfs_event.freq) {
+ data.dfs_event.link_id =
+ nl80211_get_link_id_by_freq(drv->first_bss,
+ data.dfs_event.freq);
}
wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, link=%d",
@@ -2966,6 +2968,7 @@
info = &event.scan_info;
info->aborted = aborted;
info->external_scan = external_scan;
+ info->scan_cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl,
@@ -3748,8 +3751,11 @@
(long long unsigned int) cookie,
match ? " (match)" : "",
drv->send_frame_cookie == cookie ? " (match-saved)" : "");
- if (drv->send_frame_cookie == cookie)
+ if (drv->send_frame_cookie == cookie) {
drv->send_frame_cookie = (u64) -1;
+ if (!match)
+ goto send_event;
+ }
if (!match)
return;
@@ -3759,6 +3765,7 @@
(drv->num_send_frame_cookies - i - 1) * sizeof(u64));
drv->num_send_frame_cookies--;
+send_event:
wpa_supplicant_event(drv->ctx, EVENT_TX_WAIT_EXPIRE, NULL);
}
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 68ae579..1eb4374 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -728,7 +728,7 @@
static struct wpa_scan_res *
nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
- struct nl_msg *msg)
+ struct nl_msg *msg, const u8 *bssid)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -762,6 +762,9 @@
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy))
return NULL;
+ if (bssid && bss[NL80211_BSS_BSSID] &&
+ !ether_addr_equal(bssid, nla_data(bss[NL80211_BSS_BSSID])))
+ return NULL;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -866,6 +869,7 @@
struct nl80211_bss_info_arg {
struct wpa_driver_nl80211_data *drv;
struct wpa_scan_results *res;
+ const u8 *bssid;
};
static int bss_info_handler(struct nl_msg *msg, void *arg)
@@ -875,7 +879,7 @@
struct wpa_scan_res **tmp;
struct wpa_scan_res *r;
- r = nl80211_parse_bss_info(_arg->drv, msg);
+ r = nl80211_parse_bss_info(_arg->drv, msg, _arg->bssid);
if (!r)
return NL_SKIP;
@@ -973,7 +977,7 @@
static struct wpa_scan_results *
-nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
{
struct nl_msg *msg;
struct wpa_scan_results *res;
@@ -993,6 +997,7 @@
arg.drv = drv;
arg.res = res;
+ arg.bssid = bssid;
ret = send_and_recv_resp(drv, msg, bss_info_handler, &arg);
if (ret == -EAGAIN) {
count++;
@@ -1029,16 +1034,18 @@
/**
* wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ * @bssid: Return results only for the specified BSSID, %NULL for all
* Returns: Scan results on success, -1 on failure
*/
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
+ const u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct wpa_scan_results *res;
- res = nl80211_get_scan_results(drv);
+ res = nl80211_get_scan_results(drv, bssid);
if (res)
wpa_driver_nl80211_check_bss_status(drv, res);
return res;
@@ -1055,7 +1062,7 @@
struct nl80211_dump_scan_ctx *ctx = arg;
struct wpa_scan_res *r;
- r = nl80211_parse_bss_info(ctx->drv, msg);
+ r = nl80211_parse_bss_info(ctx->drv, msg, NULL);
if (!r)
return NL_SKIP;
wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
@@ -1268,6 +1275,11 @@
goto fail;
}
+ if (is_ap_interface(drv->nlmode) &&
+ params->link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID, params->link_id))
+ goto fail;
+
nla_nest_end(msg, attr);
ret = send_and_recv_resp(drv, msg, scan_cookie_handler, &cookie);
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 0186099..3b3098d 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -160,7 +160,6 @@
NEED_LINUX_IOCTL=y
ifdef CONFIG_VLAN_NETLINK
NEED_LIBNL=y
-CONFIG_LIBNL3_ROUTE=y
endif
endif
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 8c58456..1cbe652 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -154,7 +154,6 @@
NEED_LINUX_IOCTL=y
ifdef CONFIG_VLAN_NETLINK
NEED_LIBNL=y
-CONFIG_LIBNL3_ROUTE=y
endif
endif
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index a1e7bff..fe61c83 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -255,8 +255,18 @@
cfg.new_ap_settings = &new_ap_settings;
}
- if (os_strstr(phase1, "multi_ap=1"))
- cfg.multi_ap_backhaul_sta = 1;
+ pos = os_strstr(phase1, "multi_ap=");
+ if (pos) {
+ u16 id = atoi(pos + 9);
+
+ if (id != 0) {
+ cfg.multi_ap_backhaul_sta = 1;
+ cfg.multi_ap_profile = id;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "EAP-WSC: Invalid multi_ap setting");
+ }
+ }
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index 3f0b299..481c8ca 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -30,6 +30,9 @@
#include "eloop.h"
#include "l2_packet.h"
+#ifndef ETHER_VLAN_ENCAP_LEN
+#define ETHER_VLAN_ENCAP_LEN 4
+#endif
static const u8 pae_group_addr[ETH_ALEN] =
{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c
new file mode 100644
index 0000000..e2c6681
--- /dev/null
+++ b/src/pasn/pasn_common.c
@@ -0,0 +1,232 @@
+/*
+ * PASN common processing
+ *
+ * Copyright (C) 2024, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "common/ieee802_11_defs.h"
+#include "pasn_common.h"
+
+
+struct pasn_data * pasn_data_init(void)
+{
+ struct pasn_data *pasn = os_zalloc(sizeof(struct pasn_data));
+
+ return pasn;
+}
+
+
+void pasn_data_deinit(struct pasn_data *pasn)
+{
+ bin_clear_free(pasn, sizeof(struct pasn_data));
+}
+
+
+void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx,
+ int (*send_mgmt)(void *ctx, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+ unsigned int wait),
+ int (*validate_custom_pmkid)(void *ctx,
+ const u8 *addr,
+ const u8 *pmkid))
+{
+ if (!pasn)
+ return;
+
+ pasn->cb_ctx = cb_ctx;
+ pasn->send_mgmt = send_mgmt;
+ pasn->validate_custom_pmkid = validate_custom_pmkid;
+}
+
+
+void pasn_enable_kdk_derivation(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return;
+ pasn->derive_kdk = true;
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+}
+
+
+void pasn_disable_kdk_derivation(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return;
+ pasn->derive_kdk = false;
+ pasn->kdk_len = 0;
+}
+
+
+void pasn_set_akmp(struct pasn_data *pasn, int akmp)
+{
+ if (!pasn)
+ return;
+ pasn->akmp = akmp;
+}
+
+
+void pasn_set_cipher(struct pasn_data *pasn, int cipher)
+{
+ if (!pasn)
+ return;
+ pasn->cipher = cipher;
+}
+
+
+void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->own_addr, addr, ETH_ALEN);
+}
+
+
+void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+}
+
+
+void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->bssid, addr, ETH_ALEN);
+}
+
+
+int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt)
+{
+ if (!pasn)
+ return -1;
+#ifdef CONFIG_SAE
+ pasn->pt = pt;
+ return 0;
+#else /* CONFIG_SAE */
+ return -1;
+#endif /* CONFIG_SAE */
+}
+
+
+void pasn_set_password(struct pasn_data *pasn, const char *password)
+{
+ if (!pasn)
+ return;
+ pasn->password = password;
+}
+
+
+void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt)
+{
+ if (!pasn)
+ return;
+ pasn->wpa_key_mgmt = key_mgmt;
+}
+
+
+void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise)
+{
+ if (!pasn)
+ return;
+ pasn->rsn_pairwise = rsn_pairwise;
+}
+
+
+void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab)
+{
+ if (!pasn)
+ return;
+ pasn->rsnxe_capab = rsnxe_capab;
+}
+
+
+void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie)
+{
+ if (!pasn || !rsnxe_ie)
+ return;
+ pasn->rsnxe_ie = rsnxe_ie;
+}
+
+
+void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid)
+{
+ if (!pasn || !pmkid)
+ return;
+ os_memcpy(pasn->custom_pmkid, pmkid, PMKID_LEN);
+ pasn->custom_pmkid_valid = true;
+}
+
+
+int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ if (!pasn || !extra_ies_len || !extra_ies)
+ return -1;
+
+ if (pasn->extra_ies) {
+ os_free((u8 *) pasn->extra_ies);
+ pasn->extra_ies_len = extra_ies_len;
+ }
+
+ pasn->extra_ies = os_memdup(extra_ies, extra_ies_len);
+ if (!pasn->extra_ies) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Extra IEs memory allocation failed");
+ return -1;
+ }
+ pasn->extra_ies_len = extra_ies_len;
+ return 0;
+}
+
+
+int pasn_get_akmp(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->akmp;
+}
+
+
+int pasn_get_cipher(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->cipher;
+}
+
+
+size_t pasn_get_pmk_len(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->pmk_len;
+}
+
+
+u8 * pasn_get_pmk(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return NULL;
+ return pasn->pmk;
+}
+
+
+struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return NULL;
+ return &pasn->ptk;
+}
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index a4850a2..36710c2 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -16,8 +16,6 @@
extern "C" {
#endif
-#ifdef CONFIG_PASN
-
enum pasn_fils_state {
PASN_FILS_STATE_NONE = 0,
PASN_FILS_STATE_PENDING_AS,
@@ -35,19 +33,46 @@
};
struct pasn_data {
+ /* External modules access below variables using setter and getter
+ * functions */
int akmp;
int cipher;
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ struct rsn_pmksa_cache *pmksa;
+ bool derive_kdk;
+ size_t kdk_len;
+ void *cb_ctx;
+
+#ifdef CONFIG_SAE
+ struct sae_pt *pt;
+#endif /* CONFIG_SAE */
+
+ /* Responder */
+ const char *password;
+ int wpa_key_mgmt;
+ int rsn_pairwise;
+ u16 rsnxe_capab;
+ const u8 *rsnxe_ie;
+ bool custom_pmkid_valid;
+ u8 custom_pmkid[PMKID_LEN];
+
+ /*
+ * Extra elements to add into Authentication frames. These can be used,
+ * e.g., for Wi-Fi Aware use cases.
+ */
+ const u8 *extra_ies;
+ size_t extra_ies_len;
+
+ /* External modules do not access below variables */
u16 group;
bool secure_ltf;
int freq;
- size_t kdk_len;
u8 trans_seq;
u8 status;
- u8 own_addr[ETH_ALEN];
- u8 peer_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
size_t pmk_len;
u8 pmk[PMK_LEN_MAX];
bool using_pmksa;
@@ -63,7 +88,6 @@
#ifdef CONFIG_SAE
struct sae_data sae;
- struct sae_pt *pt;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
@@ -81,15 +105,12 @@
* differently for the PASN initiator (using RSN Supplicant
* implementation) and PASN responser (using RSN Authenticator
* implementation). Functions cannot be mixed between those cases. */
- struct rsn_pmksa_cache *pmksa;
struct rsn_pmksa_cache_entry *pmksa_entry;
struct eapol_sm *eapol;
int fast_reauth;
#ifdef CONFIG_TESTING_OPTIONS
int corrupt_mic;
#endif /* CONFIG_TESTING_OPTIONS */
- void *cb_ctx;
- u16 rsnxe_capab;
int network_id;
u8 wrapped_data_format;
@@ -97,16 +118,11 @@
/* Responder */
bool noauth; /* Whether PASN without mutual authentication is enabled */
- int wpa_key_mgmt;
- int rsn_pairwise;
- bool derive_kdk;
- const char *password;
int disable_pmksa_caching;
int *pasn_groups;
struct wpabuf *wrapped_data;
int use_anti_clogging;
const u8 *rsn_ie;
- const u8 *rsnxe_ie;
size_t rsn_ie_len;
u8 *comeback_key;
@@ -114,16 +130,6 @@
u16 comeback_idx;
u16 *comeback_pending_idx;
- bool custom_pmkid_valid;
- u8 custom_pmkid[PMKID_LEN];
-
- /**
- * Extra elements to add into Authentication frames. These can be used,
- * e.g., for Wi-Fi Aware use cases.
- */
- const u8 *extra_ies;
- size_t extra_ies_len;
-
/**
* send_mgmt - Function handler to transmit a Management frame
* @ctx: Callback context from cb_ctx
@@ -147,7 +153,6 @@
};
/* Initiator */
-
void wpa_pasn_reset(struct pasn_data *pasn);
int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
const u8 *peer_addr, const u8 *bssid,
@@ -177,7 +182,45 @@
const u8 *peer_addr,
struct rsn_pmksa_cache_entry *pmksa, u16 status);
-#endif /* CONFIG_PASN */
+struct pasn_data * pasn_data_init(void);
+void pasn_data_deinit(struct pasn_data *pasn);
+void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx,
+ int (*send_mgmt)(void *ctx, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+ unsigned int wait),
+ int (*validate_custom_pmkid)(void *ctx,
+ const u8 *addr,
+ const u8 *pmkid));
+void pasn_enable_kdk_derivation(struct pasn_data *pasn);
+void pasn_disable_kdk_derivation(struct pasn_data *pasn);
+
+void pasn_set_akmp(struct pasn_data *pasn, int akmp);
+void pasn_set_cipher(struct pasn_data *pasn, int cipher);
+void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr);
+void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr);
+void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr);
+void pasn_set_initiator_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa);
+void pasn_set_responder_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa);
+int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt);
+
+/* Responder */
+void pasn_set_password(struct pasn_data *pasn, const char *password);
+void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt);
+void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise);
+void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab);
+void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie);
+void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid);
+int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies,
+ size_t extra_ies_len);
+
+int pasn_get_akmp(struct pasn_data *pasn);
+int pasn_get_cipher(struct pasn_data *pasn);
+size_t pasn_get_pmk_len(struct pasn_data *pasn);
+u8 * pasn_get_pmk(struct pasn_data *pasn);
+struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn);
#ifdef __cplusplus
}
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index 35c6206..d273067 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -26,6 +26,14 @@
#include "pasn_common.h"
+void pasn_set_initiator_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa)
+{
+ if (pasn)
+ pasn->pmksa = pmksa;
+}
+
+
#ifdef CONFIG_SAE
static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn)
@@ -741,6 +749,11 @@
pasn->rsn_ie_len = 0;
pasn->rsnxe_ie = NULL;
pasn->custom_pmkid_valid = false;
+
+ if (pasn->extra_ies) {
+ os_free((u8 *) pasn->extra_ies);
+ pasn->extra_ies = NULL;
+ }
}
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index 7501e7a..b991364 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -25,6 +25,15 @@
#include "ap/pmksa_cache_auth.h"
#include "pasn_common.h"
+
+void pasn_set_responder_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa)
+{
+ if (pasn)
+ pasn->pmksa = pmksa;
+}
+
+
#ifdef CONFIG_PASN
#ifdef CONFIG_SAE
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 18aaec1..2a7f361 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -1,18 +1,20 @@
/*
* RADIUS client
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
+#include <fcntl.h>
#include <net/if.h>
#include "common.h"
+#include "eloop.h"
+#include "crypto/tls.h"
#include "radius.h"
#include "radius_client.h"
-#include "eloop.h"
/* Defaults for RADIUS retransmit values (exponential backoff) */
@@ -169,36 +171,36 @@
struct hostapd_radius_servers *conf;
/**
- * auth_serv_sock - IPv4 socket for RADIUS authentication messages
- */
- int auth_serv_sock;
-
- /**
- * acct_serv_sock - IPv4 socket for RADIUS accounting messages
- */
- int acct_serv_sock;
-
- /**
- * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
- */
- int auth_serv_sock6;
-
- /**
- * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
- */
- int acct_serv_sock6;
-
- /**
* auth_sock - Currently used socket for RADIUS authentication server
*/
int auth_sock;
/**
+ * auth_tls - Whether current authentication connection uses TLS
+ */
+ bool auth_tls;
+
+ /**
+ * auth_tls_ready - Whether authentication TLS is ready
+ */
+ bool auth_tls_ready;
+
+ /**
* acct_sock - Currently used socket for RADIUS accounting server
*/
int acct_sock;
/**
+ * acct_tls - Whether current accounting connection uses TLS
+ */
+ bool acct_tls;
+
+ /**
+ * acct_tls_ready - Whether accounting TLS is ready
+ */
+ bool acct_tls_ready;
+
+ /**
* auth_handlers - Authentication message handlers
*/
struct radius_rx_handler *auth_handlers;
@@ -242,6 +244,12 @@
* interim_error_cb_ctx - interim_error_cb() context data
*/
void *interim_error_cb_ctx;
+
+#ifdef CONFIG_RADIUS_TLS
+ void *tls_ctx;
+ struct tls_connection *auth_tls_conn;
+ struct tls_connection *acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
};
@@ -249,7 +257,7 @@
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
- int sock, int sock6, int auth);
+ int auth);
static int radius_client_init_acct(struct radius_client_data *radius);
static int radius_client_init_auth(struct radius_client_data *radius);
static void radius_client_auth_failover(struct radius_client_data *radius);
@@ -374,9 +382,19 @@
u8 *acct_delay_time;
size_t acct_delay_time_len;
int num_servers;
+#ifdef CONFIG_RADIUS_TLS
+ struct wpabuf *out = NULL;
+ struct tls_connection *conn = NULL;
+ bool acct = false;
+#endif /* CONFIG_RADIUS_TLS */
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) {
+#ifdef CONFIG_RADIUS_TLS
+ acct = true;
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_acct_servers;
if (radius->acct_sock < 0)
radius_client_init_acct(radius);
@@ -394,6 +412,10 @@
conf->acct_server->retransmissions++;
}
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_auth_servers;
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
@@ -429,6 +451,15 @@
return 1;
}
+#ifdef CONFIG_RADIUS_TLS
+ if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
+ (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection not yet ready for TX");
+ goto not_ready;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
if (entry->msg_type == RADIUS_ACCT &&
radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
&acct_delay_time, &acct_delay_time_len,
@@ -473,11 +504,37 @@
os_get_reltime(&entry->last_attempt);
buf = radius_msg_get_buf(entry->msg);
+#ifdef CONFIG_RADIUS_TLS
+ if (conn) {
+ out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to encrypt RADIUS message (TLS)");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
+ wpabuf_len(buf), wpabuf_len(out));
+ buf = out;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
+ wpabuf_len(buf));
if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
if (radius_client_handle_send_error(radius, s, entry->msg_type)
- > 0)
+ > 0) {
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+#endif /* CONFIG_RADIUS_TLS */
return 0;
+ }
}
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+
+not_ready:
+#endif /* CONFIG_RADIUS_TLS */
entry->next_try = now + entry->next_wait;
entry->next_wait *= 2;
@@ -598,9 +655,7 @@
if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
next = conf->auth_servers;
conf->auth_server = next;
- radius_change_server(radius, next, old,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1);
+ radius_change_server(radius, next, old, 1);
}
@@ -628,9 +683,7 @@
if (next > &conf->acct_servers[conf->num_acct_servers - 1])
next = conf->acct_servers;
conf->acct_server = next;
- radius_change_server(radius, next, old,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0);
+ radius_change_server(radius, next, old, 0);
}
@@ -719,6 +772,52 @@
}
+static int radius_client_disable_pmtu_discovery(int s)
+{
+ int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+ int action = IP_PMTUDISC_DONT;
+ r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+ sizeof(action));
+ if (r == -1)
+ wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+ strerror(errno));
+#endif
+ return r;
+}
+
+
+static void radius_close_auth_socket(struct radius_client_data *radius)
+{
+ if (radius->auth_sock >= 0) {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->conf->auth_server->tls)
+ eloop_unregister_sock(radius->auth_sock,
+ EVENT_TYPE_WRITE);
+#endif /* CONFIG_RADIUS_TLS */
+ eloop_unregister_read_sock(radius->auth_sock);
+ close(radius->auth_sock);
+ radius->auth_sock = -1;
+ }
+}
+
+
+static void radius_close_acct_socket(struct radius_client_data *radius)
+{
+ if (radius->acct_sock >= 0) {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->conf->acct_server->tls)
+ eloop_unregister_sock(radius->acct_sock,
+ EVENT_TYPE_WRITE);
+#endif /* CONFIG_RADIUS_TLS */
+ eloop_unregister_read_sock(radius->acct_sock);
+ close(radius->acct_sock);
+ radius->acct_sock = -1;
+ }
+}
+
+
/**
* radius_client_send - Send a RADIUS request
* @radius: RADIUS client context from radius_client_init()
@@ -754,8 +853,18 @@
char *name;
int s, res;
struct wpabuf *buf;
+#ifdef CONFIG_RADIUS_TLS
+ struct wpabuf *out = NULL;
+ struct tls_connection *conn = NULL;
+ bool acct = false;
+#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+#ifdef CONFIG_RADIUS_TLS
+ acct = true;
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
if (conf->acct_server && radius->acct_sock < 0)
radius_client_init_acct(radius);
@@ -774,6 +883,10 @@
s = radius->acct_sock;
conf->acct_server->requests++;
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
if (conf->auth_server && radius->auth_sock < 0)
radius_client_init_auth(radius);
@@ -799,11 +912,42 @@
if (conf->msg_dumps)
radius_msg_dump(msg);
+#ifdef CONFIG_RADIUS_TLS
+ if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
+ (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection not yet ready for TX");
+ goto skip_send;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
buf = radius_msg_get_buf(msg);
+#ifdef CONFIG_RADIUS_TLS
+ if (conn) {
+ out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to encrypt RADIUS message (TLS)");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
+ wpabuf_len(buf), wpabuf_len(out));
+ buf = out;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+ wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
+ wpabuf_len(buf));
res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+#endif /* CONFIG_RADIUS_TLS */
if (res < 0)
radius_client_handle_send_error(radius, s, msg_type);
+#ifdef CONFIG_RADIUS_TLS
+skip_send:
+#endif /* CONFIG_RADIUS_TLS */
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
@@ -811,6 +955,137 @@
}
+#ifdef CONFIG_RADIUS_TLS
+
+static void radius_client_close_tcp(struct radius_client_data *radius,
+ int sock, RadiusType msg_type)
+{
+ wpa_printf(MSG_DEBUG, "RADIUS: Closing TCP connection (sock %d)",
+ sock);
+ if (msg_type == RADIUS_ACCT) {
+ radius->acct_tls_ready = false;
+ radius_close_acct_socket(radius);
+ } else {
+ radius->auth_tls_ready = false;
+ radius_close_auth_socket(radius);
+ }
+}
+
+
+static void
+radius_client_process_tls_handshake(struct radius_client_data *radius,
+ int sock, RadiusType msg_type,
+ u8 *buf, size_t len)
+{
+ struct wpabuf *in, *out = NULL, *appl;
+ struct tls_connection *conn;
+ int res;
+ bool ready = false;
+
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Process %zu bytes of received TLS handshake message",
+ len);
+
+ if (msg_type == RADIUS_ACCT)
+ conn = radius->acct_tls_conn;
+ else
+ conn = radius->auth_tls_conn;
+
+ in = wpabuf_alloc_copy(buf, len);
+ if (!in)
+ return;
+
+ appl = NULL;
+ out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
+ wpabuf_free(in);
+ if (!out) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Could not generate TLS handshake data");
+ goto fail;
+ }
+
+ if (tls_connection_get_failed(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
+ goto fail;
+ }
+
+ if (tls_connection_established(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection established (sock=%d)",
+ sock);
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_ready = true;
+ else
+ radius->auth_tls_ready = true;
+ ready = true;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
+ wpabuf_len(out));
+ res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res != wpabuf_len(out)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
+ res);
+ goto fail;
+ }
+ wpabuf_free(out);
+
+ if (ready) {
+ struct radius_msg_list *entry, *prev, *tmp;
+ struct os_reltime now;
+
+ /* Send all pending message of matching type since the TLS
+ * tunnel has now been established. */
+
+ os_get_reltime(&now);
+
+ entry = radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (entry->msg_type != msg_type) {
+ prev = entry;
+ entry = entry->next;
+ continue;
+ }
+
+ if (radius_client_retransmit(radius, entry, now.sec)) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+
+ return;
+
+fail:
+ wpabuf_free(out);
+ tls_connection_deinit(radius->tls_ctx, conn);
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_conn = NULL;
+ else
+ radius->auth_tls_conn = NULL;
+ radius_client_close_tcp(radius, sock, msg_type);
+}
+
+#endif /* CONFIG_RADIUS_TLS */
+
+
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_client_data *radius = eloop_ctx;
@@ -828,12 +1103,28 @@
struct os_reltime now;
struct hostapd_radius_server *rconf;
int invalid_authenticator = 0;
+#ifdef CONFIG_RADIUS_TLS
+ struct tls_connection *conn = NULL;
+ bool tls, tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT) {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+ tls = radius->acct_tls;
+ tls_ready = radius->acct_tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
handlers = radius->acct_handlers;
num_handlers = radius->num_acct_handlers;
rconf = conf->acct_server;
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+ tls = radius->auth_tls;
+ tls_ready = radius->auth_tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
handlers = radius->auth_handlers;
num_handlers = radius->num_auth_handlers;
rconf = conf->auth_server;
@@ -849,6 +1140,52 @@
wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno));
return;
}
+#ifdef CONFIG_RADIUS_TLS
+ if (tls && len == 0) {
+ wpa_printf(MSG_DEBUG, "RADIUS: No TCP data available");
+ goto close_tcp;
+ }
+
+ if (tls && !tls_ready) {
+ radius_client_process_tls_handshake(radius, sock, msg_type,
+ buf, len);
+ return;
+ }
+
+ if (conn) {
+ struct wpabuf *out, *in;
+
+ in = wpabuf_alloc_copy(buf, len);
+ if (!in)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Process %d bytes of encrypted TLS data",
+ len);
+ out = tls_connection_decrypt(radius->tls_ctx, conn, in);
+ wpabuf_free(in);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to decrypt TLS data");
+ goto close_tcp;
+ }
+ if (wpabuf_len(out) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Full message not yet received - continue waiting for additional TLS data");
+ wpabuf_free(out);
+ return;
+ }
+ if (wpabuf_len(out) > RADIUS_MAX_MSG_LEN) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Too long RADIUS message from TLS: %zu",
+ wpabuf_len(out));
+ wpabuf_free(out);
+ goto close_tcp;
+ }
+ os_memcpy(buf, wpabuf_head(out), wpabuf_len(out));
+ len = wpabuf_len(out);
+ wpabuf_free(out);
+ }
+#endif /* CONFIG_RADIUS_TLS */
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
@@ -964,9 +1301,121 @@
fail:
radius_msg_free(msg);
+ return;
+
+#ifdef CONFIG_RADIUS_TLS
+close_tcp:
+ radius_client_close_tcp(radius, sock, msg_type);
+#endif /* CONFIG_RADIUS_TLS */
}
+#ifdef CONFIG_RADIUS_TLS
+static void radius_client_write_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct radius_client_data *radius = eloop_ctx;
+ RadiusType msg_type = (uintptr_t) sock_ctx;
+ struct tls_connection *conn = NULL;
+ struct wpabuf *in, *out = NULL, *appl;
+ int res = -1;
+ struct tls_connection_params params;
+ struct hostapd_radius_server *server;
+
+ wpa_printf(MSG_DEBUG, "RADIUS: TCP connection established - start TLS handshake (sock=%d)",
+ sock);
+
+ if (msg_type == RADIUS_ACCT) {
+ eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
+ eloop_register_read_sock(sock, radius_client_receive, radius,
+ (void *) RADIUS_ACCT);
+ if (radius->acct_tls_conn) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Deinit previously used TLS connection");
+ tls_connection_deinit(radius->tls_ctx,
+ radius->acct_tls_conn);
+ radius->acct_tls_conn = NULL;
+ }
+ server = radius->conf->acct_server;
+ } else {
+ eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
+ eloop_register_read_sock(sock, radius_client_receive, radius,
+ (void *) RADIUS_AUTH);
+ if (radius->auth_tls_conn) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Deinit previously used TLS connection");
+ tls_connection_deinit(radius->tls_ctx,
+ radius->auth_tls_conn);
+ radius->auth_tls_conn = NULL;
+ }
+ server = radius->conf->auth_server;
+ }
+
+ if (!server)
+ goto fail;
+
+ conn = tls_connection_init(radius->tls_ctx);
+ if (!conn) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to initiate TLS connection");
+ goto fail;
+ }
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.ca_cert = server->ca_cert;
+ params.client_cert = server->client_cert;
+ params.private_key = server->private_key;
+ params.private_key_passwd = server->private_key_passwd;
+ params.flags = TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1;
+ if (tls_connection_set_params(radius->tls_ctx, conn, ¶ms)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to set TLS connection parameters");
+ goto fail;
+ }
+
+ in = NULL;
+ appl = NULL;
+ out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
+ if (!out) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Could not generate TLS handshake data");
+ goto fail;
+ }
+
+ if (tls_connection_get_failed(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
+ wpabuf_len(out));
+ res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res != wpabuf_len(out)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
+ res);
+ goto fail;
+ }
+ wpabuf_free(out);
+
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_conn = conn;
+ else
+ radius->auth_tls_conn = conn;
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "RADIUS: Failed to perform TLS handshake");
+ tls_connection_deinit(radius->tls_ctx, conn);
+ wpabuf_free(out);
+ radius_client_close_tcp(radius, sock, msg_type);
+}
+#endif /* CONFIG_RADIUS_TLS */
+
+
/**
* radius_client_get_id - Get an identifier for a new RADIUS message
* @radius: RADIUS client context from radius_client_init()
@@ -1071,7 +1520,7 @@
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
- int sock, int sock6, int auth)
+ int auth)
{
struct sockaddr_in serv, claddr;
#ifdef CONFIG_IPV6
@@ -1083,9 +1532,17 @@
int sel_sock;
struct radius_msg_list *entry;
struct hostapd_radius_servers *conf = radius->conf;
- struct sockaddr_in disconnect_addr = {
- .sin_family = AF_UNSPEC,
- };
+ int type = SOCK_DGRAM;
+ bool tls = nserv->tls;
+
+ if (tls) {
+#ifdef CONFIG_RADIUS_TLS
+ type = SOCK_STREAM;
+#else /* CONFIG_RADIUS_TLS */
+ wpa_printf(MSG_ERROR, "RADIUS: TLS not supported");
+ return -1;
+#endif /* CONFIG_RADIUS_TLS */
+ }
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -1144,7 +1601,9 @@
serv.sin_port = htons(nserv->port);
addr = (struct sockaddr *) &serv;
addrlen = sizeof(serv);
- sel_sock = sock;
+ sel_sock = socket(PF_INET, type, 0);
+ if (sel_sock >= 0)
+ radius_client_disable_pmtu_discovery(sel_sock);
break;
#ifdef CONFIG_IPV6
case AF_INET6:
@@ -1155,7 +1614,7 @@
serv6.sin6_port = htons(nserv->port);
addr = (struct sockaddr *) &serv6;
addrlen = sizeof(serv6);
- sel_sock = sock6;
+ sel_sock = socket(PF_INET6, type, 0);
break;
#endif /* CONFIG_IPV6 */
default:
@@ -1164,15 +1623,19 @@
if (sel_sock < 0) {
wpa_printf(MSG_INFO,
- "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
- nserv->addr.af, sock, sock6, auth);
+ "RADIUS: Failed to open server socket (af=%d auth=%d)",
+ nserv->addr.af, auth);
return -1;
}
- /* Force a reconnect by disconnecting the socket first */
- if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
- sizeof(disconnect_addr)) < 0)
- wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+#ifdef CONFIG_RADIUS_TLS
+ if (tls && fcntl(sel_sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "RADIUS: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ close(sel_sock);
+ return -1;
+ }
+#endif /* CONFIG_RADIUS_TLS */
#ifdef __linux__
if (conf->force_client_dev && conf->force_client_dev[0]) {
@@ -1214,19 +1677,29 @@
break;
#endif /* CONFIG_IPV6 */
default:
+ close(sel_sock);
return -1;
}
if (bind(sel_sock, cl_addr, claddrlen) < 0) {
wpa_printf(MSG_INFO, "bind[radius]: %s",
strerror(errno));
- return -1;
+ close(sel_sock);
+ return -2;
}
}
if (connect(sel_sock, addr, addrlen) < 0) {
- wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
- return -1;
+ if (nserv->tls && errno == EINPROGRESS) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TCP connection establishment in progress (sock %d)",
+ sel_sock);
+ } else {
+ wpa_printf(MSG_INFO, "connect[radius]: %s",
+ strerror(errno));
+ close(sel_sock);
+ return -2;
+ }
}
#ifndef CONFIG_NATIVE_WINDOWS
@@ -1256,10 +1729,34 @@
}
#endif /* CONFIG_NATIVE_WINDOWS */
- if (auth)
+ if (auth) {
+ radius_close_auth_socket(radius);
radius->auth_sock = sel_sock;
- else
+ } else {
+ radius_close_acct_socket(radius);
radius->acct_sock = sel_sock;
+ }
+
+ if (!tls)
+ eloop_register_read_sock(sel_sock, radius_client_receive,
+ radius,
+ auth ? (void *) RADIUS_AUTH :
+ (void *) RADIUS_ACCT);
+#ifdef CONFIG_RADIUS_TLS
+ if (tls)
+ eloop_register_sock(sel_sock, EVENT_TYPE_WRITE,
+ radius_client_write_ready, radius,
+ auth ? (void *) RADIUS_AUTH :
+ (void *) RADIUS_ACCT);
+#endif /* CONFIG_RADIUS_TLS */
+
+ if (auth) {
+ radius->auth_tls = nserv->tls;
+ radius->auth_tls_ready = false;
+ } else {
+ radius->acct_tls = nserv->tls;
+ radius->acct_tls_ready = false;
+ }
return 0;
}
@@ -1276,12 +1773,10 @@
oserv = conf->auth_server;
conf->auth_server = conf->auth_servers;
if (radius_change_server(radius, conf->auth_server, oserv,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1) < 0) {
+ 1) < 0) {
conf->auth_server = oserv;
radius_change_server(radius, oserv, conf->auth_server,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1);
+ 1);
}
}
@@ -1290,12 +1785,10 @@
oserv = conf->acct_server;
conf->acct_server = conf->acct_servers;
if (radius_change_server(radius, conf->acct_server, oserv,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0) < 0) {
+ 0) < 0) {
conf->acct_server = oserv;
radius_change_server(radius, oserv, conf->acct_server,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0);
+ 0);
}
}
@@ -1306,172 +1799,29 @@
}
-static int radius_client_disable_pmtu_discovery(int s)
-{
- int r = -1;
-#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
- /* Turn off Path MTU discovery on IPv4/UDP sockets. */
- int action = IP_PMTUDISC_DONT;
- r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
- sizeof(action));
- if (r == -1)
- wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
- strerror(errno));
-#endif
- return r;
-}
-
-
-static void radius_close_auth_sockets(struct radius_client_data *radius)
-{
- radius->auth_sock = -1;
-
- if (radius->auth_serv_sock >= 0) {
- eloop_unregister_read_sock(radius->auth_serv_sock);
- close(radius->auth_serv_sock);
- radius->auth_serv_sock = -1;
- }
-#ifdef CONFIG_IPV6
- if (radius->auth_serv_sock6 >= 0) {
- eloop_unregister_read_sock(radius->auth_serv_sock6);
- close(radius->auth_serv_sock6);
- radius->auth_serv_sock6 = -1;
- }
-#endif /* CONFIG_IPV6 */
-}
-
-
-static void radius_close_acct_sockets(struct radius_client_data *radius)
-{
- radius->acct_sock = -1;
-
- if (radius->acct_serv_sock >= 0) {
- eloop_unregister_read_sock(radius->acct_serv_sock);
- close(radius->acct_serv_sock);
- radius->acct_serv_sock = -1;
- }
-#ifdef CONFIG_IPV6
- if (radius->acct_serv_sock6 >= 0) {
- eloop_unregister_read_sock(radius->acct_serv_sock6);
- close(radius->acct_serv_sock6);
- radius->acct_serv_sock6 = -1;
- }
-#endif /* CONFIG_IPV6 */
-}
-
-
static int radius_client_init_auth(struct radius_client_data *radius)
{
- struct hostapd_radius_servers *conf = radius->conf;
- int ok = 0;
-
- radius_close_auth_sockets(radius);
-
- radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (radius->auth_serv_sock < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- else {
- radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
- ok++;
- }
-
-#ifdef CONFIG_IPV6
- radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
- if (radius->auth_serv_sock6 < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
- strerror(errno));
- else
- ok++;
-#endif /* CONFIG_IPV6 */
-
- if (ok == 0)
- return -1;
-
- radius_change_server(radius, conf->auth_server, NULL,
- radius->auth_serv_sock, radius->auth_serv_sock6,
- 1);
-
- if (radius->auth_serv_sock >= 0 &&
- eloop_register_read_sock(radius->auth_serv_sock,
- radius_client_receive, radius,
- (void *) RADIUS_AUTH)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
- radius_close_auth_sockets(radius);
- return -1;
- }
-
-#ifdef CONFIG_IPV6
- if (radius->auth_serv_sock6 >= 0 &&
- eloop_register_read_sock(radius->auth_serv_sock6,
- radius_client_receive, radius,
- (void *) RADIUS_AUTH)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
- radius_close_auth_sockets(radius);
- return -1;
- }
-#endif /* CONFIG_IPV6 */
-
- return 0;
+ radius_close_auth_socket(radius);
+ return radius_change_server(radius, radius->conf->auth_server, NULL, 1);
}
static int radius_client_init_acct(struct radius_client_data *radius)
{
- struct hostapd_radius_servers *conf = radius->conf;
- int ok = 0;
-
- radius_close_acct_sockets(radius);
-
- radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (radius->acct_serv_sock < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- else {
- radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
- ok++;
- }
-
-#ifdef CONFIG_IPV6
- radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
- if (radius->acct_serv_sock6 < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
- strerror(errno));
- else
- ok++;
-#endif /* CONFIG_IPV6 */
-
- if (ok == 0)
- return -1;
-
- radius_change_server(radius, conf->acct_server, NULL,
- radius->acct_serv_sock, radius->acct_serv_sock6,
- 0);
-
- if (radius->acct_serv_sock >= 0 &&
- eloop_register_read_sock(radius->acct_serv_sock,
- radius_client_receive, radius,
- (void *) RADIUS_ACCT)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
- radius_close_acct_sockets(radius);
- return -1;
- }
-
-#ifdef CONFIG_IPV6
- if (radius->acct_serv_sock6 >= 0 &&
- eloop_register_read_sock(radius->acct_serv_sock6,
- radius_client_receive, radius,
- (void *) RADIUS_ACCT)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
- radius_close_acct_sockets(radius);
- return -1;
- }
-#endif /* CONFIG_IPV6 */
-
- return 0;
+ radius_close_acct_socket(radius);
+ return radius_change_server(radius, radius->conf->acct_server, NULL, 0);
}
+#ifdef CONFIG_RADIUS_TLS
+static void radius_tls_event_cb(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ wpa_printf(MSG_DEBUG, "RADIUS: TLS event %d", ev);
+}
+#endif /* CONFIG_RADIUS_TLS */
+
+
/**
* radius_client_init - Initialize RADIUS client
* @ctx: Callback context to be used in hostapd_logger() calls
@@ -1493,16 +1843,14 @@
radius->ctx = ctx;
radius->conf = conf;
- radius->auth_serv_sock = radius->acct_serv_sock =
- radius->auth_serv_sock6 = radius->acct_serv_sock6 =
- radius->auth_sock = radius->acct_sock = -1;
+ radius->auth_sock = radius->acct_sock = -1;
- if (conf->auth_server && radius_client_init_auth(radius)) {
+ if (conf->auth_server && radius_client_init_auth(radius) == -1) {
radius_client_deinit(radius);
return NULL;
}
- if (conf->acct_server && radius_client_init_acct(radius)) {
+ if (conf->acct_server && radius_client_init_acct(radius) == -1) {
radius_client_deinit(radius);
return NULL;
}
@@ -1512,6 +1860,22 @@
radius_retry_primary_timer, radius,
NULL);
+#ifdef CONFIG_RADIUS_TLS
+ if ((conf->auth_server && conf->auth_server->tls) ||
+ (conf->acct_server && conf->acct_server->tls)) {
+ struct tls_config tls_conf;
+
+ os_memset(&tls_conf, 0, sizeof(tls_conf));
+ tls_conf.event_cb = radius_tls_event_cb;
+ radius->tls_ctx = tls_init(&tls_conf);
+ if (!radius->tls_ctx) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
+
return radius;
}
@@ -1525,14 +1889,21 @@
if (!radius)
return;
- radius_close_auth_sockets(radius);
- radius_close_acct_sockets(radius);
+ radius_close_auth_socket(radius);
+ radius_close_acct_socket(radius);
eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
radius_client_flush(radius, 0);
os_free(radius->auth_handlers);
os_free(radius->acct_handlers);
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->tls_ctx) {
+ tls_connection_deinit(radius->tls_ctx, radius->auth_tls_conn);
+ tls_connection_deinit(radius->tls_ctx, radius->acct_tls_conn);
+ tls_deinit(radius->tls_ctx);
+ }
+#endif /* CONFIG_RADIUS_TLS */
os_free(radius);
}
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 687cd81..db40637 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -1,6 +1,6 @@
/*
* RADIUS client
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -36,6 +36,11 @@
int port;
/**
+ * tls - Whether to use RADIUS/TLS instead of RADIUS/UDP
+ */
+ bool tls;
+
+ /**
* shared_secret - Shared secret for authenticating RADIUS messages
*/
u8 *shared_secret;
@@ -45,6 +50,26 @@
*/
size_t shared_secret_len;
+ /**
+ * ca_cert - Path to trusted CA certificate(s) for RADIUS/TLS
+ */
+ char *ca_cert;
+
+ /**
+ * client_cert - Path to client certificate for RADIUS/TLS
+ */
+ char *client_cert;
+
+ /**
+ * private_key - Path to clienbt private key for RADIUS/TLS
+ */
+ char *private_key;
+
+ /**
+ * private_key_passwd - Password for the private key for RADIUS/TLS
+ */
+ char *private_key_passwd;
+
/* Dynamic (not from configuration file) MIB data */
/**
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index e243756..a402cb6 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -857,4 +857,99 @@
}
}
+#else /* IEEE8021X_EAPOL */
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason),
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
+ void *ctx, struct wpa_sm *sm)
+{
+ return (void *) -1;
+}
+
+
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid, const void *network_ctx, int akmp)
+{
+ return NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ return NULL;
+}
+
+
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+ return NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ return NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *pmkid, const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
+ const u8 *cache_id)
+{
+ return NULL;
+}
+
+
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+
+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, bool associated)
+{
+ return -1;
+}
+
+
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *pmk, size_t pmk_len, bool external_only)
+{
+}
+
+
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+}
+
+
+void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
#endif /* IEEE8021X_EAPOL */
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6ba48f7..b1203ad 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -64,8 +64,6 @@
PMKSA_EXPIRE,
};
-#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
-
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason),
@@ -105,95 +103,4 @@
struct rsn_pmksa_cache_entry *entry);
void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
-#else /* IEEE8021X_EAPOL */
-
-static inline struct rsn_pmksa_cache *
-pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx, enum pmksa_free_reason reason),
- bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx),
- void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx),
- void *ctx, struct wpa_sm *sm)
-{
- return (void *) -1;
-}
-
-static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
-{
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
- const u8 *pmkid, const void *network_ctx, int akmp)
-{
- return NULL;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get_current(struct wpa_sm *sm)
-{
- return NULL;
-}
-
-static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
- size_t len)
-{
- return -1;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
-{
- return NULL;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
- struct rsn_pmksa_cache_entry *entry)
-{
- return NULL;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
- const u8 *pmkid, const u8 *kck, size_t kck_len,
- const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
- const u8 *cache_id)
-{
- return NULL;
-}
-
-static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
-{
-}
-
-static inline 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, bool associated)
-{
- return -1;
-}
-
-static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
- void *network_ctx,
- const u8 *pmk, size_t pmk_len,
- bool external_only)
-{
-}
-
-static inline void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
- struct rsn_pmksa_cache_entry *entry)
-{
-}
-
-static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-}
-
-#endif /* IEEE8021X_EAPOL */
-
#endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 8a75091..65960b7 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -161,6 +161,8 @@
int chan_switch_enabled;
int mld_link_id;
+ bool disc_resp_rcvd;
+ bool setup_req_rcvd;
};
@@ -1569,9 +1571,8 @@
} else {
int i;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if ((sm->mlo.valid_links & BIT(i)) &&
- ether_addr_equal(lnkid->bssid,
+ for_each_link(sm->mlo.valid_links, i) {
+ if (ether_addr_equal(lnkid->bssid,
sm->mlo.links[i].bssid)) {
*link_id = i;
break;
@@ -2868,6 +2869,12 @@
return 0;
}
+ if (sm->mlo.valid_links && !peer->disc_resp_rcvd) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: MLO STA connection - defer the setup request since Discovery Resp not yet received");
+ peer->setup_req_rcvd = true;
+ return 0;
+ }
peer->initiator = 1;
/* add the peer to the driver as a "setup in progress" peer */
@@ -3236,6 +3243,13 @@
wpa_printf(MSG_DEBUG, "TDLS: Link identifier BSS: " MACSTR
" , link id: %u", MAC2STR(lnkid->bssid), link_id);
+ peer->disc_resp_rcvd = true;
+ if (peer->setup_req_rcvd) {
+ peer->setup_req_rcvd = false;
+ wpa_printf(MSG_DEBUG, "TDLS: Process the deferred TDLS start");
+ return wpa_tdls_start(sm, addr);
+ }
+
return 0;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3eaa015..f5e24f2 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -801,8 +801,8 @@
int i;
unsigned int num_links = 0;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (sm->mlo.assoc_link_id != i && (sm->mlo.req_links & BIT(i)))
+ for_each_link(sm->mlo.req_links, i) {
+ if (sm->mlo.assoc_link_id != i)
num_links++;
}
@@ -815,8 +815,8 @@
int i;
u8 hdr[1 + ETH_ALEN];
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (sm->mlo.assoc_link_id == i || !(sm->mlo.req_links & BIT(i)))
+ for_each_link(sm->mlo.req_links, i) {
+ if (sm->mlo.assoc_link_id == i)
continue;
wpa_printf(MSG_DEBUG,
@@ -1551,10 +1551,7 @@
{
u8 i;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
if (!ie->mlo_gtk[i]) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"MLO RSN: GTK not found for link ID %u", i);
@@ -1903,10 +1900,7 @@
sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
return 0;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
if (_mlo_ieee80211w_set_keys(sm, i, ie))
return -1;
}
@@ -2932,10 +2926,7 @@
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"MLO RSN: Failed to configure MLO IGTK");
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
/*
* AP may send group keys for subset of the all links during
* rekey
diff --git a/src/utils/common.h b/src/utils/common.h
index 079f90e..14c90c9 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -600,6 +600,18 @@
u8 rssi_to_rcpi(int rssi);
char * get_param(const char *cmd, const char *param);
+#define for_each_link(__links, __i) \
+ for ((__i) = 0; (__i) < MAX_NUM_MLD_LINKS; (__i)++) \
+ if ((__links) & BIT(__i))
+
+/* Iterate all links, or, if no link is defined, iterate given index */
+#define for_each_link_default(_links, _i, _def_idx) \
+ for ((_i) = (_links) ? 0 : (_def_idx); \
+ (_i) < MAX_NUM_MLD_LINKS || \
+ (!(_links) && (_i) == (_def_idx)); \
+ (_i)++) \
+ if (!(_links) || (_links) & BIT(_i))
+
void forced_memzero(void *ptr, size_t len);
/*
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 7cfebfa..fd5bd93 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -146,6 +146,7 @@
}
data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta;
+ data->multi_ap_profile = cfg->multi_ap_profile;
return data;
}
diff --git a/src/wps/wps.h b/src/wps/wps.h
index fed3e28..b99ae94 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -195,6 +195,11 @@
* enrollee
*/
int multi_ap_backhaul_sta;
+
+ /*
+ * multi_ap_profile - Get the Multi-AP Profile
+ */
+ int multi_ap_profile;
};
struct wps_data * wps_init(const struct wps_config *cfg);
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 2cf22d4..5486e2a 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -127,6 +127,7 @@
struct wps_nfc_pw_token *nfc_pw_token;
int multi_ap_backhaul_sta;
+ int multi_ap_profile;
};
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index c0835ae..afcc04b 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -315,6 +315,7 @@
"src/common/sae.c",
"src/common/sae_pk.c",
"src/common/wpa_common.c",
+ "src/common/ptksa_cache.c",
"src/crypto/aes-ctr.c",
"src/crypto/aes-encblock.c",
"src/crypto/aes-siv.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index baf3ad1..b667d82 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -139,6 +139,8 @@
OBJS += src/utils/bitfield.c
OBJS += src/utils/ip_addr.c
OBJS += src/utils/crc32.c
+OBJS += src/common/ptksa_cache.c
+OBJS += src/rsn_supp/pmksa_cache.c
OBJS += twt.c
OBJS_p = wpa_passphrase.c
OBJS_p += src/utils/common.c
@@ -379,7 +381,6 @@
ifndef CONFIG_NO_WPA
OBJS += src/rsn_supp/wpa.c
OBJS += src/rsn_supp/preauth.c
-OBJS += src/rsn_supp/pmksa_cache.c
OBJS += src/rsn_supp/wpa_ie.c
OBJS += src/common/wpa_common.c
NEED_AES=y
@@ -432,8 +433,8 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
OBJS += src/pasn/pasn_initiator.c
+OBJS += src/pasn/pasn_common.c
OBJS += pasn_supplicant.c
endif
@@ -1846,8 +1847,9 @@
PASNOBJS += src/common/ptksa_cache.c
-ifndef CONFIG_NO_WPA
PASNOBJS += src/rsn_supp/pmksa_cache.c
+
+ifndef CONFIG_NO_WPA
PASNOBJS += src/rsn_supp/wpa_ie.c
endif
@@ -1933,6 +1935,7 @@
PASNOBJS += src/pasn/pasn_initiator.c
PASNOBJS += src/pasn/pasn_responder.c
+PASNOBJS += src/pasn/pasn_common.c
########################
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 93fc338..8bec178 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -112,6 +112,8 @@
OBJS += ../src/utils/bitfield.o
OBJS += ../src/utils/ip_addr.o
OBJS += ../src/utils/crc32.o
+OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
OBJS += twt.o
OBJS_p = wpa_passphrase.o
OBJS_p += ../src/utils/common.o
@@ -374,7 +376,6 @@
ifndef CONFIG_NO_WPA
OBJS += ../src/rsn_supp/wpa.o
OBJS += ../src/rsn_supp/preauth.o
-OBJS += ../src/rsn_supp/pmksa_cache.o
OBJS += ../src/rsn_supp/wpa_ie.o
OBJS += ../src/common/wpa_common.o
NEED_AES=y
@@ -435,8 +436,8 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
OBJS += ../src/pasn/pasn_initiator.o
+OBJS += ../src/pasn/pasn_common.o
OBJS += pasn_supplicant.o
endif
@@ -1187,6 +1188,10 @@
CFLAGS += -DCONFIG_TLSV12
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+endif
+
ifeq ($(CONFIG_TLS), wolfssl)
ifdef TLS_FUNCS
CFLAGS += -DWOLFSSL_DER_LOAD
@@ -1925,6 +1930,9 @@
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
OBJS_t += ../src/radius/radius_client.o
OBJS_t += ../src/radius/radius.o
+ifdef CONFIG_RADIUS_TLS
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
@@ -2219,9 +2227,9 @@
endif
LIBPASNSO += ../src/common/ptksa_cache.c
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
ifndef CONFIG_NO_WPA
-LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
LIBPASNSO += ../src/rsn_supp/wpa_ie.c
endif
@@ -2316,6 +2324,7 @@
LIBPASNSO += ../src/pasn/pasn_initiator.c
LIBPASNSO += ../src/pasn/pasn_responder.c
+LIBPASNSO += ../src/pasn/pasn_common.c
libpasn.so: $(LIBPASNSO)
@$(E) " CC $@ ($^)"
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 1589dd2..ae1943f 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -413,7 +413,7 @@
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, NULL);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
@@ -472,7 +472,7 @@
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, NULL);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 000914a..69a0e5e 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -440,9 +440,14 @@
}
}
- if (wpa_s->p2p_go_no_pri_sec_switch) {
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group && wpa_s->p2p_go_no_pri_sec_switch) {
conf->no_pri_sec_switch = 1;
- } else if (conf->secondary_channel) {
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
+ if (conf->secondary_channel) {
struct wpa_supplicant *iface;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
@@ -868,7 +873,8 @@
static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
- int authorized, const u8 *p2p_dev_addr, const u8 *ip)
+ int authorized, const u8 *p2p_dev_addr,
+ const u8 *ip)
{
wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr, ip);
}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 22b694c..2890353 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -400,6 +400,11 @@
if (bss == wpa_s->ml_connect_probe_bss)
return 1;
+#ifdef CONFIG_WNM
+ if (bss == wpa_s->wnm_target_bss)
+ return 1;
+#endif /* CONFIG_WNM */
+
if (wpa_s->current_bss &&
(bss->ssid_len != wpa_s->current_bss->ssid_len ||
os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
@@ -414,10 +419,7 @@
if (!wpa_s->valid_links)
return 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
return 1;
}
@@ -1513,9 +1515,6 @@
for (i = 0; i < count; i++) {
u8 bss_params;
- if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
- return;
-
if (end - pos < ap_info->tbtt_info_len)
break;
@@ -1523,6 +1522,8 @@
mld_params = pos + mld_params_offset;
link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ return;
if (*mld_params != mbssid_idx) {
wpa_printf(MSG_DEBUG,
@@ -1546,13 +1547,12 @@
wpa_s, neigh_bss->bssid)) {
struct mld_link *l;
- l = &bss->mld_links[bss->n_mld_links];
- l->link_id = link_id;
+ bss->valid_links |= BIT(link_id);
+ l = &bss->mld_links[link_id];
os_memcpy(l->bssid, pos + 1, ETH_ALEN);
l->freq = neigh_bss->freq;
l->disabled = mld_params[2] &
RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
- bss->n_mld_links++;
}
}
@@ -1678,15 +1678,15 @@
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;
+
+ bss->mld_link_id = link_id;
+ seen = bss->valid_links = BIT(link_id);
+
+ l = &bss->mld_links[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
@@ -1736,13 +1736,12 @@
}
}
- wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
- bss->n_mld_links, missing);
+ wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
+ bss->valid_links, missing);
- for (i = 0; i < bss->n_mld_links; i++) {
+ for_each_link(bss->valid_links, i) {
wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
- bss->mld_links[i].link_id,
- MAC2STR(bss->mld_links[i].bssid));
+ i, MAC2STR(bss->mld_links[i].bssid));
}
if (missing_links)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index c06c20a..cc04963 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -126,11 +126,12 @@
size_t beacon_ie_len;
/** MLD address of the AP */
u8 mld_addr[ETH_ALEN];
+ /** Link ID of this affiliated AP of the AP MLD */
+ u8 mld_link_id;
/** An array of MLD links */
- u8 n_mld_links;
+ u16 valid_links;
struct mld_link {
- u8 link_id;
u8 bssid[ETH_ALEN];
int freq;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8c0db68..c949bab 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -197,9 +197,10 @@
#endif /* NO_CONFIG_WRITE */
-static int wpa_config_parse_int(const struct parse_data *data,
- struct wpa_ssid *ssid,
- int line, const char *value)
+static int wpa_config_parse_int_impl(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value,
+ bool check_range)
{
int val, *dst;
char *end;
@@ -212,31 +213,46 @@
return -1;
}
+ if (check_range && val < (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, val,
+ (long) data->param3);
+ return -1;
+ }
+
+ if (check_range && val > (long) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, val,
+ (long) data->param4);
+ return -1;
+ }
+
if (*dst == val)
return 1;
+
*dst = val;
wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
- if (data->param3 && *dst < (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
- if (data->param4 && *dst > (long) data->param4) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param4);
- *dst = (long) data->param4;
- return -1;
- }
-
return 0;
}
+static int wpa_config_parse_int(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ return wpa_config_parse_int_impl(data, ssid, line, value, false);
+}
+
+
+static int wpa_config_parse_int_range(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ return wpa_config_parse_int_impl(data, ssid, line, value, true);
+}
+
+
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_int(const struct parse_data *data,
struct wpa_ssid *ssid)
@@ -2457,7 +2473,14 @@
#define INTe(f, m) _INTe(f, m), NULL, NULL, 0
/* INT_RANGE: Define an integer variable with allowed value range */
-#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+#ifdef NO_CONFIG_WRITE
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, OFFSET(f), \
+ (void *) 0, (void *) (min), (void *) (max), 0
+#else /* NO_CONFIG_WRITE */
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, \
+ wpa_config_write_int, OFFSET(f), \
+ (void *) 0, (void *) (min), (void *) (max), 0
+#endif /* NO_CONFIG_WRITE */
/* FUNC: Define a configuration variable that uses a custom function for
* parsing and writing the value. */
@@ -2652,7 +2675,7 @@
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
{ INT_RANGE(disable_ht, 0, 1) },
- { INT_RANGE(disable_ht40, -1, 1) },
+ { INT_RANGE(disable_ht40, 0, 1) },
{ INT_RANGE(disable_sgi, 0, 1) },
{ INT_RANGE(disable_ldpc, 0, 1) },
{ INT_RANGE(ht40_intolerant, 0, 1) },
@@ -2725,6 +2748,8 @@
{ INT_RANGE(owe_ptk_workaround, 0, 1) },
{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
+ { INT_RANGE(multi_ap_profile, MULTI_AP_PROFILE_1,
+ MULTI_AP_PROFILE_MAX) },
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
@@ -4743,9 +4768,10 @@
};
-static int wpa_global_config_parse_int(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
+static int
+wpa_global_config_parse_int_impl(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos, bool check_range)
{
int val, *dst;
char *end;
@@ -4758,31 +4784,47 @@
line, pos);
return -1;
}
+
+ if (check_range && val < (long) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, val,
+ (long) data->param2);
+ return -1;
+ }
+
+ if (check_range && val > (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, val,
+ (long) data->param3);
+ return -1;
+ }
+
same = *dst == val;
*dst = val;
wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
- if (data->param2 && *dst < (long) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param2);
- *dst = (long) data->param2;
- return -1;
- }
-
- if (data->param3 && *dst > (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
return same;
}
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ return wpa_global_config_parse_int_impl(data, config, line, pos, false);
+}
+
+
+static int
+wpa_global_config_parse_int_range(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ return wpa_global_config_parse_int_impl(data, config, line, pos, true);
+}
+
+
static int wpa_global_config_parse_str(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
@@ -5346,7 +5388,8 @@
#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
#define INT(f) _INT(f), NULL, NULL
-#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \
+ wpa_config_get_int, OFFSET(f), (void *) min, (void *) max
#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
#define STR(f) _STR(f), NULL, NULL
#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 71a64b1..414ed22 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -850,6 +850,7 @@
INT(owe_ptk_workaround);
INT(multi_ap_backhaul_sta);
INT(ft_eap_pmksa_caching);
+ INT(multi_ap_profile);
INT(beacon_prot);
INT(transition_disable);
INT(sae_pk);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 785acc3..2043a80 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1187,6 +1187,11 @@
int ft_eap_pmksa_caching;
/**
+ * multi_ap_profile - Supported Multi-AP profile
+ */
+ int multi_ap_profile;
+
+ /**
* beacon_prot - Whether Beacon protection is enabled
*
* This depends on management frame protection (ieee80211w) being
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 500f4d1..d0fda4c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -902,9 +902,20 @@
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
- wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+ int val = atoi(value);
+
+ if (val < MBO_CELL_CAPA_AVAILABLE ||
+ val > MBO_CELL_CAPA_NOT_SUPPORTED)
+ return -1;
+
+ wpas_mbo_update_cell_capa(wpa_s, val);
} else if (os_strcasecmp(cmd, "oce") == 0) {
- wpa_s->conf->oce = atoi(value);
+ int val = atoi(value);
+
+ if (val < 0 || val > 3)
+ return -1;
+
+ wpa_s->conf->oce = val;
if (wpa_s->conf->oce) {
if ((wpa_s->conf->oce & OCE_STA) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
@@ -8850,6 +8861,8 @@
wpa_s->next_ssid = NULL;
+ wnm_btm_reset(wpa_s);
+
#ifdef CONFIG_INTERWORKING
#ifdef CONFIG_HS20
hs20_cancel_fetch_osu(wpa_s);
@@ -11995,10 +12008,7 @@
pos = buf;
end = buf + buflen;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_si.valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_si.valid_links, i) {
ret = os_snprintf(pos, end - pos,
"LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
@@ -12070,10 +12080,7 @@
pos = buf;
end = buf + buflen;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
"ap_link_addr=" MACSTR
"\nsta_link_addr=" MACSTR "\n",
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index fdf7b12..27003eb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -706,6 +706,59 @@
}
+#define UINT16_ARRAY_CHUNK_SIZE 18
+#define UINT16_ARRAY_ITEM_SIZE (sizeof(dbus_uint16_t))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_uint16_array(
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+ dbus_uint32_t count = 0;
+ dbus_uint16_t *buffer, *nbuffer;
+
+ entry->uint16array_value = NULL;
+ entry->array_type = DBUS_TYPE_UINT16;
+
+ buffer = os_calloc(UINT16_ARRAY_CHUNK_SIZE, UINT16_ARRAY_ITEM_SIZE);
+ if (!buffer)
+ return FALSE;
+
+ entry->array_len = 0;
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT16) {
+ dbus_uint16_t value;
+
+ if ((count % UINT16_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+ nbuffer = os_realloc_array(
+ buffer, count + UINT16_ARRAY_CHUNK_SIZE,
+ UINT16_ARRAY_ITEM_SIZE);
+ if (!nbuffer) {
+ os_free(buffer);
+ wpa_printf(MSG_ERROR,
+ "dbus: %s out of memory trying to retrieve the uint16 array",
+ __func__);
+ return FALSE;
+ }
+ buffer = nbuffer;
+ }
+
+ dbus_message_iter_get_basic(iter, &value);
+ buffer[count] = value;
+ entry->array_len = ++count;
+ dbus_message_iter_next(iter);
+ }
+ entry->uint16array_value = buffer;
+ wpa_hexdump_key(MSG_MSGDUMP, "dbus: uint16 array contents",
+ entry->bytearray_value, entry->array_len);
+
+ /* Zero-length arrays are valid. */
+ if (entry->array_len == 0) {
+ os_free(entry->uint16array_value);
+ entry->uint16array_value = NULL;
+ }
+
+ return TRUE;
+}
+
+
#define STR_ARRAY_CHUNK_SIZE 8
#define STR_ARRAY_ITEM_SIZE (sizeof(char *))
@@ -873,6 +926,10 @@
success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
entry);
break;
+ case DBUS_TYPE_UINT16:
+ success = _wpa_dbus_dict_entry_get_uint16_array(&iter_array,
+ entry);
+ break;
case DBUS_TYPE_STRING:
success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
array_type,
@@ -1081,6 +1138,9 @@
case DBUS_TYPE_BYTE:
os_free(entry->bytearray_value);
break;
+ case DBUS_TYPE_UINT16:
+ os_free(entry->uint16array_value);
+ break;
case DBUS_TYPE_STRING:
if (!entry->strarray_value)
break;
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index cc9e26f..1d33689 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -139,6 +139,7 @@
dbus_uint64_t uint64_value;
double double_value;
char *bytearray_value;
+ dbus_uint16_t *uint16array_value;
char **strarray_value;
struct wpabuf **binarray_value;
};
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 00b38ed..8bd6a9a 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1023,6 +1023,40 @@
dbus_message_unref(msg);
}
+
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *result)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char addr[WPAS_DBUS_OBJECT_PATH_MAX], *bssid;
+
+ os_snprintf(addr, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(dst));
+ bssid = addr;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (!iface || !wpa_s->dbus_new_path)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "ANQPQueryDone");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bssid) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &result))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
#endif /* CONFIG_INTERWORKING */
@@ -2439,6 +2473,9 @@
case WPAS_DBUS_BSS_PROP_AGE:
prop = "Age";
break;
+ case WPAS_DBUS_BSS_PROP_ANQP:
+ prop = "ANQP";
+ break;
default:
wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
__func__, property);
@@ -2977,6 +3014,11 @@
NULL,
NULL
},
+ {"ANQP", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+ wpas_dbus_getter_bss_anqp,
+ NULL,
+ NULL,
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -3716,6 +3758,13 @@
END_ARGS
}
},
+ {"ANQPGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_anqp_get,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ },
+ },
#endif /* CONFIG_INTERWORKING */
{ NULL, NULL, NULL, { END_ARGS } }
};
@@ -4303,6 +4352,13 @@
END_ARGS
}
},
+ {"ANQPQueryDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "addr", "s", ARG_OUT },
+ { "result", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
{ "HS20TermsAndConditions", WPAS_DBUS_NEW_IFACE_INTERFACE,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index b653f10..952bb42 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -53,6 +53,7 @@
WPAS_DBUS_BSS_PROP_WPS,
WPAS_DBUS_BSS_PROP_IES,
WPAS_DBUS_BSS_PROP_AGE,
+ WPAS_DBUS_BSS_PROP_ANQP,
};
enum wpas_dbus_sta_prop {
@@ -279,6 +280,8 @@
int bh, int bss_load,
int conn_capab);
void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *result);
void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
const char *url);
@@ -653,6 +656,12 @@
}
static inline
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *result)
+{
+}
+
+static inline
void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
const char *url)
{
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6ad49a1..3897d98 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1957,6 +1957,7 @@
#ifdef CONFIG_INTERWORKING
+
DBusMessage *
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s)
@@ -1977,6 +1978,111 @@
return reply;
}
+
+
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter, iter_dict;
+ struct wpa_dbus_dict_entry entry;
+ int ret;
+ u8 dst_addr[ETH_ALEN];
+ bool is_addr_present = false;
+ unsigned int freq = 0;
+#define MAX_ANQP_INFO_ID 100 /* Max info ID count from CLI implementation */
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+ u32 subtypes = 0;
+ u32 mbo_subtypes = 0;
+ size_t i;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (os_strcmp(entry.key, "addr") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (hwaddr_aton(entry.str_value, dst_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: Invalid address '%s'",
+ __func__, entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "invalid address");
+ }
+
+ is_addr_present = true;
+ } else if (os_strcmp(entry.key, "freq") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_UINT16) {
+ for (i = 0; i < entry.array_len &&
+ num_id < MAX_ANQP_INFO_ID; i++) {
+ id[num_id] = entry.uint16array_value[i];
+ num_id++;
+ }
+ } else if (os_strcmp(entry.key, "hs20_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+
+ if (num <= 0 || num > 31) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message,
+ "invalid HS20 ANQP id");
+ }
+ subtypes |= BIT(num);
+ }
+ } else if (os_strcmp(entry.key, "mbo_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+
+ if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "invalid MBO ANQP id");
+ }
+ mbo_subtypes |= BIT(num);
+ }
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "unsupported parameter");
+ }
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!is_addr_present) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: address not provided", __func__);
+ return wpas_dbus_error_invalid_args(message,
+ "address not provided");
+ }
+
+ ret = anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
+ mbo_subtypes);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s[dbus]: failed to send ANQP request",
+ __func__);
+ return wpas_dbus_error_unknown_error(
+ message, "error sending ANQP request");
+ }
+
+ return NULL;
+}
+
#endif /* CONFIG_INTERWORKING */
@@ -5652,6 +5758,177 @@
/**
+ * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ANQP" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_anqp(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ DBusMessageIter iter_dict, variant_iter;
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *bss;
+ struct wpa_bss_anqp *anqp;
+ struct wpa_bss_anqp_elem *elem;
+
+ bss = get_bss_helper(args, error, __func__);
+ if (!bss)
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+ goto nomem;
+
+ anqp = bss->anqp;
+ if (anqp) {
+#ifdef CONFIG_INTERWORKING
+ if (anqp->capability_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "CapabilityList",
+ wpabuf_head(anqp->capability_list),
+ wpabuf_len(anqp->capability_list)))
+ goto nomem;
+ if (anqp->venue_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "VenueName",
+ wpabuf_head(anqp->venue_name),
+ wpabuf_len(anqp->venue_name)))
+ goto nomem;
+ if (anqp->network_auth_type &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "NetworkAuthType",
+ wpabuf_head(anqp->network_auth_type),
+ wpabuf_len(anqp->network_auth_type)))
+ goto nomem;
+ if (anqp->roaming_consortium &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "RoamingConsortium",
+ wpabuf_head(anqp->roaming_consortium),
+ wpabuf_len(anqp->roaming_consortium)))
+ goto nomem;
+ if (anqp->ip_addr_type_availability &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "IPAddrTypeAvailability",
+ wpabuf_head(anqp->ip_addr_type_availability),
+ wpabuf_len(anqp->ip_addr_type_availability)))
+ goto nomem;
+ if (anqp->nai_realm &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "NAIRealm",
+ wpabuf_head(anqp->nai_realm),
+ wpabuf_len(anqp->nai_realm)))
+ goto nomem;
+ if (anqp->anqp_3gpp &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "3GPP",
+ wpabuf_head(anqp->anqp_3gpp),
+ wpabuf_len(anqp->anqp_3gpp)))
+ goto nomem;
+ if (anqp->domain_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "DomainName",
+ wpabuf_head(anqp->domain_name),
+ wpabuf_len(anqp->domain_name)))
+ goto nomem;
+ if (anqp->fils_realm_info &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "FilsRealmInfo",
+ wpabuf_head(anqp->fils_realm_info),
+ wpabuf_len(anqp->fils_realm_info)))
+ goto nomem;
+
+#ifdef CONFIG_HS20
+ if (anqp->hs20_capability_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20CapabilityList",
+ wpabuf_head(anqp->hs20_capability_list),
+ wpabuf_len(anqp->hs20_capability_list)))
+ goto nomem;
+ if (anqp->hs20_operator_friendly_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OperatorFriendlyName",
+ wpabuf_head(anqp->hs20_operator_friendly_name),
+ wpabuf_len(anqp->hs20_operator_friendly_name)))
+ goto nomem;
+ if (anqp->hs20_wan_metrics &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20WanMetrics",
+ wpabuf_head(anqp->hs20_wan_metrics),
+ wpabuf_len(anqp->hs20_wan_metrics)))
+ goto nomem;
+ if (anqp->hs20_connection_capability &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20ConnectionCapability",
+ wpabuf_head(anqp->hs20_connection_capability),
+ wpabuf_len(anqp->hs20_connection_capability)))
+ goto nomem;
+ if (anqp->hs20_operating_class &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OperatingClass",
+ wpabuf_head(anqp->hs20_operating_class),
+ wpabuf_len(anqp->hs20_operating_class)))
+ goto nomem;
+ if (anqp->hs20_osu_providers_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OSUProvidersList",
+ wpabuf_head(anqp->hs20_osu_providers_list),
+ wpabuf_len(anqp->hs20_osu_providers_list)))
+ goto nomem;
+ if (anqp->hs20_operator_icon_metadata &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OperatorIconMetadata",
+ wpabuf_head(anqp->hs20_operator_icon_metadata),
+ wpabuf_len(anqp->hs20_operator_icon_metadata)))
+ goto nomem;
+ if (anqp->hs20_osu_providers_nai_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OSUProvidersNAIList",
+ wpabuf_head(anqp->hs20_osu_providers_nai_list),
+ wpabuf_len(anqp->hs20_osu_providers_nai_list)))
+ goto nomem;
+#endif /* CONFIG_HS20 */
+
+ dl_list_for_each(elem, &anqp->anqp_elems,
+ struct wpa_bss_anqp_elem, list) {
+ char title[32];
+
+ os_snprintf(title, sizeof(title), "anqp[%u]",
+ elem->infoid);
+ if (!wpa_dbus_dict_append_byte_array(
+ &iter_dict, title,
+ wpabuf_head(elem->payload),
+ wpabuf_len(elem->payload)))
+ goto nomem;
+
+ os_snprintf(title, sizeof(title),
+ "protected-anqp-info[%u]", elem->infoid);
+ if (!wpa_dbus_dict_append_bool(
+ &iter_dict, title,
+ elem->protected_response))
+ goto nomem;
+ }
+#endif /* CONFIG_INTERWORKING */
+ }
+
+ if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ goto nomem;
+
+ return TRUE;
+
+nomem:
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+}
+
+
+/**
* wpas_dbus_getter_enabled - Check whether network is enabled or disabled
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 97fa337..acd6af7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -157,6 +157,9 @@
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s);
+
DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
DECLARE_ACCESSOR(wpas_dbus_getter_state);
DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
@@ -216,6 +219,7 @@
DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 801e698..e223450 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -238,6 +238,12 @@
wait_time = wpa_s->dpp_resp_retry_time;
else
wait_time = 1000;
+ if (wpa_s->dpp_tx_chan_change) {
+ wpa_s->dpp_tx_chan_change = false;
+ if (wait_time > 100)
+ wait_time = 100;
+ }
+
wpa_printf(MSG_DEBUG,
"DPP: Schedule retransmission of Authentication Response frame in %u ms",
wait_time);
@@ -487,6 +493,21 @@
}
+static void wpas_dpp_neg_freq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!wpa_s->dpp_listen_on_tx_expire || !auth || !auth->neg_freq)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start listen on neg_freq %u MHz based on timeout for TX wait expiration",
+ auth->neg_freq);
+ wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+}
+
+
static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
@@ -605,7 +626,9 @@
wpa_s->dpp_auth->curr_freq,
wpa_s->dpp_auth->neg_freq);
offchannel_send_action_done(wpa_s);
- wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq);
+ wpa_s->dpp_listen_on_tx_expire = true;
+ eloop_register_timeout(0, 100000, wpas_dpp_neg_freq_timeout,
+ wpa_s, NULL);
}
if (wpa_s->dpp_auth_ok_on_ack)
@@ -1035,6 +1058,8 @@
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = lwork->freq;
wpa_drv_dpp_listen(wpa_s, true);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = false;
}
@@ -1134,11 +1159,58 @@
}
+static void wpas_dpp_tx_auth_resp(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ offchannel_send_action(wpa_s, auth->curr_freq,
+ auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg),
+ 500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_tx_auth_resp_roc_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth || !wpa_s->dpp_tx_auth_resp_on_roc_stop)
+ return;
+
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = true;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send postponed Authentication Response on remain-on-channel termination timeout");
+ wpas_dpp_tx_auth_resp(wpa_s);
+}
+
+
void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
+ wpa_printf(MSG_DEBUG, "DPP: Remain on channel cancel for %u MHz", freq);
wpas_dpp_listen_work_done(wpa_s);
+ if (wpa_s->dpp_auth && wpa_s->dpp_tx_auth_resp_on_roc_stop) {
+ eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout,
+ wpa_s, NULL);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = true;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send postponed Authentication Response on remain-on-channel termination");
+ wpas_dpp_tx_auth_resp(wpa_s);
+ return;
+ }
+
if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) {
unsigned int new_freq;
@@ -1256,17 +1328,17 @@
wpa_printf(MSG_DEBUG,
"DPP: Stop listen on %u MHz to allow response on the request %u MHz",
wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = true;
+ eloop_register_timeout(0, 100000,
+ wpas_dpp_tx_auth_resp_roc_timeout,
+ wpa_s, NULL);
wpas_dpp_listen_stop(wpa_s);
+ return;
}
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = false;
- wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
- MAC2STR(src), wpa_s->dpp_auth->curr_freq,
- DPP_PA_AUTHENTICATION_RESP);
- offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
- src, wpa_s->own_addr, broadcast,
- wpabuf_head(wpa_s->dpp_auth->resp_msg),
- wpabuf_len(wpa_s->dpp_auth->resp_msg),
- 500, wpas_dpp_tx_status, 0);
+ wpas_dpp_tx_auth_resp(wpa_s);
}
@@ -1275,6 +1347,15 @@
struct dpp_authentication *auth = wpa_s->dpp_auth;
int freq;
+ if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel",
+ auth->neg_freq);
+ eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
+ wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+ return;
+ }
+
if (!wpa_s->dpp_gas_server || !auth) {
if (auth && auth->waiting_auth_resp &&
eloop_is_timeout_registered(wpas_dpp_drv_wait_timeout,
@@ -4816,6 +4897,8 @@
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);
+ eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_neg_freq_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,
@@ -5627,6 +5710,7 @@
eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
wpa_s, NULL);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
@@ -5688,6 +5772,7 @@
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 9a4c235..d01b52b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -128,7 +128,7 @@
}
struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s);
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid);
static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
{
@@ -342,14 +342,6 @@
return -1;
}
-static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
- struct wpa_driver_ap_params *params)
-{
- if (wpa_s->driver->set_ap)
- return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
- return -1;
-}
-
static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
struct hostapd_sta_add_params *params)
{
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9641062..95953de 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -670,6 +670,10 @@
wpa_s->eapol = NULL;
if (e->radius_conf && e->radius_conf->auth_server) {
os_free(e->radius_conf->auth_server->shared_secret);
+ os_free(e->radius_conf->auth_server->ca_cert);
+ os_free(e->radius_conf->auth_server->client_cert);
+ os_free(e->radius_conf->auth_server->private_key);
+ os_free(e->radius_conf->auth_server->private_key_passwd);
os_free(e->radius_conf->auth_server);
}
os_free(e->radius_conf);
@@ -1007,7 +1011,10 @@
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
- int port, const char *secret,
+ int port, bool tls, const char *secret,
+ const char *ca_cert, const char *client_cert,
+ const char *private_key,
+ const char *private_key_passwd,
const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
@@ -1045,8 +1052,17 @@
}
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
as->port = port;
+ as->tls = tls;
as->shared_secret = (u8 *) os_strdup(secret);
as->shared_secret_len = os_strlen(secret);
+ if (ca_cert)
+ as->ca_cert = os_strdup(ca_cert);
+ if (client_cert)
+ as->client_cert = os_strdup(client_cert);
+ if (private_key)
+ as->private_key = os_strdup(private_key);
+ if (private_key_passwd)
+ as->private_key_passwd = os_strdup(private_key_passwd);
e->radius_conf->auth_server = as;
e->radius_conf->auth_servers = as;
e->radius_conf->msg_dumps = 1;
@@ -1256,11 +1272,16 @@
{
printf("usage:\n"
"eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
- "[-s<AS secret>]\\\n"
+ "[-s<AS secret>] \\\n"
+ " [-X<RADIUS protocol> \\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
+#ifdef CONFIG_RADIUS_TLS
+ " [-j<CA cert>] [-J<client cert>] \\\n"
+ "[-k<private key] [-K<private key passwd>] \\\n"
+#endif /* CONFIG_RADIUS_TLS */
" [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1269,10 +1290,11 @@
" -c<conf> = configuration file\n"
" -a<AS IP> = IP address of the authentication server, "
"default 127.0.0.1\n"
- " -p<AS port> = UDP port of the authentication server, "
- "default 1812\n"
- " -s<AS secret> = shared secret with the authentication "
- "server, default 'radius'\n"
+ " -p<AS port> = Port of the authentication server,\n"
+ " default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n"
+ " -s<AS secret> = shared secret with the authentication server,\n"
+ " default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n"
+ " -X<RADIUS protocol> = RADIUS protocol to use: UDP (default) or TLS\n"
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
@@ -1310,8 +1332,11 @@
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
- int as_port = 1812;
- char *as_secret = "radius";
+ int as_port = -1;
+ char *as_secret = NULL;
+ char *ca_cert = NULL, *client_cert = NULL;
+ char *private_key = NULL, *private_key_passwd = NULL;
+ bool tls = false;
char *cli_addr = NULL;
char *conf = NULL;
int timeout = 30;
@@ -1334,7 +1359,8 @@
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
+ c = getopt(argc, argv,
+ "a:A:c:C:ei:j:J:k:K:M:nN:o:p:P:r:R:s:St:T:vWX:");
if (c < 0)
break;
switch (c) {
@@ -1356,6 +1382,20 @@
case 'i':
ifname = optarg;
break;
+#ifdef CONFIG_RADIUS_TLS
+ case 'j':
+ ca_cert = optarg;
+ break;
+ case 'J':
+ client_cert = optarg;
+ break;
+ case 'k':
+ private_key = optarg;
+ break;
+ case 'K':
+ private_key_passwd = optarg;
+ break;
+#endif /* CONFIG_RADIUS_TLS */
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
@@ -1406,6 +1446,16 @@
case 'W':
wait_for_monitor++;
break;
+ case 'X':
+ if (os_strcmp(optarg, "UDP") == 0) {
+ tls = false;
+ } else if (os_strcmp(optarg, "TLS") == 0) {
+ tls = true;
+ } else {
+ usage();
+ return -1;
+ }
+ break;
case 'N':
p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
@@ -1440,6 +1490,11 @@
}
}
+ if (!as_secret)
+ as_secret = tls ? "radsec" : "radius";
+ if (as_port < 0)
+ as_port = tls ? 2083 : 1812;
+
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
return scard_test(&eapol_test);
}
@@ -1489,7 +1544,8 @@
wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
}
- wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
+ wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, tls, as_secret,
+ ca_cert, client_cert, private_key, private_key_passwd,
cli_addr, ifname);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index bc45579..ff18543 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -164,7 +164,7 @@
struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
/* Get the BSS from the new scan results */
bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
@@ -183,7 +183,7 @@
struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
}
@@ -1156,19 +1156,18 @@
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, NULL, NULL))
return true;
- if (bss->n_mld_links == 0)
+ if (!bss->valid_links)
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)
+ if (BIT(bss->mld_link_id) & removed_links)
return false;
return true;
@@ -1697,13 +1696,11 @@
return NULL;
}
-#ifdef CONFIG_WNM
if (wnm_is_bss_excluded(wpa_s, bss)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID excluded");
return NULL;
}
-#endif /* CONFIG_WNM */
for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
@@ -1809,10 +1806,12 @@
break;
}
- if (selected == NULL && wpa_s->bssid_ignore &&
+ if (!selected &&
+ (wpa_s->bssid_ignore || wnm_active_bss_trans_mgmt(wpa_s)) &&
!wpa_s->countermeasures) {
wpa_dbg(wpa_s, MSG_DEBUG,
"No APs found - clear BSSID ignore list and try again");
+ wnm_btm_reset(wpa_s);
wpa_bssid_ignore_clear(wpa_s);
wpa_s->bssid_ignore_cleared = true;
} else if (selected == NULL)
@@ -2409,7 +2408,7 @@
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
- NULL, 1);
+ NULL, 1, NULL);
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap ||
wpa_s->scan_res_handler == scan_only_handler)
@@ -2498,7 +2497,7 @@
return 0;
}
- if (wnm_scan_process(wpa_s, 1) > 0)
+ if (wnm_scan_process(wpa_s, false) > 0)
goto scan_work_done;
if (sme_proc_obss_scan(wpa_s) > 0)
@@ -2941,8 +2940,6 @@
}
-#ifdef CONFIG_INTERWORKING
-
static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
size_t len)
{
@@ -2975,8 +2972,6 @@
}
}
-#endif /* CONFIG_INTERWORKING */
-
static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
{
@@ -3002,25 +2997,24 @@
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems elems;
- const u8 *map_sub_elem, *pos;
- size_t len;
+ struct multi_ap_params multi_ap;
+ u16 status;
wpa_s->multi_ap_ie = 0;
if (!ies ||
ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed ||
- !elems.multi_ap || elems.multi_ap_len < 7)
+ !elems.multi_ap)
return;
- pos = elems.multi_ap + 4;
- len = elems.multi_ap_len - 4;
-
- map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
- if (!map_sub_elem || map_sub_elem[1] < 1)
+ status = check_multi_ap_ie(elems.multi_ap + 4, elems.multi_ap_len - 4,
+ &multi_ap);
+ if (status != WLAN_STATUS_SUCCESS)
return;
- wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS);
- wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] &
+ wpa_s->multi_ap_backhaul = !!(multi_ap.capability &
+ MULTI_AP_BACKHAUL_BSS);
+ wpa_s->multi_ap_fronthaul = !!(multi_ap.capability &
MULTI_AP_FRONTHAUL_BSS);
wpa_s->multi_ap_ie = 1;
}
@@ -3359,10 +3353,8 @@
wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_WNM */
-#ifdef CONFIG_INTERWORKING
interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
-#endif /* CONFIG_INTERWORKING */
if (wpa_s->hw_capab == CAPAB_VHT &&
get_ie(data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
@@ -3718,12 +3710,21 @@
if (wpa_found || rsn_found)
wpa_s->ap_ies_from_associnfo = 1;
- if (wpa_s->assoc_freq && data->assoc_info.freq &&
- wpa_s->assoc_freq != data->assoc_info.freq) {
- wpa_printf(MSG_DEBUG, "Operating frequency changed from "
- "%u to %u MHz",
- wpa_s->assoc_freq, data->assoc_info.freq);
- wpa_supplicant_update_scan_results(wpa_s);
+ if (wpa_s->assoc_freq && data->assoc_info.freq) {
+ struct wpa_bss *bss;
+ unsigned int freq = 0;
+
+ if (bssid_known) {
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (bss)
+ freq = bss->freq;
+ }
+ if (freq != data->assoc_info.freq) {
+ wpa_printf(MSG_DEBUG,
+ "Operating frequency changed from %u to %u MHz",
+ wpa_s->assoc_freq, data->assoc_info.freq);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
+ }
}
wpa_s->assoc_freq = data->assoc_info.freq;
@@ -4077,10 +4078,7 @@
if (!mlo.valid_links)
return 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo.valid_links, i) {
if (!ether_addr_equal(wpa_s->links[i].addr,
mlo.links[i].addr) ||
!ether_addr_equal(wpa_s->links[i].bssid,
@@ -4098,10 +4096,7 @@
wpa_s->valid_links = mlo.valid_links;
wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
wpa_s->links[i].freq = mlo.links[i].freq;
@@ -4134,15 +4129,13 @@
wpa_mlo.valid_links = drv_mlo.valid_links;
wpa_mlo.req_links = drv_mlo.req_links;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ for_each_link(drv_mlo.req_links, i) {
struct wpa_bss *bss;
- if (!(drv_mlo.req_links & BIT(i)))
- continue;
-
bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(
+ wpa_s, drv_mlo.links[i].bssid);
bss = wpa_supplicant_get_new_bss(
wpa_s, drv_mlo.links[i].bssid);
}
@@ -6021,13 +6014,10 @@
pos += res;
if (!info->default_map) {
- for (i = 0; i < MAX_NUM_MLD_LINKS && end > pos; i++) {
+ for_each_link(info->valid_links, i) {
char uplink_map_str[9];
char downlink_map_str[9];
- if (!(info->valid_links & BIT(i)))
- continue;
-
bitmap_to_str(info->t2lmap[i].uplink, uplink_map_str);
bitmap_to_str(info->t2lmap[i].downlink,
downlink_map_str);
@@ -6307,6 +6297,13 @@
" type=%d stype=%d",
MAC2STR(data->tx_status.dst),
data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_WNM
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+ wnm_btm_resp_tx_status(wpa_s, data->tx_status.data,
+ data->tx_status.data_len) == 0)
+ break;
+#endif /* CONFIG_WNM */
#ifdef CONFIG_PASN
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 498cc98..5676f38 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -2820,7 +2820,7 @@
struct wpa_bss *bss;
int res;
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
if (!bss && !freq) {
wpa_printf(MSG_WARNING,
"ANQP: Cannot send query without BSS freq info");
@@ -3175,7 +3175,7 @@
}
}
if (bss == NULL)
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
pos = wpabuf_head(resp);
end = pos + wpabuf_len(resp);
@@ -3206,12 +3206,12 @@
}
out_parse_done:
+ if (bss)
+ wpas_notify_bss_anqp_changed(wpa_s, bss->id);
#ifdef CONFIG_HS20
hs20_notify_parse_done(wpa_s);
#endif /* CONFIG_HS20 */
out:
- wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
- MAC2STR(dst), anqp_result);
wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
}
@@ -3290,7 +3290,7 @@
u8 query_resp_len_limit = 0;
freq = wpa_s->assoc_freq;
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
if (bss)
freq = bss->freq;
if (freq <= 0)
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 486fc6a..85c1ea8 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -231,7 +231,7 @@
hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
ifmsh->conf->vht_capab,
- he_capab, NULL)) {
+ he_capab, NULL, 0)) {
wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
wpa_supplicant_mesh_deinit(wpa_s, true);
return -1;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 6f162f2..c930651 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -627,6 +627,15 @@
}
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_ANQP, id);
+}
+
+
void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
{
if (wpa_s->p2p_mgmt)
@@ -935,7 +944,8 @@
const u8 *p2p_dev_addr, const u8 *ip)
{
if (authorized)
- wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr, ip);
+ wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr,
+ ip);
else
wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
}
@@ -1057,11 +1067,14 @@
const char *result,
const struct wpa_bss_anqp *anqp)
{
+ wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+ MAC2STR(bssid), result);
#ifdef CONFIG_INTERWORKING
if (!wpa_s || !bssid || !anqp)
return;
wpas_aidl_notify_anqp_query_done(wpa_s, bssid, result, anqp);
+ wpas_dbus_signal_anqp_query_done(wpa_s, bssid, result);
#endif /* CONFIG_INTERWORKING */
}
@@ -1359,6 +1372,7 @@
wpas_dbus_signal_interworking_select_done(wpa_s);
}
+
#endif /* CONFIG_INTERWORKING */
void wpas_notify_eap_method_selected(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index d4656ad..a584884 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -89,6 +89,8 @@
void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
unsigned int id);
void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index b4ad3ca..ff11d20 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,12 @@
unsigned int *flags)
{
int i;
- bool is_6ghz = op_class >= 131 && op_class <= 136;
+ bool is_6ghz = is_6ghz_op_class(op_class);
for (i = 0; i < mode->num_channels; i++) {
bool chan_is_6ghz;
- chan_is_6ghz = mode->channels[i].freq >= 5935 &&
- mode->channels[i].freq <= 7115;
+ chan_is_6ghz = is_6ghz_freq(mode->channels[i].freq);
if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
break;
}
@@ -187,6 +186,69 @@
}
+static int get_center_320mhz(struct hostapd_hw_modes *mode, u8 channel,
+ const u8 *center_channels, size_t num_chan)
+{
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A || !mode->is_6ghz)
+ return 0;
+
+ for (i = 0; i < num_chan; i++) {
+ /*
+ * In 320 MHz, the bandwidth "spans" 60 channels (e.g., 65-125),
+ * so the center channel is 30 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 30 &&
+ channel <= center_channels[i] + 30)
+ return center_channels[i];
+ }
+
+ return 0;
+}
+
+
+static enum chan_allowed verify_320mhz(struct hostapd_hw_modes *mode,
+ u8 op_class, u8 channel)
+{
+ u8 center_chan;
+ unsigned int i;
+ bool no_ir = false;
+ const u8 *center_channels;
+ size_t num_chan;
+ const u8 center_channels_6ghz[] = { 31, 63, 95, 127, 159, 191 };
+
+ center_channels = center_channels_6ghz;
+ num_chan = ARRAY_SIZE(center_channels_6ghz);
+
+ center_chan = get_center_320mhz(mode, channel, center_channels,
+ num_chan);
+ if (!center_chan)
+ return NOT_ALLOWED;
+
+ /* Check all the channels are available */
+ for (i = 0; i < 16; i++) {
+ unsigned int flags;
+ u8 adj_chan = center_chan - 30 + i * 4;
+
+ if (allow_channel(mode, op_class, adj_chan, &flags) ==
+ NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (!(flags & HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL))
+ return NOT_ALLOWED;
+
+ if (flags & HOSTAPD_CHAN_NO_IR)
+ no_ir = true;
+ }
+
+ if (no_ir)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
u8 channel, u8 bw)
{
@@ -228,6 +290,13 @@
* result and use only the 80 MHz specific version.
*/
res2 = res = verify_80mhz(mode, op_class, channel);
+ } else if (bw == BW320) {
+ /*
+ * channel is a center channel and as such, not necessarily a
+ * valid 20 MHz channels. Override earlier allow_channel()
+ * result and use only the 320 MHz specific version.
+ */
+ res2= res = verify_320mhz(mode, op_class, channel);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -251,6 +320,7 @@
int z;
int freq2 = 0;
int freq5 = 0;
+ bool freq6 = false;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode,
is_6ghz_op_class(op_class->op_class));
@@ -265,7 +335,9 @@
if (f == 0)
break; /* end of list */
- if (f > 4000 && f < 6000)
+ if (is_6ghz_freq(f))
+ freq6 = true;
+ else if (f > 4000 && f < 6000)
freq5 = 1;
else if (f > 2400 && f < 2500)
freq2 = 1;
@@ -274,8 +346,11 @@
/* No frequencies specified, can use anything hardware supports.
*/
freq2 = freq5 = 1;
+ freq6 = true;
}
+ if (is_6ghz_op_class(op_class->op_class) && !freq6)
+ return 0;
if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
return 0;
if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
@@ -453,6 +528,7 @@
u8 op, current, chan;
u8 *ie_len;
size_t res;
+ bool op128 = false, op130 = false, op133 = false, op135 = false;
/*
* Determine the current operating class correct mode based on
@@ -480,8 +556,50 @@
wpabuf_put_u8(buf, current);
for (op = 0; global_op_class[op].op_class; op++) {
- if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
- wpabuf_put_u8(buf, global_op_class[op].op_class);
+ bool supp;
+ u8 op_class = global_op_class[op].op_class;
+
+ supp = wpas_op_class_supported(wpa_s, ssid,
+ &global_op_class[op]);
+ if (!supp)
+ continue;
+ switch (op_class) {
+ case 128:
+ op128 = true;
+ break;
+ case 130:
+ op130 = true;
+ break;
+ case 133:
+ op133 = true;
+ break;
+ case 135:
+ op135 = true;
+ break;
+ }
+ if (is_80plus_op_class(op_class))
+ continue;
+
+ /* Add a 1-octet operating class to the Operating Class field */
+ wpabuf_put_u8(buf, global_op_class[op].op_class);
+ }
+
+ /* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */
+ if ((op128 && op130) || (op133 && op135)) {
+ /* Operating Class Duple Sequence field */
+
+ /* Zero Delimiter */
+ wpabuf_put_u8(buf, 0);
+
+ /* Operating Class Duple List */
+ if (op128 && op130) {
+ wpabuf_put_u8(buf, 130);
+ wpabuf_put_u8(buf, 128);
+ }
+ if (op133 && op135) {
+ wpabuf_put_u8(buf, 135);
+ wpabuf_put_u8(buf, 133);
+ }
}
*ie_len = wpabuf_len(buf) - 2;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 3c121c8..9c20ee5 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1103,6 +1103,8 @@
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+ wpa_s->p2p_go_no_pri_sec_switch = 0;
+
return 0;
}
@@ -7003,7 +7005,8 @@
return -1;
}
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, selected_freq, vht_center_freq2,
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, selected_freq,
+ vht_center_freq2,
ht40, vht, max_oper_chwidth, he, edmg,
NULL))
return -1;
@@ -7097,7 +7100,7 @@
* fetch time on the same radio so it reflects the actual time the last
* scan result event occurred.
*/
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, go_bssid);
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs == wpa_s)
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2e65cf0..1bb38f7 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -166,7 +166,7 @@
bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, peer_addr);
bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
@@ -560,9 +560,10 @@
derive_kdk = wpa_s->conf->force_kdk_derivation;
#endif /* CONFIG_TESTING_OPTIONS */
if (derive_kdk)
- pasn->kdk_len = WPA_KDK_MAX_LEN;
+ pasn_enable_kdk_derivation(pasn);
else
- pasn->kdk_len = 0;
+ pasn_disable_kdk_derivation(pasn);
+
wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
@@ -582,9 +583,8 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
- pasn->rsnxe_capab = capab;
- pasn->send_mgmt = wpas_pasn_send_mlme;
-
+ pasn_set_rsnxe_caps(pasn, capab);
+ pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
#ifdef CONFIG_SAE
@@ -594,7 +594,7 @@
"PASN: No network profile found for SAE");
goto fail;
}
- pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+ pasn_set_pt(pasn, wpas_pasn_sae_derive_pt(ssid, awork->group));
if (!pasn->pt) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
goto fail;
@@ -629,8 +629,7 @@
}
#endif /* CONFIG_FILS */
- pasn->cb_ctx = wpa_s;
- pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+ pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa));
if (wpa_key_mgmt_ft(awork->akmp)) {
#ifdef CONFIG_IEEE80211R
@@ -753,7 +752,8 @@
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
- wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
+ wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn_get_akmp(pasn),
+ pasn_get_cipher(pasn),
pasn->status, pasn->comeback,
pasn->comeback_after);
@@ -765,8 +765,8 @@
struct pasn_data *pasn,
struct wpa_pasn_params_data *params)
{
- int akmp = pasn->akmp;
- int cipher = pasn->cipher;
+ int akmp = pasn_get_akmp(pasn);
+ int cipher = pasn_get_cipher(pasn);
u16 group = pasn->group;
u8 own_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
@@ -806,20 +806,22 @@
if (!wpa_s->pasn_auth_work)
return -2;
- pasn->cb_ctx = wpa_s;
+ pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
if (ret == 0) {
ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
- pasn->cipher, dot11RSNAConfigPMKLifetime,
- &pasn->ptk,
+ pasn_get_cipher(pasn),
+ dot11RSNAConfigPMKLifetime,
+ pasn_get_ptk(pasn),
wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
- wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
+ wpa_s->pasn_params ? wpa_s : NULL,
+ pasn_get_akmp(pasn));
if (pasn->pmksa_entry)
wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
}
- forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+ forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
if (ret == -1) {
wpas_pasn_auth_stop(wpa_s);
@@ -909,7 +911,8 @@
}
wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
- pasn->cipher, pasn->akmp);
+ pasn_get_cipher(pasn),
+ pasn_get_akmp(pasn));
wpas_pasn_auth_stop(wpa_s);
wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 7ce854b..2ec4310 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -515,6 +515,8 @@
num_primary_channels = 4;
else if (op->bw == BW160)
num_primary_channels = 8;
+ else if (op->bw == BW320)
+ num_primary_channels = 16;
else
num_primary_channels = 1;
@@ -561,6 +563,7 @@
u8 channels_80mhz_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151,
167, 183, 199, 215 };
u8 channels_160mhz_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
+ u8 channels_320mhz_6ghz[] = { 31, 63, 95, 127, 159, 191 };
const u8 *channels = NULL;
size_t num_chan = 0;
bool is_6ghz = is_6ghz_op_class(op->op_class);
@@ -579,6 +582,9 @@
channels_160mhz_5ghz;
num_chan = is_6ghz ? ARRAY_SIZE(channels_160mhz_6ghz) :
ARRAY_SIZE(channels_160mhz_5ghz);
+ } else if (op->bw == BW320) {
+ channels = channels_320mhz_6ghz;
+ num_chan = ARRAY_SIZE(channels_320mhz_6ghz);
}
return wpas_add_channels(op, mode, channels, num_chan);
@@ -1520,10 +1526,7 @@
if (!wpa_s->valid_links)
return ether_addr_equal(wpa_s->current_bss->bssid, bssid);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
if (ether_addr_equal(wpa_s->links[i].bssid, bssid))
return true;
}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 6e6f05d..ccd694b 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -698,10 +698,7 @@
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;
-
+ for_each_link(links, link_id) {
wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
/* Subelement length includes only the control */
@@ -2606,8 +2603,8 @@
}
-void filter_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *res)
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *res)
{
size_t i, j;
@@ -3169,6 +3166,7 @@
* @wpa_s: Pointer to wpa_supplicant data
* @info: Information about what was scanned or %NULL if not available
* @new_scan: Whether a new scan was performed
+ * @bssid: Return BSS entries only for a single BSSID, %NULL for all
* Returns: Scan results, %NULL on failure
*
* This function request the current scan results from the driver and updates
@@ -3177,13 +3175,14 @@
*/
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
- struct scan_info *info, int new_scan)
+ struct scan_info *info, int new_scan,
+ const u8 *bssid)
{
struct wpa_scan_results *scan_res;
size_t i;
int (*compar)(const void *, const void *) = wpa_scan_result_compar;
- scan_res = wpa_drv_get_scan_results2(wpa_s);
+ scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
if (scan_res == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
@@ -3241,6 +3240,7 @@
/**
* wpa_supplicant_update_scan_results - Update scan results from the driver
* @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: Update BSS entries only for a single BSSID, %NULL for all
* Returns: 0 on success, -1 on failure
*
* This function updates the BSS table within wpa_supplicant based on the
@@ -3250,10 +3250,11 @@
* needed information to complete the connection (e.g., to perform validation
* steps in 4-way handshake).
*/
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
{
struct wpa_scan_results *scan_res;
- scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
if (scan_res == NULL)
return -1;
wpa_scan_results_free(scan_res);
@@ -3350,6 +3351,7 @@
params->duration = src->duration;
params->duration_mandatory = src->duration_mandatory;
params->oce_scan = src->oce_scan;
+ params->link_id = src->link_id;
if (src->sched_scan_plans_num > 0) {
params->sched_scan_plans =
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 8402e74..d4c06c1 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -54,8 +54,10 @@
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);
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+ struct scan_info *info, int new_scan,
+ const u8 *bssid);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type);
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
@@ -86,8 +88,6 @@
int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
unsigned int type, u8 *mask);
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
-void filter_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *res);
void scan_snr(struct wpa_scan_res *res);
void scan_est_throughput(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index b8f7c65..be0bc0d 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -182,7 +182,7 @@
if (!bss) {
wpa_printf(MSG_DEBUG,
"SAE: BSS not available, update scan result to get BSS");
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
}
if (bss) {
@@ -405,10 +405,7 @@
return bss;
if (!is_zero_ether_addr(wpa_s->conf->mld_connect_bssid_pref)) {
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
if (wpa_s->mlo_assoc_link_id == i)
continue;
@@ -439,10 +436,7 @@
return bss;
}
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
if (wpa_s->mlo_assoc_link_id == i)
continue;
@@ -523,21 +517,23 @@
static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
- int i;
+ u8 i;
wpa_s->valid_links = 0;
+ wpa_s->mlo_assoc_link_id = bss->mld_link_id;
- for (i = 0; i < bss->n_mld_links; i++) {
- u8 link_id = bss->mld_links[i].link_id;
+ for_each_link(bss->valid_links, i) {
const u8 *bssid = bss->mld_links[i].bssid;
- if (i == 0)
- wpa_s->mlo_assoc_link_id = link_id;
- wpa_s->valid_links |= BIT(link_id);
- os_memcpy(wpa_s->links[link_id].bssid, bssid, ETH_ALEN);
- wpa_s->links[link_id].freq = bss->mld_links[i].freq;
- wpa_s->links[link_id].bss = wpa_bss_get_bssid(wpa_s, bssid);
- wpa_s->links[link_id].disabled = bss->mld_links[i].disabled;
+ wpa_s->valid_links |= BIT(i);
+ os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+ wpa_s->links[i].freq = bss->mld_links[i].freq;
+ wpa_s->links[i].disabled = bss->mld_links[i].disabled;
+
+ if (bss->mld_link_id == i)
+ wpa_s->links[i].bss = bss;
+ else
+ wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
}
}
@@ -578,7 +574,7 @@
if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
!wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
NULL, ssid, NULL) &&
- bss->n_mld_links) {
+ bss->valid_links) {
wpa_printf(MSG_DEBUG, "MLD: In authentication");
wpas_sme_set_mlo_links(wpa_s, bss);
@@ -2416,12 +2412,16 @@
if (ssid && ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
+ struct multi_ap_params multi_ap = { 0 };
+
+ multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+ multi_ap.profile = ssid->multi_ap_profile;
multi_ap_ie_len = add_multi_ap_ie(
wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len,
- MULTI_AP_BACKHAUL_STA);
+ &multi_ap);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
@@ -2601,10 +2601,7 @@
params.mld_params.mld_addr = wpa_s->ap_mld_addr;
params.mld_params.valid_links = wpa_s->valid_links;
params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
params.mld_params.mld_links[i].bssid =
wpa_s->links[i].bssid;
params.mld_params.mld_links[i].freq =
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 775e75b..ea79ae6 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -418,7 +418,7 @@
}
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+void wnm_btm_reset(struct wpa_supplicant *wpa_s)
{
int i;
@@ -431,8 +431,17 @@
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
- wpabuf_free(wpa_s->coloc_intf_elems);
- wpa_s->coloc_intf_elems = NULL;
+ wpa_s->wnm_cand_valid_until.sec = 0;
+ wpa_s->wnm_cand_valid_until.usec = 0;
+
+ wpa_s->wnm_mode = 0;
+ wpa_s->wnm_dialog_token = 0;
+ wpa_s->wnm_reply = 0;
+
+#ifdef CONFIG_MBO
+ wpa_s->wnm_mbo_trans_reason_present = 0;
+ wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
}
@@ -781,22 +790,11 @@
}
}
- if (bss->ssid_len != target->ssid_len ||
- os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
- /*
- * TODO: Could consider allowing transition to another
- * ESS if PMF was enabled for the association.
- */
- wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
- " (pref %d) in different ESS",
- MAC2STR(nei->bssid),
- nei->preference_present ? nei->preference :
- -1);
- continue;
- }
-
- if (wpa_s->current_ssid &&
- !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+ /*
+ * TODO: Could consider allowing transition to another ESS if
+ * PMF was enabled for the association.
+ */
+ if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
1, 0)) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) does not match the current network profile",
@@ -806,14 +804,6 @@
continue;
}
- if (wpa_is_bss_tmp_disallowed(wpa_s, target)) {
- wpa_printf(MSG_DEBUG,
- "MBO: Candidate BSS " MACSTR
- " retry delay is not over yet",
- MAC2STR(nei->bssid));
- continue;
- }
-
if (target->level < bss->level && target->level < -80) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) does not have sufficient signal level (%d)",
@@ -1045,8 +1035,8 @@
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
-static void wnm_send_bss_transition_mgmt_resp(
- struct wpa_supplicant *wpa_s, u8 dialog_token,
+static int wnm_send_bss_transition_mgmt_resp(
+ struct wpa_supplicant *wpa_s,
enum bss_trans_mgmt_status_code status,
enum mbo_transition_reject_reason reason,
u8 delay, const u8 *target_bssid)
@@ -1054,21 +1044,24 @@
struct wpabuf *buf;
int res;
+ wpa_s->wnm_reply = 0;
+
wpa_printf(MSG_DEBUG,
"WNM: Send BSS Transition Management Response to " MACSTR
" dialog_token=%u status=%u reason=%u delay=%d",
- MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
+ MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
+ reason, delay);
if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Current BSS not known - drop response");
- return;
+ return -1;
}
buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
if (!buf) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for BTM response");
- return;
+ return -1;
}
wpa_s->bss_tm_status = status;
@@ -1076,7 +1069,7 @@
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
- wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
wpabuf_put_u8(buf, status);
wpabuf_put_u8(buf, delay);
if (target_bssid) {
@@ -1090,7 +1083,7 @@
wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
}
- if (status == WNM_BSS_TM_ACCEPT && !wpa_s->wnm_link_removal)
+ if (status == WNM_BSS_TM_ACCEPT && target_bssid)
wnm_add_cand_list(wpa_s, &buf);
#ifdef CONFIG_MBO
@@ -1106,7 +1099,7 @@
wpabuf_free(buf);
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for MBO IE");
- return;
+ return -1;
}
wpabuf_put_data(buf, mbo, ret);
@@ -1123,6 +1116,8 @@
}
wpabuf_free(buf);
+
+ return res;
}
@@ -1140,19 +1135,24 @@
/* Send the BSS Management Response - Accept */
if (wpa_s->wnm_reply) {
- wpa_s->wnm_reply = 0;
+ wpa_s->wnm_target_bss = bss;
wpa_printf(MSG_DEBUG,
"WNM: Sending successful BSS Transition Management Response");
- wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
- MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
- bss->bssid);
+
+ /* This function will be called again from the TX handler to
+ * start the actual reassociation after this response has been
+ * delivered to the current AP. */
+ if (wnm_send_bss_transition_mgmt_resp(
+ wpa_s, WNM_BSS_TM_ACCEPT,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+ bss->bssid) >= 0)
+ return;
}
if (bss == wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Already associated with the preferred candidate");
- wnm_deallocate_memory(wpa_s);
+ wnm_btm_reset(wpa_s);
return;
}
@@ -1168,11 +1168,10 @@
*/
if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
wpa_s->bss_trans_mgmt_in_progress = true;
- wnm_deallocate_memory(wpa_s);
}
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
{
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -1180,27 +1179,51 @@
enum mbo_transition_reject_reason reason =
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
- if (!wpa_s->wnm_neighbor_report_elements)
+ if (!wpa_s->wnm_dialog_token)
return 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Process scan results for BSS Transition Management");
- if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+ if (!pre_scan_check &&
+ os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
+ os_reltime_before(&wpa_s->wnm_cand_valid_until,
&wpa_s->scan_trigger_time)) {
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
- wnm_deallocate_memory(wpa_s);
- return 0;
- }
-
- if (!wpa_s->current_bss ||
- !ether_addr_equal(wpa_s->wnm_cand_from_bss,
- wpa_s->current_bss->bssid)) {
- wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
- return 0;
+ goto send_bss_resp_fail;
}
/* Compare the Neighbor Report and scan results */
bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
+
+ /*
+ * If this is a pre-scan check, returning 0 will trigger a scan and
+ * another call. In that case, reject "bad" candidates in the hope of
+ * finding a better candidate after scanning.
+ *
+ * Use a simple heuristic to check whether the selection is reasonable
+ * or a scan is a good idea. For that, we need to have found a
+ * candidate BSS (which might be the current one), it is up-to-date,
+ * and we don't want to immediately roam back again.
+ */
+ if (pre_scan_check) {
+ struct os_reltime age;
+
+ if (!bss)
+ return 0;
+
+ os_reltime_age(&bss->last_update, &age);
+ if (age.sec >= 10)
+ return 0;
+
+#ifndef CONFIG_NO_ROAMING
+ if (wpa_s->current_bss && bss != wpa_s->current_bss &&
+ wpa_supplicant_need_to_roam_within_ess(wpa_s,
+ wpa_s->current_bss,
+ bss))
+ return 0;
+#endif /* CONFIG_NO_ROAMING */
+ }
+
if (!bss) {
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -1212,18 +1235,13 @@
return 1;
send_bss_resp_fail:
- if (!reply_on_fail)
- return 0;
-
/* Send reject response for all the failures */
- if (wpa_s->wnm_reply) {
- wpa_s->wnm_reply = 0;
- wnm_send_bss_transition_mgmt_resp(wpa_s,
- wpa_s->wnm_dialog_token,
- status, reason, 0, NULL);
- }
- wnm_deallocate_memory(wpa_s);
+ if (wpa_s->wnm_reply)
+ wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
+ 0, NULL);
+
+ wnm_btm_reset(wpa_s);
return 0;
}
@@ -1330,6 +1348,10 @@
struct neighbor_report *nei;
nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+ if (nei->preference_present && nei->preference == 0)
+ continue;
+
if (nei->freq <= 0) {
wpa_printf(MSG_DEBUG,
"WNM: Unknown neighbor operating frequency for "
@@ -1354,79 +1376,6 @@
}
-static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
-{
- struct wpa_scan_results *scan_res;
- struct wpa_bss *bss;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- u8 i, found = 0;
- size_t j;
-
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: Fetch current scan results from the driver for checking transition candidates");
- scan_res = wpa_drv_get_scan_results2(wpa_s);
- if (!scan_res) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
- return 0;
- }
-
- if (scan_res->fetch_time.sec == 0)
- os_get_reltime(&scan_res->fetch_time);
-
- filter_scan_res(wpa_s, scan_res);
-
- for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
- struct neighbor_report *nei;
-
- nei = &wpa_s->wnm_neighbor_report_elements[i];
- if (nei->preference_present && nei->preference == 0)
- continue;
-
- for (j = 0; j < scan_res->num; j++) {
- struct wpa_scan_res *res;
- const u8 *ssid_ie;
-
- res = scan_res->res[j];
- if (!ether_addr_equal(nei->bssid, res->bssid) ||
- res->age > WNM_SCAN_RESULT_AGE * 1000)
- continue;
- bss = wpa_s->current_bss;
- ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
- if (bss && ssid_ie && ssid_ie[1] &&
- (bss->ssid_len != ssid_ie[1] ||
- os_memcmp(bss->ssid, ssid_ie + 2,
- bss->ssid_len) != 0))
- continue; /* Skip entries for other ESSs */
-
- /* Potential candidate found */
- found = 1;
- scan_snr(res);
- scan_est_throughput(wpa_s, res);
- wpa_bss_update_scan_res(wpa_s, res,
- &scan_res->fetch_time);
- }
- }
-
- wpa_scan_results_free(scan_res);
- if (!found) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: No transition candidate matches existing scan results");
- return 0;
- }
-
- bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
- if (!bss) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: Comparison of scan results against transition candidates did not find matches");
- return 0;
- }
-
- /* Associate to the network */
- wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
- return 1;
-}
-
-
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
const u8 *pos, const u8 *end,
int reply)
@@ -1445,8 +1394,6 @@
return;
#ifdef CONFIG_MBO
- wpa_s->wnm_mbo_trans_reason_present = 0;
- wpa_s->wnm_mbo_transition_reason = 0;
wpa_s->wnm_mbo_cell_pref_present = 0;
wpa_s->wnm_mbo_cell_preference = 0;
wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
@@ -1458,6 +1405,8 @@
else
beacon_int = 100; /* best guess */
+ wnm_btm_reset(wpa_s);
+
wpa_s->wnm_dialog_token = pos[0];
wpa_s->wnm_mode = pos[1];
wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
@@ -1477,8 +1426,7 @@
"WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
wpa_s->reject_btm_req_reason);
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token,
- wpa_s->reject_btm_req_reason,
+ wpa_s, wpa_s->reject_btm_req_reason,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
return;
}
@@ -1541,15 +1489,34 @@
* set to 1, and the BSS Termination Included field is set to 1, only
* one of the links is removed and the other links remain associated.
* Ignore the Disassociation Imminent field in such a case.
+ *
+ * TODO: We should check if the AP has more than one link.
+ * TODO: We should pass the RX link and use that
*/
- if (disassoc_imminent &&
- (wpa_s->valid_links & (wpa_s->valid_links - 1)) != 0 &&
+ if (disassoc_imminent && wpa_s->valid_links &&
(wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
(wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
- wpa_printf(MSG_INFO,
- "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
- disassoc_imminent = false;
+ /* If we still have a link, then just accept the request */
+ if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
+ wpa_printf(MSG_INFO,
+ "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
+ disassoc_imminent = false;
+
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
+
+ return;
+ }
+
+ /* The last link is being removed (which must be the assoc link)
+ */
wpa_s->wnm_link_removal = true;
+ os_memcpy(wpa_s->wnm_dissoc_addr,
+ wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
+ ETH_ALEN);
+ } else {
+ os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ?
+ wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN);
}
if (disassoc_imminent) {
@@ -1566,7 +1533,6 @@
unsigned int valid_ms;
wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
- wnm_deallocate_memory(wpa_s);
wpa_s->wnm_neighbor_report_elements = os_calloc(
WNM_MAX_NEIGHBOR_REPORT,
sizeof(struct neighbor_report));
@@ -1614,8 +1580,7 @@
wpa_printf(MSG_DEBUG,
"WNM: Candidate list included bit is set, but no candidates found");
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token,
- WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+ wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
NULL);
return;
@@ -1625,8 +1590,7 @@
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,
+ wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
NULL);
return;
@@ -1643,35 +1607,21 @@
wpa_s->wnm_cand_valid_until.sec +=
wpa_s->wnm_cand_valid_until.usec / 1000000;
wpa_s->wnm_cand_valid_until.usec %= 1000000;
- os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
/*
- * Fetch the latest scan results from the kernel and check for
- * candidates based on those results first. This can help in
- * finding more up-to-date information should the driver has
- * done some internal scanning operations after the last scan
- * result update in wpa_supplicant.
- */
- if (wnm_fetch_scan_results(wpa_s) > 0)
+ * Try fetching the latest scan results from the kernel.
+ * This can help in finding more up-to-date information should
+ * the driver have done some internal scanning operations after
+ * the last scan result update in wpa_supplicant.
+ *
+ * It is not a new scan, this does not update the last_scan
+ * timestamp nor will it expire old BSSs.
+ */
+ wpa_supplicant_update_scan_results(wpa_s, NULL);
+ if (wnm_scan_process(wpa_s, true) > 0)
return;
-
- /*
- * Try to use previously received scan results, if they are
- * recent enough to use for a connection.
- */
- if (wpa_s->last_scan_res_used > 0) {
- struct os_reltime now;
-
- os_get_reltime(&now);
- if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
- wpa_printf(MSG_DEBUG,
- "WNM: Try to use recent scan results");
- if (wnm_scan_process(wpa_s, 0) > 0)
- return;
- wpa_printf(MSG_DEBUG,
- "WNM: No match in previous scan results - try a new scan");
- }
- }
+ wpa_printf(MSG_DEBUG,
+ "WNM: No valid match in previous scan results - try a new scan");
wnm_set_scan_freqs(wpa_s);
if (wpa_s->wnm_num_neighbor_report == 1) {
@@ -1694,12 +1644,45 @@
status = WNM_BSS_TM_REJECT_UNSPECIFIED;
}
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token, status,
+ wpa_s, status,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
}
}
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t data_len)
+{
+ const struct ieee80211_mgmt *frame =
+ (const struct ieee80211_mgmt *) data;
+
+ if (data_len <
+ IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
+ frame->u.action.category != WLAN_ACTION_WNM ||
+ frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
+ frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
+ return -1;
+
+ /*
+ * If disassoc imminent bit was set in the request, the response may
+ * indicate accept even if no candidate was found, so bail out here.
+ */
+ if (!wpa_s->wnm_target_bss) {
+ wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
+ return 0;
+ }
+
+ if (!wpa_s->current_ssid)
+ return 0;
+
+ wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
+ 0);
+
+ wpa_s->wnm_target_bss = NULL;
+ return 0;
+}
+
+
#define BTM_QUERY_MIN_SIZE 4
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
@@ -2052,14 +2035,14 @@
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
struct wpabuf *elems)
{
- wpabuf_free(wpa_s->coloc_intf_elems);
if (elems && wpabuf_len(elems) == 0) {
wpabuf_free(elems);
elems = NULL;
}
- wpa_s->coloc_intf_elems = elems;
- if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+ /* NOTE: The elements are not stored as they are only send out once */
+
+ if (wpa_s->conf->coloc_intf_reporting && elems &&
wpa_s->coloc_intf_dialog_token &&
(wpa_s->coloc_intf_auto_report == 1 ||
wpa_s->coloc_intf_auto_report == 3)) {
@@ -2068,8 +2051,10 @@
*/
wnm_send_coloc_intf_report(wpa_s,
wpa_s->coloc_intf_dialog_token,
- wpa_s->coloc_intf_elems);
+ elems);
}
+
+ wpabuf_free(elems);
}
@@ -2082,8 +2067,6 @@
bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
- unsigned int i;
-
if (!(wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))
return false;
@@ -2091,26 +2074,14 @@
* In case disassociation imminent is set, do no try to use a BSS to
* which we are connected.
*/
-
- if (wpa_s->current_bss &&
- ether_addr_equal(wpa_s->current_bss->bssid, bss->bssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: Disassociation imminent: current BSS");
- return true;
- }
-
- if (!wpa_s->valid_links)
- return false;
-
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
- if (ether_addr_equal(wpa_s->links[i].bssid, bss->bssid)) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: MLD: Disassociation imminent: current link");
+ if (wpa_s->wnm_link_removal ||
+ !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) ||
+ is_zero_ether_addr(bss->mld_addr)) {
+ if (ether_addr_equal(bss->bssid, wpa_s->wnm_dissoc_addr))
return true;
- }
+ } else {
+ if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_dissoc_addr))
+ return true;
}
return false;
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 2a473db..235a838 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -65,19 +65,28 @@
const char *btm_candidates,
int cand_list);
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
const struct wpabuf *elems);
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
struct wpabuf *elems);
-bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t data_len);
#ifdef CONFIG_WNM
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check);
void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s);
+bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+
+void wnm_btm_reset(struct wpa_supplicant *wpa_s);
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+ return !!wpa_s->wnm_dialog_token;
+}
+
#else /* CONFIG_WNM */
static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
@@ -90,6 +99,21 @@
{
}
+static inline bool
+wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return false;
+}
+
+static inline void wnm_btm_reset(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+ return false;
+}
+
#endif /* CONFIG_WNM */
#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index b1334e2..60f8562 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -5129,7 +5129,7 @@
eloop_register_signal_terminate(wpa_cli_terminate, NULL);
- if (ctrl_ifname == NULL)
+ if (!ctrl_ifname && !global)
ctrl_ifname = wpa_cli_get_default_ifname();
if (reconnect && action_file && ctrl_ifname) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 31a9af6..88f3f2a 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -187,7 +187,10 @@
int val;
size_t i;
- res = iface->driver->get_scan_results2(iface->drv_priv);
+ if (iface->driver->get_scan_results)
+ res = iface->driver->get_scan_results(iface->drv_priv, NULL);
+ else
+ res = iface->driver->get_scan_results2(iface->drv_priv);
if (res == NULL)
goto fail;
@@ -231,7 +234,7 @@
if (iface->drv_priv == NULL)
return;
- if (iface->driver->get_scan_results2)
+ if (iface->driver->get_scan_results || iface->driver->get_scan_results2)
wpa_priv_get_scan_results2(iface, from, fromlen);
else
sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a851024..3fde0a8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -678,9 +678,7 @@
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
-#ifdef CONFIG_WNM
- wnm_deallocate_memory(wpa_s);
-#endif /* CONFIG_WNM */
+ wnm_btm_reset(wpa_s);
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
@@ -894,7 +892,7 @@
struct wpa_scan_results *scan_res;
wpa_s->bgscan_ssid = wpa_s->current_ssid;
scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
- 0);
+ 0, NULL);
if (scan_res) {
bgscan_notify_scan(wpa_s, scan_res);
wpa_scan_results_free(scan_res);
@@ -1081,6 +1079,10 @@
if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
wpa_supplicant_start_autoscan(wpa_s);
+ if (state == WPA_COMPLETED || state == WPA_INTERFACE_DISABLED ||
+ state == WPA_INACTIVE)
+ wnm_btm_reset(wpa_s);
+
#ifndef CONFIG_NO_WMM_AC
if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
wmm_ac_notify_disassoc(wpa_s);
@@ -2522,6 +2524,7 @@
#endif /* CONFIG_NO_WMM_AC */
#ifdef CONFIG_WNM
wpa_s->wnm_mode = 0;
+ wpa_s->wnm_target_bss = NULL;
#endif /* CONFIG_WNM */
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
@@ -2919,7 +2922,8 @@
if (obss_scan) {
struct wpa_scan_results *scan_res;
- scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0,
+ NULL);
if (scan_res == NULL) {
/* Back to HT20 */
freq->sec_channel_offset = 0;
@@ -3083,7 +3087,7 @@
freq->sec_channel_offset,
chwidth, seg0, seg1, vht_caps,
&mode->he_capab[ieee80211_mode],
- &mode->eht_capab[ieee80211_mode]) != 0)
+ &mode->eht_capab[ieee80211_mode], 0) != 0)
return false;
*freq = vht_freq;
@@ -3831,10 +3835,14 @@
if (ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
+ struct multi_ap_params multi_ap = { 0 };
+
+ multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+ multi_ap.profile = ssid->multi_ap_profile;
multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len,
- MULTI_AP_BACKHAUL_STA);
+ &multi_ap);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
@@ -9173,8 +9181,7 @@
if (modes[i].mode != mode ||
!modes[i].num_channels || !modes[i].channels)
continue;
- if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) ||
- (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq)))
+ if (is_6ghz == modes[i].is_6ghz)
return &modes[i];
}
@@ -9413,17 +9420,21 @@
struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s)
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_scan_results *scan_res;
#ifdef CONFIG_TESTING_OPTIONS
size_t idx;
#endif /* CONFIG_TESTING_OPTIONS */
- if (!wpa_s->driver->get_scan_results2)
+ if (wpa_s->driver->get_scan_results)
+ scan_res = wpa_s->driver->get_scan_results(wpa_s->drv_priv,
+ bssid);
+ else if (wpa_s->driver->get_scan_results2)
+ scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+ else
return NULL;
- scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
#ifdef CONFIG_TESTING_OPTIONS
for (idx = 0; scan_res && idx < scan_res->num; idx++) {
@@ -9461,10 +9472,7 @@
if (!wpa_s->valid_links)
return false;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
if (ether_addr_equal(wpa_s->links[i].bssid, addr))
return true;
}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d689441..89c5d03 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1717,6 +1717,12 @@
# support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be
# added to a bridge to allow forwarding frames over this backhaul link.
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 3a5f61c..0fea5cc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1327,14 +1327,14 @@
u8 wnm_num_neighbor_report;
u8 wnm_mode;
bool wnm_link_removal;
+ u8 wnm_dissoc_addr[ETH_ALEN];
u16 wnm_dissoc_timer;
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
- u8 wnm_cand_from_bss[ETH_ALEN];
+ struct wpa_bss *wnm_target_bss;
enum bss_trans_mgmt_status_code bss_tm_status;
bool bss_trans_mgmt_in_progress;
- struct wpabuf *coloc_intf_elems;
u8 coloc_intf_dialog_token;
u8 coloc_intf_auto_report;
u8 coloc_intf_timeout;
@@ -1500,6 +1500,9 @@
int dpp_netrole;
int dpp_auth_ok_on_ack;
int dpp_in_response_listen;
+ bool dpp_tx_auth_resp_on_roc_stop;
+ bool dpp_tx_chan_change;
+ bool dpp_listen_on_tx_expire;
int dpp_gas_client;
int dpp_gas_server;
int dpp_gas_dialog_token;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 111680c..2fea640 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -465,7 +465,7 @@
/* No WPA/RSN IE found in the cached scan results. Try to get updated
* scan results from the driver. */
- if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+ if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
return -1;
return wpa_get_beacon_ie(wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 81e11e7..f103237 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1208,14 +1208,19 @@
}
}
#endif /* CONFIG_P2P */
- os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
- multi_ap_backhaul_sta ? " multi_ap=1" : "");
+ if (multi_ap_backhaul_sta)
+ os_snprintf(phase1, sizeof(phase1), "pbc=1 multi_ap=%d",
+ multi_ap_backhaul_sta);
+ else
+ os_snprintf(phase1, sizeof(phase1), "pbc=1");
if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
return -1;
if (wpa_s->wps_fragment_size)
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
- if (multi_ap_backhaul_sta)
+ if (multi_ap_backhaul_sta) {
ssid->multi_ap_backhaul_sta = 1;
+ ssid->multi_ap_profile = multi_ap_backhaul_sta;
+ }
wpa_s->supp_pbc_active = true;
wpa_s->wps_overlap = false;
wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);