[wpa_supplicant] cumilative patch from commit 4b755c967
Bug: 329004037
Test: Connect to open, WPA2, WPA3 and OWE
Test: Establish P2P connection
Test: Basic SoftAp tests
Test: Ran above tests on Pixel6
Test: Regression test (b/329003970)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source
4b755c967 build: De-duplicate _DIRS before calling mkdir
9a4423645 hostapd: Only attempt to set QoS map if supported by the driver
dec6fccf1 Support qos_map_set without CONFIG_INTERWORKING
8634e7343 mesh: Allow processing authentication frames in blocked state
a210fdb1c nl80211: Rewrite neigh code to not depend on libnl3-route
3ef057901 ndisc_snoop: Call dl_list_del() before freeing IPv6 addresses
e1cd3fe3c Cancel channel_list_update_timeout() in hostapd_cleanup_iface_partial()
47d7f3169 nl80211: Update drv->ifindex on removing the first BSS
1be706e86 hostapd: Add RRM link measurement request support
92fdb49b2 AP MLD: Set DTIM information properly in per-STA profile
36bd75dfd hostapd: Fix channel switch to a DFS channel
f4b84ecaf AP MLD: Track radar detection in offloaded DFS case
aaf879ef2 AP MLD: Do not update other links' RNR element if not enabled yet
32261721e nl80211: AP MLD: Parse link ID to determine the BSS for radar event
216cfd708 AP MLD: Fix missing check for legacy client case
d5e6f7998 AP MLD: Request Handle OBSS scan for a specific link
c9ad16870 AP MLD: Allow scan processing link to match the request
9b682e72d AP MLD: Find the link that is waiting for scan events
147f83692 PASN: Add set and get API for PASN data context
ab37a5731 Replace PMKSA cache inline stubs with wrapper function stubs
ba55088a7 Replace PTKSA cache inline stubs with wrapper function stubs
1f230a497 MBSSID: Include Extended Capabilities element in non-TX BSSID profile
37c00c3c5 AP MLD: Provide link addresses for non-AP MLDs in control interface
b818a1be1 Add a QCA vendor attribute to set avoid frequencies per netdev
9ac0e785c Revert "nl80211: Skip interface down/up when setting MAC address"
94506e8ed Use the latest updated BSS entry for sending ANQP requests
a9bc6e89d OpenSSL: Fix a memory leak in CMAC
4bc61b657 AP MLD: Remove restriction of having to disable the first link BSS last
a6d92da9a AP MLD: Support removal of link station from AP
1f88b3daf nl80211: Add callback function for removing link STAs
19e50f862 Export hostapd_sta_is_link_sta()
df34c2ced AP MLD: De-initialize/disable link BSS properly
63982fd09 nl80211: Print the MLD capabilities in debug
9fdbaf2ed AP MLD: Fix advertisement of MLD capabilities
7a0501d20 AP MLD: Refresh beacons for other links when one gets disabled/enabled
d2b62b3fe AP MLD: Support link removal before removing interface
55c30e8ab nl80211: Remove AP MLD links while removing the interface
a576180cd nl80211: Use per-BSS command for remove link
b162886fd nl80211: Re-factor nl80211_remove_links() function
b810426ea nl80211: Remove redundant put_freq call in set_ap() for AP MLD
420065733 nl80211: Fix set_ap() to add frequency without CONFIG_IEEE80211AX
f2f0dd354 nl80211: Cache hostapd_data context in per link BSS struct for AP MLD
60e1dca1e AP MLD: Clean up MLD when not required any further
fac34688a AP MLD: Assign link ID during BSS creation
b19aa9c42 AP MLD: Use MLD struct for MLD level information
2f0e5303e AP MLD: Add a separate MLD level structure
259b43a31 hostapd: MLO: Avoid use of mld_id as user configuration
69d53b8b6 nl80211: Fix potential NULL pointer dereference in set_ap()
666e954ca Remove unused wpa_drv_set_ap()
9be122d2e nl80211: Fix AP MLD frequency update on channel switch
9144f876a nl80211: Fix sending NL80211_CMD_DEL_BEACON command to wrong interface
ec4b755b0 wpa_cli: Don't select interface when using global socket
c24453dd9 Add a vendor attribute per MLO link ratemask bitmap configuration
77f39ed23 Document vendor command ratemask bitmap for EHT case
58017de69 Add QCA vendor sub-command and attribute for spectral scan completion
8f9da72d2 Add QCA vendor attribute indicating the spectral transport mode
5b4a78b1f Optimize internal BSS table updates based on a specific BSSID
8d0bd7f9c Update BSS entry on roaming only for actual BSS frequency change
024d4bca1 Multi-AP: WPS support for different Multi-AP profiles
69d086298 Multi-AP: Add support for VLAN related information
210c2b4bd Multi-AP: Add hostapd config option to disallow certain profiles
9a1512532 Multi-AP: Reject non-Multi-AP STA association on backhaul-only BSS
420afbdbd Multi-AP: Allow supported profile to be configured
c3e528653 Multi-AP: Parse Profile subelement
003411242 Multi-AP: Generation of Multi-AP Profile subelement
364cb7c94 Multi-AP: Parse the Multi-AP element using a shared helper function
0e2ca2e4e Multi-AP: Use proper length for remaining buffer for the element
61e46f860 Multi-AP: Move IE parameters into a struct for extensibility
2ae1e6f18 DBus: Add ANQP fields to BSS properties
2ea04435e DBus: Signal ANQP query done
d71c83851 DBus: Add a method to get ANQP fields
5eb409c4b DBus: Add dict helper for uint16 arrays
a438e5293 OpenSSL: Fix a memory leak on hpke_labeled_expand() error path
b35b1036f OpenSSL: Fix a memory leak on openssl_evp_pkey_ec_prime_len() error path
35df7ee09 DPP: Emit a DPP PB_STATUS event when push button starts
69dd408fb EHT: More accurate no-second-channel-offset checks when puncturing
131ee5926 EHT: Support punct_bitmap overriding in HE element generation
c96c3adc3 Move punct_update_legacy_bw() into src/common
9f43c1e26 Provide punct_bitmap to hostapd_set_freq_params()
47dad1ed1 EHT: Move puncturing bitmap determination into a helper function
010d8d10e EHT: Use eht_oper_puncturing_override when constructing VHT elements
c00abc69f Handle 6 GHz channels in Supported Operating Classes with freq_list
9e90486bc 2-octet operating classes in Support Operating Classes element
5dabc1018 Extend support for the 6 GHz operating class 137 (320 MHz)
e74d95e0a nl80211: Process 6 GHz regulatory rules to accurate channel flags
59951ebf0 Use a helper function to free neighbor DB entries
96f0af07e Clear all neighbor entry items explicitly
6f285fbaf Update own report in nr_db if SSID is changed
b653420a2 AP MLD: Set link address only when non-AP MLD is not added to driver
16abdac80 Fix INTERFACES command buffer size to allow more data
b483ceafc hostapd: Dump VHT/HE/EHT full capabilities in STA command output
040ba112a Use os_snprintf_error() more consistently in STA output generation
31bbc9391 Do not change out-of-range configuration parameters
fd24ed949 Fix valid range for disable_ht40
b1a880f38 Enforce valid range check for SET mbo_cell_capa and oce
e5b7e5b90 wpa_supplicant: Fix ignoring boundary 0 in config parser
10122e951 P2P: Fix pri/sec channel switch skipping for GO
e508c070c WNM: Keep BTM information until connection completes
17a2aa822 WNM: Follow BTM procedure if the last link is dropped
770f3cb30 WNM: Remove dialog_token parameter
c7cafef8a WNM: Set wnm_reply to 0 when sending it
261f7f9e5 WNM: Do not store coloc_intf_elems
033adbf83 WNM: Drop explicit wpa_is_bss_tmp_disallowed() check
8b51310f4 WNM: Drop explicit SSID check
939cd294b WNM: Drop check for current_ssid
436f07d02 WNM: Define a stub wnm_is_bss_excluded if WNM is disabled
e164943f4 WNM: Wait for BTM response TX status before roaming
40ef706e5 WNM: Don't scan frequencies of neighbors that should be ignored
20ed289a7 WNM: Clean up old scan data processing
4750a4f62 DPP: Wait for TX wait expiration on moving to neg_freq for Auth Resp
963dbad7d nl80211: Indicate EVENT_TX_WAIT_EXPIRE on match-saved
451d29952 DPP: Wait for ROC cancelled event on Auth Resp TX on another channel
45fffac0f BSS: Switch struct wpa_bss to use valid_links bitmask
e90f6678f nl80211: Remnove unused struct i802_link ctx
0d4288a00 nl80211: Use valid_links bitmask for bss->links array
9ed51186e Use a single define MAX_NUM_MLD_LINKS for the maximum number of links
11f26fed6 Use for_each_link() where possible
dbdf7ef67 Use for_each_link() in most cases
c9f8fe066 common: Introduce for_each_link() macro
6cb421c1f nl80211: Fix link indexing in nl80211_connect_common()
408a399aa nl80211: Explicitly differentiate between 5 GHz and 6 GHz modes
dbcf9ff15 P2P: Notify the IP address of the connected P2P Client
b18d95759 P2P: Disable pri/sec channel switch for GO with forced frequency
0e4aa28da hostapd_cli: Indentation cleanup
a01972a06 Add "stop_ap" command for hostapd_cli
8cdb0d3f2 AP MLD: Stop AP per link
d084ef36b AP MLD: Clean up disassoc handling for non-AP MLD link validity check
7ceafb6e9 AP MLD: Handle disassociation notification with SME offload to driver
95a825bc4 RADIUS: Preliminary support RADIUS/TLS as an alternative to RADIUS/UDP
87f33c26b RADIUS: Simplify IPv4/IPv6 socket handling in client
971b78147 RADIUS: Simplify radius_change_server() parameters
3386e1327 l2_packet_freebsd: Fix macOS build
86c242171 TDLS: Defer the start request until the discovery response RX for MLO
352ad5f1a Apply CHAN_SWITCH in all BSS for MBSSID case
87120a5b6 Add QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER command
fe82a61ef Add QCA vendor attribute for BTM support configuration
Change-Id: I11d4eeb2e4ab44630e3359a2886361b3fccd7ead
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;
};