From rmekonnen at chromium.org Mon Apr 1 11:06:58 2024 From: rmekonnen at chromium.org (Ruth Mekonnen) Date: Mon, 1 Apr 2024 18:06:58 +0000 Subject: [PATCH] Emit AuthBSS property Message-ID: <20240401180747.1635441-1-rmekonnen@chromium.org> This CL adds wpa_s->auth_bss and the getter AuthBSS to expose the BSS the STA is trying to connect to at the AUTHENTICATING state for SME devices and at the ASSOCIATING state for non-SME devices. When the state is set to ASSOCIATED or DISCONNECTED wpa_s->auth_bss set to null and emitted. The AuthBSS property can be used by platforms to track connection attempt success rates against APs. Signed-off-by: Ruth Mekonnen --- wpa_supplicant/dbus/dbus_new.c | 8 ++++++++ wpa_supplicant/dbus/dbus_new.h | 1 + wpa_supplicant/dbus/dbus_new_handlers.c | 27 +++++++++++++++++++++++++ wpa_supplicant/dbus/dbus_new_handlers.h | 1 + wpa_supplicant/notify.c | 8 ++++++++ wpa_supplicant/notify.h | 1 + wpa_supplicant/sme.c | 2 ++ wpa_supplicant/wpa_supplicant.c | 7 +++++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 9 files changed, 56 insertions(+) diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 8bd6a9a43..2ed62350d 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -2362,6 +2362,9 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_PROP_STATE: prop = "State"; break; + case WPAS_DBUS_PROP_AUTH_BSS: + prop = "AuthBSS"; + break; case WPAS_DBUS_PROP_CURRENT_BSS: prop = "CurrentBSS"; break; @@ -3830,6 +3833,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL, NULL }, + { "AuthBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", + wpas_dbus_getter_auth_bss, + NULL, + NULL + }, { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", wpas_dbus_getter_current_network, NULL, diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 952bb422a..0eaefade9 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -27,6 +27,7 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_SCANNING, WPAS_DBUS_PROP_STATE, WPAS_DBUS_PROP_CURRENT_BSS, + WPAS_DBUS_PROP_AUTH_BSS, WPAS_DBUS_PROP_CURRENT_NETWORK, WPAS_DBUS_PROP_CURRENT_AUTH_MODE, WPAS_DBUS_PROP_BSSS, diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 3897d98f4..2052ccef4 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -4170,6 +4170,33 @@ dbus_bool_t wpas_dbus_getter_current_bss( &bss_obj_path, error); } +/** + * wpas_dbus_getter_auth_bss - Get auth bss object path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "AuthBSS" property. + */ +dbus_bool_t wpas_dbus_getter_auth_bss( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; + + if (wpa_s->auth_bss && wpa_s->dbus_new_path) + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, wpa_s->auth_bss->id); + else + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &bss_obj_path, error); +} + /** * wpas_dbus_getter_current_network - Get current network object path diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index acd6af7ff..642697091 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -189,6 +189,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname); DECLARE_ACCESSOR(wpas_dbus_setter_bridge_ifname); DECLARE_ACCESSOR(wpas_dbus_getter_config_file); DECLARE_ACCESSOR(wpas_dbus_getter_current_bss); +DECLARE_ACCESSOR(wpas_dbus_getter_auth_bss); DECLARE_ACCESSOR(wpas_dbus_getter_current_network); DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode); DECLARE_ACCESSOR(wpas_dbus_getter_bsss); diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 3ea1cbfaf..f3fbc8530 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -212,6 +212,14 @@ void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s) wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS); } +void wpas_notify_auth_bssid_changed(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AUTH_BSS); +} + void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s) { diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index d560d0b9b..1faefa982 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -35,6 +35,7 @@ void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_auth_bssid_changed(struct wpa_supplicant *wpa_s); void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s); void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s); void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index a7ad5ecf4..8fb4e7b99 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1142,6 +1142,8 @@ no_fils: eapol_sm_notify_portValid(wpa_s->eapol, false); wpa_clear_keys(wpa_s, bss->bssid); wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + wpa_s->auth_bss = bss; + wpas_notify_auth_bssid_changed(wpa_s); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index b9938d8dd..106f73286 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1083,6 +1083,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); + if (wpa_s->auth_bss && (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_ASSOCIATED)) { + wpa_s->auth_bss = NULL; + wpas_notify_auth_bssid_changed(wpa_s); + } /* * Notify the P2P Device interface about a state change in one @@ -4195,6 +4200,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->current_ssid = ssid; wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); + wpa_s->auth_bss = bss; + wpas_notify_auth_bssid_changed(wpa_s); if (bss) { params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index b90d92a69..8ff6e2a07 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -725,6 +725,7 @@ struct wpa_supplicant { * before this has been cleared */ struct wpa_ssid *current_ssid; struct wpa_ssid *last_ssid; + struct wpa_bss *auth_bss; struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; -- 2.44.0.478.gd926399ef9-goog From satyaprakash.developer.unix at gmail.com Mon Apr 1 16:55:07 2024 From: satyaprakash.developer.unix at gmail.com (Satya Prakash Prasad) Date: Tue, 2 Apr 2024 05:25:07 +0530 Subject: EAP TLS - TLSv1.2 Record Layer: Alert, Level: Fatal, Description: Certificate Unknown, 46 Message-ID: Hi, I am not sure if this questions should be addressed to this support team but in hope that some positive information might come up. I am trying to analyze an SSL handshake failure issue. Based on the issue please find below steps to create client / server certificates where currently I am receiving Description: Certificate Unknown (46) error - note I am running EAP-TLS [https://github.com/championswimmer/kernel_sony_tamsui/tree/master/platform/external/hostap-06] code as client and hostapd daemon as server. I am not sure if I have generated the certificates correctly - but I am trying to test a Mutual trusted Server / Client SSL connection. So there is no certificate chain I have made during their certificate creation - they are self-signed ones. Note that when asked about the CN I gave "CA" (for CA), "example.com" (for server) and "client" (for client). openssl genrsa -out ca.key 2048 openssl req -new -x509 -days 1826 -key ca.key -out ca.crt openssl genrsa -out server.key 2048 openssl req -new -out server.csr -key server.key openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 360 At this step I have below files: ca.crt (which I use as trusted_client.pem), server.crt and server.key at server side Client Side certificate generation: openssl genrsa -out client.key 2048 openssl req -out client.csr -key client.key -new openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 360 So now at client side I have below files: client.crt client.key trusted_client.pem [generated during Server certificate step] When I run the flow I get below error: TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Unknown CA) Wireshark logs: 103 2024-04-01 11:17:42.886627 Device_00:8c:94 Nearest-non-TPMR-bridge EAPOL 60 Start 104 2024-04-01 11:17:42.887165 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge EAP 60 Request, Identity 105 2024-04-01 11:17:45.890174 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge EAP 60 Request, Identity 106 2024-04-01 11:17:45.892093 Device_00:8c:94 Nearest-non-TPMR-bridge EAP 60 Response, Identity 107 2024-04-01 11:17:45.892425 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge EAP 60 Request, TLS EAP (EAP-TLS) 108 2024-04-01 11:17:47.732072 Device_00:8c:94 Nearest-non-TPMR-bridge TLSv1.2 226 Client Hello 109 2024-04-01 11:17:47.746814 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge EAP 1421 Request, TLS EAP (EAP-TLS) 110 2024-04-01 11:17:47.750570 Device_00:8c:94 Nearest-non-TPMR-bridge EAP 60 Response, TLS EAP (EAP-TLS) 111 2024-04-01 11:17:47.750881 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge SSL 1068 Continuation Data 112 2024-04-01 11:17:49.896020 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge SSL 1068 Continuation Data 113 2024-04-01 11:17:50.104051 Device_00:8c:94 Nearest-non-TPMR-bridge TLSv1.2 233 Client Hello, Alert (Level: Fatal, Description: Certificate Unknown) -- Description: Certificate Unknown (46) 114 2024-04-01 11:17:50.104413 MS-NLB-PhysServer-17_11:11:11:11 Nearest-non-TPMR-bridge EAP 60 Failure Server Hello, Certificate, Server Key Exchange, Certificate Request, Server Hello Done Frame 111: 1068 bytes on wire (8544 bits), 1068 bytes captured (8544 bits) on interface \Device\NPF_{87758CCA-2149-4961-9FDA-E59432A16D13}, id 0 Ethernet II, Src: MS-NLB-PhysServer-17_11:11:11:11 (02:11:11:11:11:11), Dst: Nearest-non-TPMR-bridge (01:80:c2:00:00:03) 802.1X Authentication Extensible Authentication Protocol Code: Request (1) Id: 56 Length: 1050 Type: TLS EAP (EAP-TLS) (13) EAP-TLS Flags: 0x00 0... .... = Length Included: False .0.. .... = More Fragments: False ..0. .... = Start: False [2 EAP-TLS Fragments (2437 bytes): #109(1393), #111(1044)] [Frame: 109, payload: 0-1392 (1393 bytes)] [Frame: 111, payload: 1393-2436 (1044 bytes)] [Fragment Count: 2] [Reassembled EAP-TLS Length: 2437] Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Server Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 61 Handshake Protocol: Server Hello Handshake Type: Server Hello (2) Length: 57 Version: TLS 1.2 (0x0303) Random: e8497a7739576c02beabbb0b95a6b95f026ba3bc167b4992af22b64fb10f1e8b GMT Unix Time: Jun 29, 2093 21:29:51.000000000 India Standard Time Random Bytes: 39576c02beabbb0b95a6b95f026ba3bc167b4992af22b64fb10f1e8b Session ID Length: 0 Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8) Compression Method: null (0) Extensions Length: 17 Extension: renegotiation_info (len=1) Type: renegotiation_info (65281) Length: 1 Renegotiation Info extension Extension: ec_point_formats (len=4) Type: ec_point_formats (11) Length: 4 EC point formats Length: 3 Elliptic curves point formats (3) EC point format: uncompressed (0) EC point format: ansiX962_compressed_prime (1) EC point format: ansiX962_compressed_char2 (2) Extension: extended_master_secret (len=0) Type: extended_master_secret (23) Length: 0 [JA3S Fullstring: 771,52392,65281-11-23] [JA3S: d7d95b173b904a8f4de65bd751cb534a] TLSv1.2 Record Layer: Handshake Protocol: Certificate Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 1793 Handshake Protocol: Certificate Handshake Type: Certificate (11) Length: 1789 Certificates Length: 1786 Certificates (1786 bytes) TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 401 Handshake Protocol: Server Key Exchange Handshake Type: Server Key Exchange (12) Length: 397 EC Diffie-Hellman Server Params TLSv1.2 Record Layer: Handshake Protocol: Certificate Request Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 153 Handshake Protocol: Certificate Request Handshake Type: Certificate Request (13) Length: 149 Certificate types count: 3 Certificate types (3 types) Signature Hash Algorithms Length: 40 Signature Hash Algorithms (20 algorithms) Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503) Signature Hash Algorithm Hash: SHA384 (5) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: ecdsa_secp521r1_sha512 (0x0603) Signature Hash Algorithm Hash: SHA512 (6) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: ed25519 (0x0807) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (7) Signature Algorithm: ed448 (0x0808) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (8) Signature Algorithm: rsa_pss_pss_sha256 (0x0809) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (9) Signature Algorithm: rsa_pss_pss_sha384 (0x080a) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (10) Signature Algorithm: rsa_pss_pss_sha512 (0x080b) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (11) Signature Algorithm: rsa_pss_rsae_sha256 (0x0804) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: SM2 (4) Signature Algorithm: rsa_pss_rsae_sha384 (0x0805) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (5) Signature Algorithm: rsa_pss_rsae_sha512 (0x0806) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (6) Signature Algorithm: rsa_pkcs1_sha256 (0x0401) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: rsa_pkcs1_sha384 (0x0501) Signature Hash Algorithm Hash: SHA384 (5) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: rsa_pkcs1_sha512 (0x0601) Signature Hash Algorithm Hash: SHA512 (6) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: SHA224 ECDSA (0x0303) Signature Hash Algorithm Hash: SHA224 (3) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: SHA224 RSA (0x0301) Signature Hash Algorithm Hash: SHA224 (3) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: SHA224 DSA (0x0302) Signature Hash Algorithm Hash: SHA224 (3) Signature Hash Algorithm Signature: DSA (2) Signature Algorithm: SHA256 DSA (0x0402) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: DSA (2) Signature Algorithm: SHA384 DSA (0x0502) Signature Hash Algorithm Hash: SHA384 (5) Signature Hash Algorithm Signature: DSA (2) Signature Algorithm: SHA512 DSA (0x0602) Signature Hash Algorithm Hash: SHA512 (6) Signature Hash Algorithm Signature: DSA (2) Distinguished Names Length: 101 Distinguished Names (101 bytes) TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 4 Handshake Protocol: Server Hello Done Handshake Type: Server Hello Done (14) Length: 0 Client Hello, Alert (Level: Fatal, Description: Certificate Unknown) Extensible Authentication Protocol Code: Response (2) Id: 56 Length: 215 Type: TLS EAP (EAP-TLS) (13) EAP-TLS Flags: 0x00 Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 197 Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Length: 193 Version: TLS 1.2 (0x0303) Random: 259ea02b1870ac3618e57b7cbdf4a4ad7df085bf1180f24c52141c38f640cdac Session ID Length: 0 Cipher Suites Length: 80 Cipher Suites (40 suites) Compression Methods Length: 1 Compression Methods (1 method) Extensions Length: 72 Extension: signature_algorithms (len=22) Extension: supported_groups (len=24) Extension: ec_point_formats (len=2) Extension: encrypt_then_mac (len=0) Extension: extended_master_secret (len=0) Extension: session_ticket (len=0) [JA4: 12i400600_9479543b8654_7b0ba9b4cf08] [JA4_r [truncated]: 12i400600_002f,0033,0035,0039,003c,003d,0067,006b,009c,009d,009e,009f,00ff,c009,c00a,c013,c014,c023,c024,c027,c028,c02b,c02c,c02f,c030,c09c,c09d,c09e,c09f,c0a0,c0a1,c0a2,c0a3,c0ac,c0ad,c0ae,c0af,cca8,cca9,ccaa_000a,000b,] [JA3 Fullstring [truncated]: 771,52392-52393-52394-49196-49200-159-49325-49311-49188-49192-107-49162-49172-57-49327-49315-49195-49199-158-49324-49310-49187-49191-103-49161-49171-51-49326-49314-157-49309-61-53-49313-156-49308-60-47-49312-255] [JA3: fee1630eb5b7688c9f8303364702933f] TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Certificate Unknown) Content Type: Alert (21) Version: TLS 1.2 (0x0303) Length: 2 Alert Message Level: Fatal (2) Description: Certificate Unknown (46) Regards, Prakash From nbd at nbd.name Thu Apr 4 03:52:11 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:11 +0200 Subject: [PATCH 1/7] driver_nl80211: update drv->ifindex on removing the first BSS Message-ID: <20240404105217.12478-1-nbd@nbd.name> Otherwise it will point at the ifindex of the just removed BSS Signed-off-by: Felix Fietkau --- src/drivers/driver_nl80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 4949de5772a7..863497e8fe86 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -8989,6 +8989,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, 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"); -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:15 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:15 +0200 Subject: [PATCH 5/7] mesh: allow processing authentication frames in blocked state In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-5-nbd@nbd.name> If authentication fails repeatedly e.g. because of a weak signal, the link can end up in blocked state. If one of the nodes tries to establish a link again before it is unblocked on the other side, it will block the link to that other side. The same happens on the other side when it unblocks the link. In that scenario, the link never recovers on its own. To fix this, allow restarting authentication even if the link is in blocked state, but don't initiate the attempt until the blocked period is over. Signed-off-by: Felix Fietkau --- src/ap/ieee802_11.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 5a3132de45ef..ff1b1fcff44d 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -3049,15 +3049,6 @@ static void handle_auth(struct hostapd_data *hapd, 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)) { -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:12 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:12 +0200 Subject: [PATCH 2/7] hostapd: cancel channel_list_update_timeout in hostapd_cleanup_iface_partial In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-2-nbd@nbd.name> Fixes a crash when disabling an interface during channel list update Signed-off-by: Felix Fietkau --- src/ap/hostapd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index f8cb6432d007..7ea65d654db3 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -685,6 +685,7 @@ static void sta_track_deinit(struct hostapd_iface *iface) 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 */ @@ -714,7 +715,6 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) 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); -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:14 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:14 +0200 Subject: [PATCH 4/7] driver_nl80211: rewrite neigh code to not depend on libnl3-route In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-4-nbd@nbd.name> Removes an unnecessary dependency and also makes the code smaller Signed-off-by: Felix Fietkau --- src/drivers/driver_nl80211.c | 148 ++++++++++++++--------------------- src/drivers/drivers.mak | 1 - src/drivers/drivers.mk | 1 - 3 files changed, 58 insertions(+), 92 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 863497e8fe86..5ee5e6c69475 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -18,9 +18,6 @@ #include #include #include -#ifdef CONFIG_LIBNL3_ROUTE -#include -#endif /* CONFIG_LIBNL3_ROUTE */ #include #include #include @@ -5858,26 +5855,29 @@ fail: 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) + goto errout; + + if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr)) + goto errout; + + if (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), @@ -5887,9 +5887,8 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) MACSTR, MAC2STR(addr)); } - nl_addr_put(nl_addr); - rtnl_neigh_put(rn); -#endif /* CONFIG_LIBNL3_ROUTE */ +errout: + nlmsg_free(msg); } @@ -8614,7 +8613,6 @@ static void *i802_init(struct hostapd_data *hapd, (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; @@ -8631,7 +8629,6 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } } -#endif /* CONFIG_LIBNL3_ROUTE */ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) { wpa_printf(MSG_DEBUG, @@ -12146,13 +12143,14 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version, 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) @@ -12171,85 +12169,66 @@ static int wpa_driver_br_add_ip_neigh(void *priv, u8 version, } 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) 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"); + + if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr)) 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; + if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr)) 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 = nl_send_auto_complete(drv->rtnl_sk, msg); + if (res < 0) + goto errout; - 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; @@ -12267,41 +12246,30 @@ static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version, 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) goto errout; - } - res = rtnl_neigh_set_dst(rn, nl_ipaddr); - if (res) { - wpa_printf(MSG_DEBUG, - "nl80211: neigh set destination addr failed"); + + if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr)) goto errout; - } - rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + res = nl_send_auto_complete(drv->rtnl_sk, msg); + if (res < 0) + goto errout; - 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 */ } diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index a03d4a034511..6b452c64e165 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -156,7 +156,6 @@ ifdef CONFIG_FULL_DYNAMIC_VLAN 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 10eab6a92e17..de4cb99de172 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -151,7 +151,6 @@ ifdef CONFIG_FULL_DYNAMIC_VLAN NEED_LINUX_IOCTL=y ifdef CONFIG_VLAN_NETLINK NEED_LIBNL=y -CONFIG_LIBNL3_ROUTE=y endif endif -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:13 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:13 +0200 Subject: [PATCH 3/7] ndisc_snoop: call dl_list_del before freeing ipv6 addresses In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-3-nbd@nbd.name> Fixes a segmentation fault on sta disconnect Signed-off-by: Felix Fietkau --- src/ap/ndisc_snoop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c index 788c12fdc1cf..bc1eb6251ac0 100644 --- a/src/ap/ndisc_snoop.c +++ b/src/ap/ndisc_snoop.c @@ -61,6 +61,7 @@ void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) 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); } } -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:16 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:16 +0200 Subject: [PATCH 6/7] hostapd: support qos_map_set without CONFIG_INTERWORKING In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-6-nbd@nbd.name> This feature is useful on its own even without full interworking support Signed-off-by: Felix Fietkau --- hostapd/config_file.c | 6 +++--- src/ap/hostapd.c | 2 +- src/ap/ieee802_11_shared.c | 2 -- wpa_supplicant/events.c | 6 ------ 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 56b2df3ae09c..a1b4d0eb8852 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1678,6 +1678,8 @@ static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) return 0; } +#endif /* CONFIG_INTERWORKING */ + static int parse_qos_map_set(struct hostapd_bss_config *bss, char *buf, int line) @@ -1719,8 +1721,6 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss, return 0; } -#endif /* CONFIG_INTERWORKING */ - #ifdef CONFIG_HS20 static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, @@ -4258,10 +4258,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->gas_frag_limit = val; } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); +#endif /* CONFIG_INTERWORKING */ } else if (os_strcmp(buf, "qos_map_set") == 0) { if (parse_qos_map_set(bss, pos, line) < 0) return 1; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_RADIUS_TEST } else if (os_strcmp(buf, "dump_msk_file") == 0) { os_free(bss->dump_msk_file); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 7ea65d654db3..643a023ce97d 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1584,6 +1584,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, 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, @@ -1591,7 +1592,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, 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"); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index a5716f0379b5..85790c7ed600 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -1138,13 +1138,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) 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/wpa_supplicant/events.c b/wpa_supplicant/events.c index ca27946384b6..e58fc12e9ef5 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2935,8 +2935,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) } -#ifdef CONFIG_INTERWORKING - static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, size_t len) { @@ -2969,8 +2967,6 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, } } -#endif /* CONFIG_INTERWORKING */ - static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s) { @@ -3349,10 +3345,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_WNM */ -#ifdef CONFIG_INTERWORKING interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); -#endif /* CONFIG_INTERWORKING */ if (wpa_s->hw_capab == CAPAB_VHT && get_ie(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) -- 2.43.0 From nbd at nbd.name Thu Apr 4 03:52:17 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 12:52:17 +0200 Subject: [PATCH 7/7] hostapd: only attempt to set qos map if supported by the driver In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: <20240404105217.12478-7-nbd@nbd.name> Fixes issues with full-mac drivers like brcmfmac Signed-off-by: Felix Fietkau --- src/ap/ap_drv_ops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 0d493b837c9b..11fe39c2599f 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -1023,7 +1023,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, 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); -- 2.43.0 From nbd at nbd.name Thu Apr 4 04:06:04 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 13:06:04 +0200 Subject: [PATCH] build: make _make_dirs robust against too long argument error In-Reply-To: <20240325095149.194489-1-robimarko@gmail.com> References: <20240325095149.194489-1-robimarko@gmail.com> Message-ID: On 25.03.24 10:51, Robert Marko wrote: > _make_dirs currently can fail as _DIRS can be really long and thus go over > the MAX_ARG_STRLEN limit so it will fail with: > /bin/sh: Argument list too long > > Lets avoid this by stripping the $(BUILDDIR) prefix and then restoring it. > > Signed-off-by: Robert Marko > --- > src/build.rules | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/src/build.rules b/src/build.rules > index acda88472..497580d88 100644 > --- a/src/build.rules > +++ b/src/build.rules > @@ -80,7 +80,7 @@ endif > _DIRS := $(BUILDDIR)/$(PROJ) > .PHONY: _make_dirs > _make_dirs: > - @mkdir -p $(_DIRS) > + @printf '$(BUILDDIR)/%s ' $(patsubst $(BUILDDIR)/%,%,$(_DIRS)) | xargs mkdir -p It's simpler and more robust to simply do: @mkdir -p $(sort $(_DIRS)) - Felix From robimarko at gmail.com Thu Apr 4 05:46:14 2024 From: robimarko at gmail.com (Robert Marko) Date: Thu, 4 Apr 2024 14:46:14 +0200 Subject: [PATCH] build: make _make_dirs robust against too long argument error In-Reply-To: References: <20240325095149.194489-1-robimarko@gmail.com> Message-ID: On Thu, 4 Apr 2024 at 13:06, Felix Fietkau wrote: > > On 25.03.24 10:51, Robert Marko wrote: > > _make_dirs currently can fail as _DIRS can be really long and thus go over > > the MAX_ARG_STRLEN limit so it will fail with: > > /bin/sh: Argument list too long > > > > Lets avoid this by stripping the $(BUILDDIR) prefix and then restoring it. > > > > Signed-off-by: Robert Marko > > --- > > src/build.rules | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/src/build.rules b/src/build.rules > > index acda88472..497580d88 100644 > > --- a/src/build.rules > > +++ b/src/build.rules > > @@ -80,7 +80,7 @@ endif > > _DIRS := $(BUILDDIR)/$(PROJ) > > .PHONY: _make_dirs > > _make_dirs: > > - @mkdir -p $(_DIRS) > > + @printf '$(BUILDDIR)/%s ' $(patsubst $(BUILDDIR)/%,%,$(_DIRS)) | xargs mkdir -p > > It's simpler and more robust to simply do: > @mkdir -p $(sort $(_DIRS)) I agree, do you want to send that version? Regards, Robert > > - Felix From nbd at nbd.name Thu Apr 4 06:03:27 2024 From: nbd at nbd.name (Felix Fietkau) Date: Thu, 4 Apr 2024 15:03:27 +0200 Subject: [PATCH] build: de-duplicate _DIRS before calling mkdir Message-ID: <20240404130327.80024-1-nbd@nbd.name> If the build path is long, the contents of the _DIRS variable can be very long, since it repeats the same directories very often. In some cases, this has triggered an "Argument list too long" build error. Reported-by: Robert Marko Suggested-by: Eneas U de Queiroz Signed-off-by: Felix Fietkau --- src/build.rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.rules b/src/build.rules index acda8847284d..c756ccb84e8c 100644 --- a/src/build.rules +++ b/src/build.rules @@ -80,7 +80,7 @@ endif _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) $< -- 2.43.0 From juliusz at wolfssl.com Thu Apr 4 11:16:07 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:07 +0200 Subject: [PATCH 01/24] wolfssl: simplify tls_get_cipher Message-ID: <20240404181630.2431991-1-juliusz@wolfssl.com> It appears that wolfSSL_get_cipher_name returns the ciphersuite in the format expected by hostapd. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 0b2947daf9..4db23e14ff 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1924,34 +1924,16 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - WOLFSSL_CIPHER *cipher; const char *name; if (!conn || !conn->ssl) return -1; - cipher = wolfSSL_get_current_cipher(conn->ssl); - if (!cipher) - return -1; - - name = wolfSSL_CIPHER_get_name(cipher); + name = wolfSSL_get_cipher_name(conn->ssl); if (!name) return -1; - if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0) - os_strlcpy(buf, "RC4-SHA", buflen); - else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "ADH-AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0) - os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen); - else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0) - os_strlcpy(buf, "AES256-SHA", buflen); - else - os_strlcpy(buf, name, buflen); + os_strlcpy(buf, name, buflen); return 0; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:08 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:08 +0200 Subject: [PATCH 02/24] wolfssl: implement suiteb ciphersuite In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-2-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 166 +++++++++++++++++++++++++++++------- tests/hwsim/test_suite_b.py | 31 +++++-- 2 files changed, 162 insertions(+), 35 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 4db23e14ff..25616f2c7a 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -223,11 +223,127 @@ static void wolfSSL_logging_cb(const int log_level, #endif /* DEBUG_WOLFSSL */ +#define SUITEB_OLDTLS_192_CIPHERS "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384" +#define SUITEB_TLS13_192_CIPHERS "TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256" +#define SUITEB_TLS_192_CIPHERS SUITEB_TLS13_192_CIPHERS ":" SUITEB_OLDTLS_192_CIPHERS + +#define SUITEB_OLDTLS_128_CIPHERS SUITEB_OLDTLS_192_CIPHERS ":ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256" +#define SUITEB_TLS13_128_CIPHERS SUITEB_TLS13_192_CIPHERS ":TLS13-AES128-GCM-SHA256" +#define SUITEB_TLS_128_CIPHERS SUITEB_TLS13_128_CIPHERS ":" SUITEB_OLDTLS_128_CIPHERS + +#define SUITEB_TLS_192_SIGALGS "ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384" +#define SUITEB_TLS_128_SIGALGS SUITEB_TLS_192_SIGALGS ":ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256" + +#define SUITEB_TLS_192_CURVES "P-384:P-521" +#define SUITEB_TLS_128_CURVES "P-256:" SUITEB_TLS_192_CURVES + +static int handle_ciphersuites(WOLFSSL_CTX* ssl_ctx, WOLFSSL* ssl, + const char* openssl_ciphers, unsigned int flags) +{ + const char* ciphers = "DEFAULT:!aNULL"; + const char* sigalgs = NULL; + const char* curves = NULL; + unsigned int tls13 = !(flags & TLS_CONN_DISABLE_TLSv1_3); + unsigned int tls13OnlyMask = TLS_CONN_DISABLE_TLSv1_2 | + TLS_CONN_DISABLE_TLSv1_1 | TLS_CONN_DISABLE_TLSv1_0; + unsigned int oldTlsOnly = ((flags & tls13OnlyMask) != tls13OnlyMask) && !tls13; + unsigned int tls13only = ((flags & tls13OnlyMask) == tls13OnlyMask) && + !(flags & TLS_CONN_DISABLE_TLSv1_3); + short keySz = 0; + short eccKeySz = 0; + if (openssl_ciphers) { + if (os_strcmp(openssl_ciphers, "SUITEB128") == 0) { + if (tls13only) + ciphers = SUITEB_TLS13_128_CIPHERS; + else if (oldTlsOnly) + ciphers = SUITEB_OLDTLS_128_CIPHERS; + else + ciphers = SUITEB_TLS_128_CIPHERS; + sigalgs = SUITEB_TLS_128_SIGALGS; + keySz = 2048; + eccKeySz = 224; + curves = SUITEB_TLS_128_CURVES; + } + else if (os_strcmp(openssl_ciphers, "SUITEB192") == 0) { + if (tls13only) + ciphers = SUITEB_TLS13_192_CIPHERS; + else if (oldTlsOnly) + ciphers = SUITEB_OLDTLS_192_CIPHERS; + else + ciphers = SUITEB_TLS_192_CIPHERS; + sigalgs = SUITEB_TLS_192_SIGALGS; + keySz = 3072; + eccKeySz = 256; + curves = SUITEB_TLS_192_CURVES; + } + else + ciphers = openssl_ciphers; + } + else if (flags & TLS_CONN_SUITEB) { + if (tls13only) + ciphers = SUITEB_TLS13_192_CIPHERS; + else if (oldTlsOnly) + ciphers = SUITEB_OLDTLS_192_CIPHERS; + else + ciphers = SUITEB_TLS_192_CIPHERS; + sigalgs = SUITEB_TLS_192_SIGALGS; + keySz = 3072; + eccKeySz = 256; + curves = SUITEB_TLS_192_CURVES; + } + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites for %s", ssl_ctx ? "ctx" : "ssl"); + wpa_printf(MSG_DEBUG, "wolfSSL: openssl_ciphers: %s", openssl_ciphers ? openssl_ciphers : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers ? ciphers : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: sigalgs: %s", sigalgs ? sigalgs : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: key size: %d", keySz); + if (ciphers) { + if ((ssl_ctx && wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) || + (ssl && wolfSSL_set_cipher_list(ssl, ciphers) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set cipher string '%s'", ciphers); + return -1; + } + } + if (sigalgs) { + if ((ssl_ctx && wolfSSL_CTX_set1_sigalgs_list(ssl_ctx, sigalgs) != 1) || + (ssl && wolfSSL_set1_sigalgs_list(ssl, sigalgs) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set sigalgs '%s'", sigalgs); + return -1; + } + } + if (keySz) { + if ((ssl_ctx && wolfSSL_CTX_SetMinRsaKey_Sz(ssl_ctx, keySz) != 1) || + (ssl && wolfSSL_SetMinRsaKey_Sz(ssl, keySz) != 1) || + (ssl_ctx && wolfSSL_CTX_SetMinDhKey_Sz(ssl_ctx, keySz) != 1) || + (ssl && wolfSSL_SetMinDhKey_Sz(ssl, keySz) != 1)) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set min key size"); + return -1; + } + } + if (eccKeySz) { + if ((ssl_ctx && wolfSSL_CTX_SetMinEccKey_Sz(ssl_ctx, eccKeySz) != 1) || + (ssl && wolfSSL_SetMinEccKey_Sz(ssl, eccKeySz) != 1) || + (ssl_ctx && wolfSSL_CTX_SetTmpEC_DHE_Sz(ssl_ctx, eccKeySz/8) != 1) || + (ssl && wolfSSL_SetTmpEC_DHE_Sz(ssl, eccKeySz/8) != 1)) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set min ecc key size"); + return -1; + } + } + if (curves) { + if ((ssl_ctx && wolfSSL_CTX_set1_curves_list(ssl_ctx, curves) != 1) || + (ssl && wolfSSL_set1_curves_list(ssl, curves) != 1)) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set curves"); + return -1; + } + } + return 0; +} + void * tls_init(const struct tls_config *conf) { WOLFSSL_CTX *ssl_ctx; struct tls_context *context; - const char *ciphers; #ifdef DEBUG_WOLFSSL wolfSSL_SetLoggingCb(wolfSSL_logging_cb); @@ -280,19 +396,14 @@ void * tls_init(const struct tls_config *conf) WOLFSSL_SESS_CACHE_OFF); } - if (conf && conf->openssl_ciphers) - ciphers = conf->openssl_ciphers; - else - ciphers = "ALL"; - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers); - if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { - wpa_printf(MSG_ERROR, - "wolfSSL: Failed to set cipher string '%s'", - ciphers); + if (handle_ciphersuites(ssl_ctx, NULL, conf->openssl_ciphers, + conf ? conf->tls_flags : 0) != 0) { + wpa_printf(MSG_INFO, "Error setting ciphersuites"); tls_deinit(ssl_ctx); return NULL; } + return ssl_ctx; } @@ -819,6 +930,8 @@ static enum tls_fail_reason wolfssl_tls_fail_reason(int err) case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_CERT_REJECTED: return TLS_FAIL_BAD_CERTIFICATE; + case RSA_KEY_SIZE_E: + return TLS_FAIL_INSUFFICIENT_KEY_LEN; default: return TLS_FAIL_UNSPECIFIED; } @@ -1324,13 +1437,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", - params->openssl_ciphers ? params->openssl_ciphers : "N/A"); - if (params->openssl_ciphers && - wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { - wpa_printf(MSG_INFO, - "wolfSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + if (handle_ciphersuites(NULL, conn->ssl, params->openssl_ciphers, + params->flags) != 0) { + wpa_printf(MSG_INFO, "Error setting ciphersuites"); return -1; } @@ -1556,14 +1665,9 @@ int tls_global_set_params(void *tls_ctx, return -1; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", - params->openssl_ciphers ? params->openssl_ciphers : "N/A"); - if (params->openssl_ciphers && - wolfSSL_CTX_set_cipher_list(tls_ctx, - params->openssl_ciphers) != 1) { - wpa_printf(MSG_INFO, - "wolfSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + if (handle_ciphersuites(tls_ctx, NULL, params->openssl_ciphers, + params->flags) != 0) { + wpa_printf(MSG_INFO, "Error setting ciphersuites"); return -1; } @@ -1870,7 +1974,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, if (!conn || !conn->ssl || !ciphers) return -1; - buf[0] = '\0'; + buf[0] = buf[1] = '\0'; pos = buf; end = pos + sizeof(buf); @@ -1910,9 +2014,8 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, c++; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1); - - if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) { + /* +1 to skip the ":" */ + if (handle_ciphersuites(NULL, conn->ssl, buf + 1, conn->flags) != 0) { wpa_printf(MSG_DEBUG, "Cipher suite configuration failed"); return -1; } @@ -1929,7 +2032,10 @@ int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, if (!conn || !conn->ssl) return -1; - name = wolfSSL_get_cipher_name(conn->ssl); + if (wolfSSL_version(conn->ssl) == TLS1_3_VERSION) + name = wolfSSL_get_cipher(conn->ssl); + else + name = wolfSSL_get_cipher_name(conn->ssl); if (!name) return -1; diff --git a/tests/hwsim/test_suite_b.py b/tests/hwsim/test_suite_b.py index d03a39deef..59e255a3c6 100644 --- a/tests/hwsim/test_suite_b.py +++ b/tests/hwsim/test_suite_b.py @@ -74,7 +74,8 @@ def test_suite_b(dev, apdev): hapd.wait_sta() tls_cipher = dev[0].get_status_field("EAP TLS cipher") if tls_cipher != "ECDHE-ECDSA-AES128-GCM-SHA256" and \ - tls_cipher != "ECDHE-ECDSA-AES-128-GCM-AEAD": + tls_cipher != "ECDHE-ECDSA-AES-128-GCM-AEAD" and \ + tls_cipher != "ECDHE-ECDSA-AES256-GCM-SHA384": raise Exception("Unexpected TLS cipher: " + tls_cipher) bss = dev[0].get_bss(apdev[0]['bssid']) @@ -488,7 +489,16 @@ def test_suite_b_192_rsa_insufficient_key(dev, apdev): params["ca_cert"] = "auth_serv/ca.pem" params["server_cert"] = "auth_serv/server.pem" params["private_key"] = "auth_serv/server.key" - hapd = hostapd.add_ap(apdev[0], params) + + try: + hapd = hostapd.add_ap(apdev[0], params) + except: + hapd = hostapd.add_ap(apdev[0], suite_b_192_rsa_ap_params()) + tls = hapd.request("GET tls_library") + if tls.startswith("wolfSSL"): + # wolfSSL fails during key loading with too short key + return + raise dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192", ieee80211w="2", @@ -505,6 +515,8 @@ def test_suite_b_192_rsa_insufficient_key(dev, apdev): raise Exception("Certificate error not reported") if "reason=11" in ev and "err='Insufficient RSA modulus size'" in ev: return + if "reason=11" in ev and "err='RSA key too small'" in ev: + return if "reason=7" in ev and "err='certificate uses insecure algorithm'" in ev: return raise Exception("Unexpected error reason: " + ev) @@ -516,7 +528,16 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev): params = suite_b_192_rsa_ap_params() params["tls_flags"] = "[SUITEB-NO-ECDH]" params["dh_file"] = "auth_serv/dh.conf" - hapd = hostapd.add_ap(apdev[0], params) + try: + hapd = hostapd.add_ap(apdev[0], params) + except: + hapd = hostapd.add_ap(apdev[0], suite_b_192_rsa_ap_params()) + tls = hapd.request("GET tls_library") + if tls.startswith("wolfSSL"): + # wolfSSL fails during key loading with too short key + return + raise + dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192", ieee80211w="2", @@ -528,14 +549,14 @@ def test_suite_b_192_rsa_insufficient_dh(dev, apdev): pairwise="GCMP-256", group="GCMP-256", scan_freq="2412", wait_connect=False) ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='local TLS alert'", - "CTRL-EVENT-CONNECTED"], + "CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"], timeout=10) dev[0].request("DISCONNECT") if ev is None: raise Exception("DH error not reported") if "CTRL-EVENT-CONNECTED" in ev: raise Exception("Unexpected connection") - if "insufficient security" not in ev and "internal error" not in ev: + if "insufficient security" not in ev and "internal error" not in ev and "authentication failed" not in ev: raise Exception("Unexpected error reason: " + ev) def test_suite_b_192_rsa_radius(dev, apdev): -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:09 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:09 +0200 Subject: [PATCH 03/24] wolfssl: use defines for ex_data access In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-3-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 25616f2c7a..26832ed430 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -42,7 +42,9 @@ static int tls_ref_count = 0; -static int tls_ex_idx_session = 0; +#define TLS_SESSION_EX_IDX (0) +#define TLS_SSL_CTX_CTX_EX_IDX (0) +#define TLS_SSL_CON_EX_IDX (0) /* tls input data for wolfSSL Read Callback */ @@ -184,7 +186,7 @@ static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) { struct wpabuf *buf; - buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + buf = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); if (!buf) return; wpa_printf(MSG_DEBUG, @@ -192,7 +194,7 @@ static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) buf, sess); wpabuf_free(buf); - wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); + wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, NULL); } @@ -380,7 +382,7 @@ void * tls_init(const struct tls_config *conf) wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); context->tls_session_lifetime = conf->tls_session_lifetime; - wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context); + wolfSSL_CTX_set_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX, context); if (conf->tls_session_lifetime > 0) { wolfSSL_CTX_set_session_id_context(ssl_ctx, @@ -410,7 +412,8 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { - struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, + TLS_SSL_CTX_CTX_EX_IDX); if (context != tls_global) os_free(context); @@ -462,8 +465,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); - wolfSSL_set_ex_data(conn->ssl, 0, conn); - conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + wolfSSL_set_ex_data(conn->ssl, TLS_SSL_CON_EX_IDX, conn); + conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX); /* Need randoms post-hanshake for EAP-FAST, export key and deriving * session ID in EAP methods. */ @@ -1109,7 +1112,7 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf, sizeof(buf)); - conn = wolfSSL_get_ex_data(ssl, 0); + conn = wolfSSL_get_ex_data(ssl, TLS_SSL_CON_EX_IDX); if (!conn) { wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data"); return 0; @@ -1733,7 +1736,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, wolfSSL_set_accept_state(conn->ssl); - context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, 0); + context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, + TLS_SSL_CTX_CTX_EX_IDX); if (context && context->tls_session_lifetime == 0) { /* * Set session id context to a unique value to make sure @@ -2361,13 +2365,13 @@ void tls_connection_set_success_data(struct tls_connection *conn, goto fail; } - old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + old = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); if (old) { wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p", old); wpabuf_free(old); } - if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + if (wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, data) != 1) goto fail; wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data); @@ -2390,7 +2394,7 @@ tls_connection_get_success_data(struct tls_connection *conn) sess = wolfSSL_get_session(conn->ssl); if (!sess) return NULL; - return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + return wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:10 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:10 +0200 Subject: [PATCH 04/24] wolfssl: fix get_x509_cert In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-4-juliusz@wolfssl.com> The conditional was checking if data was not present. We should be allocating the buffer when data *is* present. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 26832ed430..e851dd09d1 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -961,7 +961,7 @@ static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) int cert_len; data = wolfSSL_X509_get_der(cert, &cert_len); - if (!data) + if (data) buf = wpabuf_alloc_copy(data, cert_len); return buf; -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:11 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:11 +0200 Subject: [PATCH 05/24] wolfssl: support tod policy In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-5-juliusz@wolfssl.com> - Implement wolfssl_cert_tod() to support setting the correct tod value in the certificate event message. - Always send the certificate event message in addition to error messages. This is the same order of messages that the OpenSSL backend sends. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 34 ++++++++++++++++++++++++++++++++-- tests/hwsim/utils.py | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index e851dd09d1..38575375de 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -992,6 +992,35 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn, wpabuf_free(cert); } +static int wolfssl_cert_tod(X509 *cert) +{ + WOLFSSL_STACK *ext; + int i; + char *buf; + int tod = 0; + + ext = wolfSSL_X509_get_ext_d2i(cert, CERT_POLICY_OID, NULL, NULL); + if (!ext) + return 0; + + for (i = 0; i < wolfSSL_sk_num(ext); i++) { + WOLFSSL_ASN1_OBJECT *policy; + + policy = wolfSSL_sk_value(ext, i); + if (!policy) + continue; + + buf = (char*)policy->obj; + wpa_printf(MSG_DEBUG, "wolfSSL: Certificate Policy %s", buf); + if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0) + tod = 1; /* TOD-STRICT */ + else if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.2") == 0 && !tod) + tod = 2; /* TOD-TOFU */ + } + wolfSSL_sk_pop_free(ext, NULL); + + return tod; +} static void wolfssl_tls_cert_event(struct tls_connection *conn, WOLFSSL_X509 *err_cert, int depth, @@ -1080,6 +1109,7 @@ static void wolfssl_tls_cert_event(struct tls_connection *conn, for (alt = 0; alt < num_alt_subject; alt++) ev.peer_cert.altsubject[alt] = alt_subject[alt]; ev.peer_cert.num_altsubject = num_alt_subject; + ev.peer_cert.tod = wolfssl_cert_tod(err_cert); context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); @@ -1185,6 +1215,8 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) } #endif /* CONFIG_SHA256 */ + wolfssl_tls_cert_event(conn, err_cert, depth, buf); + if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'", @@ -1232,8 +1264,6 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); - } else { - wolfssl_tls_cert_event(conn, err_cert, depth, buf); } if (conn->cert_probe && preverify_ok && depth == 0) { diff --git a/tests/hwsim/utils.py b/tests/hwsim/utils.py index 7e36082843..cd97c0175e 100644 --- a/tests/hwsim/utils.py +++ b/tests/hwsim/utils.py @@ -145,7 +145,7 @@ def check_imsi_privacy_support(dev): def check_tls_tod(dev): tls = dev.request("GET tls_library") - if not tls.startswith("OpenSSL") and not tls.startswith("internal"): + if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL") and not tls.startswith("internal"): raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls) def vht_supported(): -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:12 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:12 +0200 Subject: [PATCH 06/24] wolfssl: implement EAP-AKA In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-6-juliusz@wolfssl.com> Implement the crypto_rsa_key set of functions in the wolfSSL backend to enable EAP-AKA and EAP-SIM Signed-off-by: Juliusz Sosinowicz --- hostapd/Makefile | 1 + src/crypto/crypto_wolfssl.c | 256 ++++++++++++++++++++++++++++++++++++ wpa_supplicant/Makefile | 1 + 3 files changed, 258 insertions(+) diff --git a/hostapd/Makefile b/hostapd/Makefile index b3cb686734..2e69453443 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -708,6 +708,7 @@ CFLAGS += -DCONFIG_TLSV12 endif ifeq ($(CONFIG_TLS), wolfssl) +CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 CONFIG_CRYPTO=wolfssl ifdef TLS_FUNCS OBJS += ../src/crypto/tls_wolfssl.o diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c index 269174321b..74e25fab91 100644 --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -3554,6 +3555,261 @@ fail: #endif /* CONFIG_DPP */ +struct crypto_rsa_key { + RsaKey key; + WC_RNG *rng; +}; + +static struct crypto_rsa_key * crypto_rsa_key_init(void) +{ + struct crypto_rsa_key* ret; + int err; + ret = os_zalloc(sizeof(*ret)); + if (ret) { + err = wc_InitRsaKey(&ret->key, NULL); + if (err != MP_OKAY) { + LOG_WOLF_ERROR_FUNC(wc_InitRsaKey, err); + goto fail; + } + ret->rng = wc_rng_init(); + if (!ret->rng) { + LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init); + goto fail; + } + err = wc_RsaSetRNG(&ret->key, ret->rng); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaSetRNG, err); + goto fail; + } + } + return ret; +fail: + crypto_rsa_key_free(ret); + return NULL; +} + +void crypto_rsa_key_free(struct crypto_rsa_key *key) +{ + int err; + if (key) { + err = wc_FreeRsaKey(&key->key); + if (err != 0) + LOG_WOLF_ERROR_FUNC(wc_FreeRsaKey, err); + wc_rng_free(key->rng); + os_free(key); + } +} + +static void read_rsa_key_from_x509(unsigned char *keyPem, size_t keyPemLen, + DerBuffer** keyDer) +{ + struct DecodedCert cert; + DerBuffer* certDer = NULL; + word32 derKeySz = 0; + int err; + + err = wc_PemToDer(keyPem, (long)keyPemLen, CERT_TYPE, &certDer, + NULL, NULL, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + + wc_InitDecodedCert(&cert, certDer->buffer, certDer->length, NULL); + err = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + + err = wc_GetPubKeyDerFromCert(&cert, NULL, &derKeySz); + if (err != LENGTH_ONLY_E) { + LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err); + goto fail; + } + + if (*keyDer) + wc_FreeDer(keyDer); + *keyDer = NULL; + + err = wc_AllocDer(keyDer, derKeySz, PUBLICKEY_TYPE, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_AllocDer, err); + goto fail; + } + + err = wc_GetPubKeyDerFromCert(&cert, (*keyDer)->buffer, &(*keyDer)->length); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err); + goto fail; + } + +fail: + if (certDer) { + wc_FreeDecodedCert(&cert); + wc_FreeDer(&certDer); + } + /* caller is responsible for free'ing keyDer */ +} + +struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key) +{ + struct crypto_rsa_key* ret = NULL; + unsigned char *keyPem = NULL; + size_t keyPemLen = 0; + DerBuffer* keyDer = NULL; + int keyFormat = 0; + int err; + int success = 0; + word32 idx = 0; + + keyPem = (unsigned char*)os_readfile(file, &keyPemLen); + if (!keyPem) { + LOG_WOLF_ERROR_FUNC_NULL(os_readfile); + goto fail; + } + + err = wc_PemToDer(keyPem, (long)keyPemLen, private_key ? PRIVATEKEY_TYPE : + PUBLICKEY_TYPE, &keyDer, + NULL, NULL, &keyFormat); + if (err != 0) { + if (private_key) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + else { + /* input file might be public key or x509 cert we want to extract + * the key from */ + wpa_printf(MSG_DEBUG, + "wolfSSL: trying to extract key from x509 cert"); + read_rsa_key_from_x509(keyPem, keyPemLen, &keyDer); + if (!keyDer) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + LOG_WOLF_ERROR_FUNC_NULL(read_rsa_key_from_x509); + goto fail; + } + } + } + if (private_key && keyFormat != RSAk) { + LOG_WOLF_ERROR("Private key is not RSA key"); + goto fail; + } + + /* No longer needed so let's free the memory early */ + os_free(keyPem); + keyPem = NULL; + + ret = crypto_rsa_key_init(); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(crypto_rsa_key_init); + goto fail; + } + + if (private_key) + err = wc_RsaPrivateKeyDecode(keyDer->buffer, &idx, &ret->key, keyDer->length); + else + err = wc_RsaPublicKeyDecode(keyDer->buffer, &idx, &ret->key, keyDer->length); + + if (err != 0) { + if (private_key) + LOG_WOLF_ERROR_FUNC(wc_RsaPrivateKeyDecode, err); + else + LOG_WOLF_ERROR_FUNC(wc_RsaPublicKeyDecode, err); + goto fail; + } + + success = 1; +fail: + if (keyPem) + os_free(keyPem); + if (keyDer) + wc_FreeDer(&keyDer); + if (!success && ret) { + crypto_rsa_key_free(ret); + ret = NULL; + } + return ret; +} + +struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key, + const struct wpabuf *in) +{ + int err; + int success = 0; + struct wpabuf *ret = NULL; + + if (!key || !in) { + LOG_INVALID_PARAMETERS(); + return NULL; + } + + ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key)); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "wolfSSL: crypto_rsa_oaep_sha256_encrypt: wpabuf_len(in) %ld " + "wc_RsaEncryptSize(key->key) %d", wpabuf_len(in), wc_RsaEncryptSize(&key->key)); + + err = wc_RsaPublicEncrypt_ex(wpabuf_head_u8(in), wpabuf_len(in), + wpabuf_mhead_u8(ret), wpabuf_size(ret), &key->key, key->rng, + WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256, WC_MGF1SHA256, NULL, 0); + if (err <= 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err); + goto fail; + } + wpabuf_put(ret, err); + + success = 1; +fail: + if (!success && ret) { + wpabuf_free(ret); + ret = NULL; + } + return ret; +} + +struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key, + const struct wpabuf *in) +{ + int err; + int success = 0; + struct wpabuf *ret = NULL; + + if (!key || !in) { + LOG_INVALID_PARAMETERS(); + return NULL; + } + + ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key)); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "wolfSSL: crypto_rsa_oaep_sha256_decrypt: wpabuf_len(in) %ld " + "wc_RsaEncryptSize(key->key) %d", wpabuf_len(in), wc_RsaEncryptSize(&key->key)); + + err = wc_RsaPrivateDecrypt_ex(wpabuf_head_u8(in), wpabuf_len(in), + wpabuf_mhead_u8(ret), wpabuf_size(ret), &key->key, WC_RSA_OAEP_PAD, + WC_HASH_TYPE_SHA256, WC_MGF1SHA256, NULL, 0); + if (err <= 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err); + goto fail; + } + wpabuf_put(ret, err); + + success = 1; +fail: + if (!success && ret) { + wpabuf_free(ret); + ret = NULL; + } + return ret; +} void crypto_unload(void) { diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index dd13308f7b..3f75399eb9 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1188,6 +1188,7 @@ TLS_FUNCS=y endif ifeq ($(CONFIG_TLS), wolfssl) +CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ifdef TLS_FUNCS CFLAGS += -DWOLFSSL_DER_LOAD OBJS += ../src/crypto/tls_wolfssl.o -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:13 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:13 +0200 Subject: [PATCH 07/24] openssl: Use uncompressed format for ECC keys In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-7-juliusz@wolfssl.com> OpenSSL <3.0 uses ECC keys in the uncompressed format. We should keep it that way for compatibility. The wolfSSL backend also uses the uncompressed format. Signed-off-by: Juliusz Sosinowicz --- src/crypto/crypto_openssl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 2d8ff60aa9..e83a40b57c 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -3676,6 +3676,8 @@ struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key, 0); pkey = copy; } + EVP_PKEY_set_utf8_string_param(pkey, + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, "uncompressed"); ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER", "type-specific", NULL); -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:15 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:15 +0200 Subject: [PATCH 09/24] wolfssl: tune test_ap_wpa2_eap_fast_prf_oom for wolfssl In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-9-juliusz@wolfssl.com> The wolfSSL backend only does one malloc in tls_connection_get_eap_fast_key. Failing on the second one skips the only malloc and fails the test. Signed-off-by: Juliusz Sosinowicz --- tests/hwsim/test_ap_eap.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index a201403163..5fe2cbc711 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -4248,9 +4248,12 @@ def test_ap_wpa2_eap_fast_prf_oom(dev, apdev): """WPA2-Enterprise connection using EAP-FAST and OOM in PRF""" check_eap_capa(dev[0], "FAST") tls = dev[0].request("GET tls_library") - if tls.startswith("OpenSSL") or tls.startswith("wolfSSL"): + if tls.startswith("OpenSSL"): func = "tls_connection_get_eap_fast_key" count = 2 + elif tls.startswith("wolfSSL"): + func = "tls_connection_get_eap_fast_key" + count = 1 elif tls.startswith("internal"): func = "tls_connection_prf" count = 1 -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:14 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:14 +0200 Subject: [PATCH 08/24] wolfssl: Set additional sigalgs when using anon cipher In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-8-juliusz@wolfssl.com> When setting an anonymous cipher, wolfSSL would only set the anonymous signature algorithm. This sets some better defaults. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 38575375de..a58e1f7607 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -2004,6 +2004,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, char buf[128], *pos, *end; u8 *c; int ret; + int setSigAlgs = 0; if (!conn || !conn->ssl || !ciphers) return -1; @@ -2028,6 +2029,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, break; case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; + setSigAlgs = 1; break; case TLS_CIPHER_RSA_DHE_AES256_SHA: suite = "DHE-RSA-AES256-SHA"; @@ -2054,6 +2056,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, return -1; } + if (setSigAlgs && + wolfSSL_set1_sigalgs_list(conn->ssl, SUITEB_TLS_128_SIGALGS) != 1) { + wpa_printf(MSG_DEBUG, "Sigalg configuration failed"); + return -1; + } + return 0; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:16 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:16 +0200 Subject: [PATCH 10/24] ap_wpa2_eap_tls_rsa_and_ec: use ciphersuites that wolfSSL understands In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-10-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- tests/hwsim/test_ap_eap.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 5fe2cbc711..8f4b846f57 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -6406,7 +6406,12 @@ def test_ap_wpa2_eap_tls_rsa_and_ec(dev, apdev, params): private_key="auth_serv/ec-user.key") dev[0].request("REMOVE_NETWORK all") dev[0].wait_disconnected() - + + tls = dev[1].request("GET tls_library") + if tls.startswith("wolfSSL"): + ciphers = "RSA" + else: + ciphers = "DEFAULT:-aECDH:-aECDSA" # TODO: Make wpa_supplicant automatically filter out cipher suites that # would require ECDH/ECDSA keys when those are not configured in the # selected client certificate. And for no-client-cert case, deprioritize @@ -6414,7 +6419,7 @@ def test_ap_wpa2_eap_tls_rsa_and_ec(dev, apdev, params): # likely to work cipher suites are selected by the server. Only do these # when an explicit openssl_ciphers parameter is not set. eap_connect(dev[1], hapd, "TLS", "tls user", - openssl_ciphers="DEFAULT:-aECDH:-aECDSA", + openssl_ciphers=ciphers, ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem", private_key="auth_serv/user.key") @@ -6450,7 +6455,12 @@ def test_ap_wpa2_eap_tls_ec_and_rsa(dev, apdev, params): private_key="auth_serv/ec-user.key") dev[0].request("REMOVE_NETWORK all") dev[0].wait_disconnected() - + + tls = dev[1].request("GET tls_library") + if tls.startswith("wolfSSL"): + ciphers = "RSA" + else: + ciphers = "DEFAULT:-aECDH:-aECDSA" # TODO: Make wpa_supplicant automatically filter out cipher suites that # would require ECDH/ECDSA keys when those are not configured in the # selected client certificate. And for no-client-cert case, deprioritize @@ -6458,7 +6468,7 @@ def test_ap_wpa2_eap_tls_ec_and_rsa(dev, apdev, params): # likely to work cipher suites are selected by the server. Only do these # when an explicit openssl_ciphers parameter is not set. eap_connect(dev[1], hapd, "TLS", "tls user", - openssl_ciphers="DEFAULT:-aECDH:-aECDSA", + openssl_ciphers=ciphers, ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem", private_key="auth_serv/user.key") -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:17 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:17 +0200 Subject: [PATCH 11/24] ap_wpa2_eap_fast_cipher_suites: allow wolfSSL to skip RC4 test In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-11-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- tests/hwsim/test_ap_eap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 8f4b846f57..14f8980132 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -4341,7 +4341,7 @@ def test_ap_wpa2_eap_fast_cipher_suites(dev, apdev): if cipher == "RC4-SHA" and \ ("Could not select EAP method" in str(e) or \ "EAP failed" in str(e)): - if "run=OpenSSL" in tls: + if "run=OpenSSL" in tls or "wolfSSL" in tls: logger.info("Allow failure due to missing TLS library support") dev[0].request("REMOVE_NETWORK all") dev[0].wait_disconnected() -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:18 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:18 +0200 Subject: [PATCH 12/24] ap_wpa2_eap_tls_versions: run tests with wolfSSL In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-12-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- tests/hwsim/test_ap_eap.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 14f8980132..3350da7e4e 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -6205,11 +6205,8 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev): "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2") if tls.startswith("wolfSSL"): - if ("build=3.10.0" in tls and "run=3.10.0" in tls) or \ - ("build=3.13.0" in tls and "run=3.13.0" in tls): - check_tls_ver(dev[0], hapd, - "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", - "TLSv1.2") + check_tls_ver(dev[0], hapd, + "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2") elif tls.startswith("internal"): check_tls_ver(dev[0], hapd, "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2") @@ -6217,7 +6214,8 @@ def test_ap_wpa2_eap_tls_versions(dev, apdev): "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1") check_tls_ver(dev[2], hapd, "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1") - if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls: + if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3." in tls or \ + tls.startswith("wolfSSL"): check_tls_ver(dev[0], hapd, "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3") -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:20 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:20 +0200 Subject: [PATCH 14/24] wolfssl: remove unnecessary WOLFSSL_X509_STORE manipulation In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-14-juliusz@wolfssl.com> Setting a new WOLFSSL_X509_STORE is not necessary when calling wolfSSL_CTX_load_verify_locations(). Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 4016b6a46b..f126c37479 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1383,15 +1383,8 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, } if (ca_cert || ca_path) { - WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new(); - - if (!cm) { - wpa_printf(MSG_INFO, - "SSL: failed to create certificate store"); - return -1; - } - wolfSSL_CTX_set_cert_store(ctx, cm); - + wpa_printf(MSG_DEBUG, "SSL: loading CA's from '%s' and '%s'", + ca_cert ? ca_cert : "N/A", ca_path ? ca_path : "N/A"); if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) != SSL_SUCCESS) { wpa_printf(MSG_INFO, @@ -1408,6 +1401,7 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, return -1; } } + wpa_printf(MSG_DEBUG, "SSL: Loaded ca_cert or ca_path"); return 0; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:19 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:19 +0200 Subject: [PATCH 13/24] wolfssl: generate events when OCSP status is revoked In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-13-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index a58e1f7607..4016b6a46b 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1836,6 +1836,18 @@ static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, wolfSSL_ERR_error_string(err, msg)); conn->failed++; } + + /* Generate extra events */ + if (err == OCSP_CERT_REVOKED || err == BAD_CERTIFICATE_STATUS_ERROR || + err == OCSP_CERT_REVOKED) { + char buf[256]; + WOLFSSL_X509* err_cert = wolfSSL_get_peer_certificate(conn->ssl); + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), + buf, sizeof(buf)); + wolfssl_tls_fail_event(conn, err_cert, err, 0, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } } return conn->output.out_data; -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:21 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:21 +0200 Subject: [PATCH 15/24] wolfssl: log error number on failure In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-15-juliusz@wolfssl.com> Log raw error number after wolfSSL_accept or wolfSSL_connect error. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index f126c37479..360a4a3b48 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1824,9 +1824,9 @@ static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, char msg[80]; wpa_printf(MSG_DEBUG, - "SSL: %s - failed %s", + "SSL: %s - failed (%d) %s", server ? "wolfSSL_accept" : - "wolfSSL_connect", + "wolfSSL_connect", err, wolfSSL_ERR_error_string(err, msg)); conn->failed++; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:23 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:23 +0200 Subject: [PATCH 17/24] wolfssl: add missing return in tls_init In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-17-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 32331d25b8..0cdc4c809a 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -378,6 +378,7 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; } + return NULL; } wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:22 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:22 +0200 Subject: [PATCH 16/24] wolfssl: remove unused and non-compiling code In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-16-juliusz@wolfssl.com> OCSP is handled internally by wolfSSL. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 360a4a3b48..32331d25b8 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1275,31 +1275,6 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } -#ifdef HAVE_OCSP_WOLFSSL - if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && - preverify_ok) { - enum ocsp_result res; - - res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, - conn->peer_issuer, - conn->peer_issuer_issuer); - if (res == OCSP_REVOKED) { - preverify_ok = 0; - wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, - "certificate revoked", - TLS_FAIL_REVOKED); - if (err == X509_V_OK) - X509_STORE_CTX_set_error( - x509_ctx, X509_V_ERR_CERT_REVOKED); - } else if (res != OCSP_GOOD && - (conn->flags & TLS_CONN_REQUIRE_OCSP)) { - preverify_ok = 0; - wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, - "bad certificate status response", - TLS_FAIL_UNSPECIFIED); - } - } -#endif /* HAVE_OCSP_WOLFSSL */ if (depth == 0 && preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:25 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:25 +0200 Subject: [PATCH 19/24] wolfssl: Actually use ocsp_stapling_response In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-19-juliusz@wolfssl.com> Without a call to wolfSSL_CTX_EnableOCSP(tls_ctx, WOLFSSL_OCSP_URL_OVERRIDE); then the override URL would not be used. But since we don't actually want to enable OCSP in this step, disable it immediately after. The option will stay turned on. Fully turn on OCSP stapling and do error checking on all calls. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 44 ++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index b88e259e40..b6869b7488 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1872,16 +1872,48 @@ int tls_global_set_params(void *tls_ctx, #ifdef HAVE_SESSION_TICKET /* Session ticket is off by default - can't disable once on. */ - if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) - wolfSSL_CTX_UseSessionTicket(tls_ctx); + if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) { + if (wolfSSL_CTX_UseSessionTicket(tls_ctx) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseSessionTicket failed"); + return -1; + } + } #endif /* HAVE_SESSION_TICKET */ #ifdef HAVE_OCSP if (params->ocsp_stapling_response) { - wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, - params->ocsp_stapling_response); - wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, - ocsp_resp_free_cb, NULL); + if (wolfSSL_CTX_EnableOCSP(tls_ctx, + WOLFSSL_OCSP_URL_OVERRIDE) != WOLFSSL_SUCCESS || + /* Workaround to force using the override URL without enabling OCSP */ + wolfSSL_CTX_DisableOCSP(tls_ctx) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseOCSPStapling failed"); + return -1; + } + if (wolfSSL_CTX_UseOCSPStapling(tls_ctx, WOLFSSL_CSR_OCSP, + WOLFSSL_CSR_OCSP_USE_NONCE) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseOCSPStapling failed"); + return -1; + } + if (wolfSSL_CTX_EnableOCSPStapling(tls_ctx) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_EnableOCSPStapling failed"); + return -1; + } + if (wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, + params->ocsp_stapling_response) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_SetOCSP_OverrideURL failed"); + return -1; + } + if (wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, + ocsp_resp_free_cb, NULL) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_SetOCSP_Cb failed"); + return -1; + } } #endif /* HAVE_OCSP */ -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:28 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:28 +0200 Subject: [PATCH 22/24] wolfSSL: simplify option setting in tls_set_conn_flags In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-22-juliusz@wolfssl.com> Use one call to wolfSSL_set_options with all the relevant options already set. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 22f8d6eb78..8940de98d4 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1565,19 +1565,23 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags) { + long op = 0; #ifdef HAVE_SESSION_TICKET if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET)) wolfSSL_UseSessionTicket(ssl); #endif /* HAVE_SESSION_TICKET */ + wpa_printf(MSG_DEBUG, "SSL: conn_flags: %d", flags); + if (flags & TLS_CONN_DISABLE_TLSv1_0) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1); + op |= WOLFSSL_OP_NO_TLSv1; if (flags & TLS_CONN_DISABLE_TLSv1_1) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + op |= WOLFSSL_OP_NO_TLSv1_1; if (flags & TLS_CONN_DISABLE_TLSv1_2) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + op |= WOLFSSL_OP_NO_TLSv1_2; if (flags & TLS_CONN_DISABLE_TLSv1_3) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_3); + op |= WOLFSSL_OP_NO_TLSv1_3; + wolfSSL_set_options(ssl, op); } @@ -1947,6 +1951,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, return -1; wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer); + wpa_printf(MSG_DEBUG, "SSL: flags: %d", flags); if (verify_peer) { conn->ca_cert_verify = 1; @@ -1976,6 +1981,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, session_ctx_len); } + tls_set_conn_flags(conn->ssl, flags); + return 0; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:30 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:30 +0200 Subject: [PATCH 24/24] wolfSSL: test_ap_wpa2_eap_fast_server_oom In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-24-juliusz@wolfssl.com> wolfSSL sends the session ticket extension on the first TLS handshake. This causes the malloc error to be triggered on the first connection instead of the second and the failure event is not registered in the python test. Signed-off-by: Juliusz Sosinowicz --- tests/hwsim/test_ap_eap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 580660e592..9b1ca119b7 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -4277,6 +4277,9 @@ def test_ap_wpa2_eap_fast_prf_oom(dev, apdev): def test_ap_wpa2_eap_fast_server_oom(dev, apdev): """EAP-FAST/MSCHAPv2 and server OOM""" check_eap_capa(dev[0], "FAST") + tls = dev[0].request("GET tls_library") + if not tls.startswith("OpenSSL"): + raise HwsimSkip("TLS library is not OpenSSL: " + tls) params = int_eap_server_params() params['dh_file'] = 'auth_serv/dh.conf' -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:27 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:27 +0200 Subject: [PATCH 21/24] wolfssl: Implement EAP-FAST In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-21-juliusz@wolfssl.com> Add tls_session_ticket_ext_cb and use the new wolfSSL_set_session_ticket_ext_cb API. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 59 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index b6869b7488..22f8d6eb78 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -94,7 +94,8 @@ struct tls_connection { #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) tls_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; - byte session_ticket[SESSION_TICKET_LEN]; + u8 *session_ticket; + size_t session_ticket_len; #endif unsigned int ca_cert_verify:1; unsigned int cert_probe:1; @@ -513,6 +514,7 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) os_free(conn->domain_match); os_free(conn->peer_subject); os_free(conn->check_cert_subject); + os_free(conn->session_ticket); /* self */ os_free(conn); @@ -2481,32 +2483,58 @@ static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg) int ret; unsigned char client_random[RAN_LEN]; unsigned char server_random[RAN_LEN]; - word32 ticket_len = sizeof(conn->session_ticket); if (!conn || !conn->session_ticket_cb) - return 1; + return -1; + + wpa_printf(MSG_DEBUG, "wolfSSL: %s", __func__); if (wolfSSL_get_client_random(s, client_random, sizeof(client_random)) == 0 || wolfSSL_get_server_random(s, server_random, - sizeof(server_random)) == 0 || - wolfSSL_get_SessionTicket(s, conn->session_ticket, - &ticket_len) != 1) - return 1; - - if (ticket_len == 0) - return 0; + sizeof(server_random)) == 0) + return -1; ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, - conn->session_ticket, ticket_len, + conn->session_ticket, conn->session_ticket_len, client_random, server_random, secret); + + wpa_printf(MSG_DEBUG, "wolfSSL: %s conn->session_ticket_cb: %d", __func__, ret); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + if (ret <= 0) - return 1; + return -1; *secret_len = SECRET_LEN; return 0; } +static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, + int len, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "wolfSSL: %s: length=%d", __func__, len); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + wpa_hexdump(MSG_DEBUG, "wolfSSL: ClientHello SessionTicket " + "extension", data, len); + + conn->session_ticket = os_memdup(data, len); + if (conn->session_ticket == NULL) + return 0; + + conn->session_ticket_len = len; + + return 1; +} #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ @@ -2521,11 +2549,16 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, if (cb) { if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, - conn) != 1) + conn) != 1) + return -1; + if (wolfSSL_set_session_ticket_ext_cb(conn->ssl, + tls_session_ticket_ext_cb, conn) != 1) return -1; } else { if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) return -1; + if (wolfSSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL) != 1) + return -1; } return 0; -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:24 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:24 +0200 Subject: [PATCH 18/24] wolfssl: implement check_cert_subject In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-18-juliusz@wolfssl.com> Overall design was copied from tls_openssl.c. Multiple same distinguished names in one subject name are not supported. Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 253 ++++++++++++++++++++++++++++++++++----- 1 file changed, 220 insertions(+), 33 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 0cdc4c809a..b88e259e40 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -65,13 +65,15 @@ struct tls_context { int cert_in_cb; char *ocsp_stapling_response; unsigned int tls_session_lifetime; + /* This is alloc'ed and needs to be free'd */ + char *check_cert_subject; }; static struct tls_context *tls_global = NULL; /* wolfssl tls_connection */ struct tls_connection { - struct tls_context *context; + const struct tls_context *context; WOLFSSL *ssl; int read_alerts; int write_alerts; @@ -82,6 +84,7 @@ struct tls_connection { char *alt_subject_match; char *suffix_match; char *domain_match; + char *check_cert_subject; u8 srv_cert_hash[32]; @@ -121,6 +124,21 @@ static struct tls_context * tls_context_new(const struct tls_config *conf) return context; } +static void tls_context_free(struct tls_context* context) +{ + if (context) { + if (context->check_cert_subject) + os_free(context->check_cert_subject); + } + os_free(context); +} + +/* Helper to make sure the context stays const */ +static const struct tls_context* ssl_ctx_get_tls_context(void *ssl_ctx) +{ + return wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX); +} + static void wolfssl_reset_in_data(struct tls_in_data *in, const struct wpabuf *buf) @@ -373,9 +391,9 @@ void * tls_init(const struct tls_config *conf) if (!ssl_ctx) { tls_ref_count--; if (context != tls_global) - os_free(context); + tls_context_free(context); if (tls_ref_count == 0) { - os_free(tls_global); + tls_context_free(tls_global); tls_global = NULL; } return NULL; @@ -413,18 +431,19 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { - struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, - TLS_SSL_CTX_CTX_EX_IDX); + struct tls_context *context = + /* Need to cast the const away */ + (struct tls_context *)ssl_ctx_get_tls_context(ssl_ctx); if (context != tls_global) - os_free(context); + tls_context_free(context); wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx); tls_ref_count--; if (tls_ref_count == 0) { wolfSSL_Cleanup(); - os_free(tls_global); + tls_context_free(tls_global); tls_global = NULL; } } @@ -467,7 +486,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx) wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); wolfSSL_set_ex_data(conn->ssl, TLS_SSL_CON_EX_IDX, conn); - conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX); + conn->context = ssl_ctx_get_tls_context(ssl_ctx); /* Need randoms post-hanshake for EAP-FAST, export key and deriving * session ID in EAP methods. */ @@ -493,6 +512,7 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) os_free(conn->suffix_match); os_free(conn->domain_match); os_free(conn->peer_subject); + os_free(conn->check_cert_subject); /* self */ os_free(conn); @@ -542,7 +562,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *alt_subject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -576,6 +597,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -954,6 +983,138 @@ static const char * wolfssl_tls_err_string(int err, const char *err_str) } } +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(WOLFSSL_X509 *cert, int nid, const char *field, + const char *value) +{ + int ret = 0; + int len = os_strlen(value); + char buf[256]; + /* Fetch value based on NID */ + int buf_len = wolfSSL_X509_NAME_get_text_by_NID( + wolfSSL_X509_get_subject_name((WOLFSSL_X509*)cert), nid, + buf, sizeof(buf)); + + if (buf_len >= 0) { + wpa_printf(MSG_DEBUG, "wolfSSL: Matching fields: '%s' '%s' '%s'", field, + value, buf); + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + ret = buf_len >= len && os_memcmp(buf, value, len - 1) == 0; + } else { + ret = os_strcmp(buf, value) == 0; + } + } else { + wpa_printf(MSG_ERROR, "wolfSSL: cert does not contain entry for '%s'", + field); + } + + return ret; +} + +#define DN_FIELD_LEN 20 + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @processedNIDs: List of NIDs already processed + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(WOLFSSL_X509 *cert, char *field_str, + int* processedNIDs) +{ + int nid, i; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + nid = wolfSSL_OBJ_txt2nid(name); + if (nid == NID_undef) { + wpa_printf(MSG_ERROR, + "wolfSSL: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + /* Check for duplicates */ + for (i = 0; processedNIDs[i] != NID_undef && i < DN_FIELD_LEN; i++) { + if (processedNIDs[i] == nid) { + wpa_printf(MSG_ERROR, "wolfSSL: no support for multiple DN's in " + "check_cert_subject"); + return 0; + } + } + if (i == DN_FIELD_LEN) { + wpa_printf(MSG_ERROR, "wolfSSL: only %d DN's are supported in check_cert_subject", + DN_FIELD_LEN); + return 0; + } + processedNIDs[i] = nid; + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, "wolfSSL: Distinguished Name field '%s' value is " + "not defined in check_cert_subject", name); + return 0; + } + + return match_dn_field(cert, nid, name, value); +} + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(WOLFSSL_X509 *cert, const char *match) +{ + const char *token, *last = NULL; + /* Maximum length of each DN field is 255 characters */ + char field[256]; + int processedNIDs[DN_FIELD_LEN], i; + + for (i = 0; i < DN_FIELD_LEN; i++) + processedNIDs[i] = NID_undef; + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, processedNIDs)) { + wpa_printf(MSG_INFO, "wolfSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) { @@ -976,7 +1137,7 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn, { union tls_event_data ev; struct wpabuf *cert = NULL; - struct tls_context *context = conn->context; + const struct tls_context *context = conn->context; if (!context->event_cb) return; @@ -1029,7 +1190,7 @@ static void wolfssl_tls_cert_event(struct tls_connection *conn, { struct wpabuf *cert = NULL; union tls_event_data ev; - struct tls_context *context = conn->context; + const struct tls_context *context = conn->context; char *alt_subject[TLS_MAX_ALT_SUBJECT]; int alt, num_alt_subject = 0; WOLFSSL_GENERAL_NAME *gen; @@ -1126,8 +1287,9 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) int err, depth; WOLFSSL *ssl; struct tls_connection *conn; - struct tls_context *context; + const struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1231,7 +1393,20 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", __func__, preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); - if (depth == 0 && match && os_strstr(buf, match) == NULL) { + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->context->check_cert_subject; + if (check_cert_subject && depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + wpa_printf(MSG_WARNING, + "TLS: Subject '%s' did not match with '%s'", + buf, check_cert_subject); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + else if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not match with '%s'", buf, match); @@ -1412,8 +1587,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match) < 0) { - wpa_printf(MSG_INFO, "Error setting subject match"); + params->domain_match, + params->check_cert_subject) < 0) { + wpa_printf(MSG_ERROR, "Error setting subject match"); return -1; } @@ -1421,14 +1597,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path) < 0) { - wpa_printf(MSG_INFO, "Error setting CA cert"); + wpa_printf(MSG_ERROR, "Error setting CA cert"); return -1; } if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len) < 0) { - wpa_printf(MSG_INFO, "Error setting client cert"); + wpa_printf(MSG_ERROR, "Error setting client cert"); return -1; } @@ -1436,13 +1612,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->private_key_passwd, params->private_key_blob, params->private_key_blob_len) < 0) { - wpa_printf(MSG_INFO, "Error setting private key"); + wpa_printf(MSG_ERROR, "Error setting private key"); return -1; } if (handle_ciphersuites(NULL, conn->ssl, params->openssl_ciphers, params->flags) != 0) { - wpa_printf(MSG_INFO, "Error setting ciphersuites"); + wpa_printf(MSG_ERROR, "Error setting ciphersuites"); return -1; } @@ -1475,7 +1651,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, wolfSSL_CTX_EnableOCSP(ctx, 0); #else /* HAVE_OCSP */ if (params->flags & TLS_CONN_REQUIRE_OCSP) { - wpa_printf(MSG_INFO, + wpa_printf(MSG_ERROR, "wolfSSL: No OCSP support included - reject configuration"); return -1; } @@ -1636,19 +1812,33 @@ void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response) int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { + struct tls_context *context = + /* Need to cast away const as this is one of the only places + * where we should modify it */ + (struct tls_context*)ssl_ctx_get_tls_context(tls_ctx); + wpa_printf(MSG_DEBUG, "SSL: global set params"); - if (params->check_cert_subject) - return -1; /* not yet supported */ + os_free(context->check_cert_subject); + context->check_cert_subject = NULL; + if (params->check_cert_subject) { + context->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!context->check_cert_subject) { + wpa_printf(MSG_ERROR, "SSL: Failed to copy check_cert_subject '%s'", + params->check_cert_subject); + return -1; + } + } if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { - wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", + wpa_printf(MSG_ERROR, "SSL: Failed to load ca cert file '%s'", params->ca_cert); return -1; } if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) { - wpa_printf(MSG_INFO, + wpa_printf(MSG_ERROR, "SSL: Failed to load client cert file '%s'", params->client_cert); return -1; @@ -1656,26 +1846,26 @@ int tls_global_set_params(void *tls_ctx, if (tls_global_private_key(tls_ctx, params->private_key, params->private_key_passwd) < 0) { - wpa_printf(MSG_INFO, + wpa_printf(MSG_ERROR, "SSL: Failed to load private key file '%s'", params->private_key); return -1; } if (tls_global_dh(tls_ctx, params->dh_file) < 0) { - wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'", + wpa_printf(MSG_ERROR, "SSL: Failed to load DH file '%s'", params->dh_file); return -1; } if (handle_ciphersuites(tls_ctx, NULL, params->openssl_ciphers, params->flags) != 0) { - wpa_printf(MSG_INFO, "Error setting ciphersuites"); + wpa_printf(MSG_ERROR, "Error setting ciphersuites"); return -1; } if (params->openssl_ecdh_curves) { - wpa_printf(MSG_INFO, + wpa_printf(MSG_ERROR, "wolfSSL: openssl_ecdh_curves not supported"); return -1; } @@ -1717,7 +1907,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; - struct tls_context *context; + const struct tls_context *context; if (!conn) return -1; @@ -1736,8 +1926,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, wolfSSL_set_accept_state(conn->ssl); - context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, - TLS_SSL_CTX_CTX_EX_IDX); + context = ssl_ctx_get_tls_context(ssl_ctx); if (context && context->tls_session_lifetime == 0) { /* * Set session id context to a unique value to make sure @@ -1753,8 +1942,6 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, session_ctx_len); } - /* TODO: do we need to fake a session like OpenSSL does here? */ - return 0; } -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:29 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:29 +0200 Subject: [PATCH 23/24] wolfSSL: Implement openssl_ecdh_curves In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-23-juliusz@wolfssl.com> Signed-off-by: Juliusz Sosinowicz --- src/crypto/tls_wolfssl.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index 8940de98d4..224940a402 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -1628,6 +1628,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ecdh_curves) { + if (wolfSSL_set1_curves_list(conn->ssl, params->openssl_ecdh_curves) + != 1) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } + } + tls_set_conn_flags(conn->ssl, params->flags); #ifdef HAVE_CERTIFICATE_STATUS_REQUEST @@ -1871,9 +1880,12 @@ int tls_global_set_params(void *tls_ctx, } if (params->openssl_ecdh_curves) { - wpa_printf(MSG_ERROR, - "wolfSSL: openssl_ecdh_curves not supported"); - return -1; + if (wolfSSL_CTX_set1_curves_list((WOLFSSL_CTX*)tls_ctx, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } } #ifdef HAVE_SESSION_TICKET -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:16:26 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:16:26 +0200 Subject: [PATCH 20/24] run_ap_wpa2_eap_tls_intermediate_ca_ocsp: fix cert configuration In-Reply-To: <20240404181630.2431991-1-juliusz@wolfssl.com> References: <20240404181630.2431991-1-juliusz@wolfssl.com> Message-ID: <20240404181630.2431991-20-juliusz@wolfssl.com> When wolfSSL is on the server side, it won't send the entire chain. The client needs to have the server CA loaded to be able to verify the server and needs to load user_and_ica.pem so it sends a cert chain. Use entire cert chain PEM since the test relies on chain being sent. wolfSSL only sends the certificate that was loaded and not the full chain. Signed-off-by: Juliusz Sosinowicz --- .../iCA-server/server-revoked_and_ica.pem | 162 +++++++++--------- tests/hwsim/auth_serv/ica-generate.sh | 2 +- tests/hwsim/test_ap_eap.py | 12 +- 3 files changed, 90 insertions(+), 86 deletions(-) diff --git a/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem b/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem index 09619be1aa..22997b8655 100644 --- a/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem +++ b/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem @@ -1,84 +1,3 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - d8:d3:e3:a6:cb:e3:cc:f7 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA - Validity - Not Before: May 3 15:20:10 2020 GMT - Not After : May 3 15:20:10 2030 GMT - Subject: C=FI, O=w1.fi, CN=Server Intermediate CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95: - 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22: - 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e: - db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c: - 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44: - 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c: - 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05: - 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f: - ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85: - ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f: - 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8: - 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85: - af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52: - ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee: - 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08: - 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2: - 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d: - 3b:5d - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55 - X509v3 Authority Key Identifier: - keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1 - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - Signature Algorithm: sha256WithRSAEncryption - 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e: - b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83: - 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57: - 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41: - 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52: - 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93: - 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00: - 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2: - 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42: - 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e: - 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70: - 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a: - ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e: - cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4: - 97:ac:df:2e ------BEGIN CERTIFICATE----- -MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV -BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE -AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ -BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy -bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m -F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV -T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi -tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv -Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+ -9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8 -6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi -vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI -MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy -u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e -rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i -/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m -DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak -53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE -HRKxYh36KPSXrN8u ------END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) @@ -165,3 +84,84 @@ zoQkEtp/qKsV/SSbzxyuL48TKCcJHlcryh/IvKSVCCdOxCFopUWfWkIcfzdZ1+0w vu0mEl2A9X19lP9SVvxnDz8AIee0L0h7d4b7FiiraOFNgOteS5mIL+yjHQbFBC67 VvtrdZ1beINjK3B8IZShWKSOizDTKIg= -----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + d8:d3:e3:a6:cb:e3:cc:f7 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA + Validity + Not Before: May 3 15:20:10 2020 GMT + Not After : May 3 15:20:10 2030 GMT + Subject: C=FI, O=w1.fi, CN=Server Intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95: + 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22: + 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e: + db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c: + 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44: + 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c: + 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05: + 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f: + ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85: + ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f: + 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8: + 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85: + af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52: + ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee: + 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08: + 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2: + 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d: + 3b:5d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55 + X509v3 Authority Key Identifier: + keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1 + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e: + b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83: + 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57: + 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41: + 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52: + 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93: + 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00: + 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2: + 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42: + 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e: + 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70: + 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a: + ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e: + cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4: + 97:ac:df:2e +-----BEGIN CERTIFICATE----- +MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE +AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ +BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy +bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m +F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV +T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi +tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv +Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+ +9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8 +6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi +vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI +MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy +u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e +rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i +/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m +DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak +53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE +HRKxYh36KPSXrN8u +-----END CERTIFICATE----- diff --git a/tests/hwsim/auth_serv/ica-generate.sh b/tests/hwsim/auth_serv/ica-generate.sh index d3fe7b9645..555cdb06d3 100755 --- a/tests/hwsim/auth_serv/ica-generate.sh +++ b/tests/hwsim/auth_serv/ica-generate.sh @@ -58,7 +58,7 @@ cat ec-ca-openssl.cnf | $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-server/server-revoked.key -out iCA-server/server-revoked.req -outform PEM -sha256 $OPENSSL ca -config openssl.cnf.tmp -batch -keyfile iCA-server/private/cakey.pem -cert iCA-server/cacert.pem -create_serial -in iCA-server/server-revoked.req -out iCA-server/server-revoked.pem -extensions ext_server -md sha256 $OPENSSL ca -config openssl.cnf.tmp -revoke iCA-server/server-revoked.pem -keyfile iCA-server/private/cakey.pem -cert iCA-server/cacert.pem -cat iCA-server/cacert.pem iCA-server/server-revoked.pem > iCA-server/server-revoked_and_ica.pem +cat iCA-server/server-revoked.pem iCA-server/cacert.pem > iCA-server/server-revoked_and_ica.pem rm openssl.cnf.tmp echo diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index 3350da7e4e..580660e592 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -4972,14 +4972,18 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md): fn = ica_ocsp("server.pem", md) params["ocsp_stapling_response"] = fn try: - hostapd.add_ap(apdev[0], params) + hapd = hostapd.add_ap(apdev[0], params) tls = dev[0].request("GET tls_library") if "GnuTLS" in tls or "wolfSSL" in tls: - ca_cert = "auth_serv/iCA-user/ca-and-root.pem" client_cert = "auth_serv/iCA-user/user_and_ica.pem" else: - ca_cert = "auth_serv/iCA-user/ca-and-root.pem" client_cert = "auth_serv/iCA-user/user.pem" + hapd_tls = hapd.request("GET tls_library") + if "GnuTLS" in hapd_tls or "wolfSSL" in hapd_tls: + ca_cert = "auth_serv/iCA-server/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS", identity="tls user", ca_cert=ca_cert, @@ -5003,7 +5007,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md): check_ocsp_support(dev[0]) params = int_eap_server_params() params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem" - params["server_cert"] = "auth_serv/iCA-server/server-revoked.pem" + params["server_cert"] = "auth_serv/iCA-server/server-revoked_and_ica.pem" params["private_key"] = "auth_serv/iCA-server/server-revoked.key" fn = ica_ocsp("server-revoked.pem", md) params["ocsp_stapling_response"] = fn -- 2.34.1 From juliusz at wolfssl.com Thu Apr 4 11:40:04 2024 From: juliusz at wolfssl.com (Juliusz Sosinowicz) Date: Thu, 4 Apr 2024 20:40:04 +0200 Subject: [PATCH 01/12] Print ciphersuites in wolfSSL In-Reply-To: References: <20230308171850.267577-1-juliusz@wolfssl.com> Message-ID: <13e12b73-edc8-450b-8173-336fa8475bf2@wolfssl.com> Hi Jouni, its taken me a while but I was able to fix the failing tests. Fixing bugs and implementing new features. The large patchset I just sent passes all tests except the following: ? - ap_wpa2_eap_tls_ec_and_rsa ? - ap_wpa2_eap_tls_rsa_and_ec ????? - wolfSSL does not support loading multiple certs into one object. This could be implemented by lazily loading the appropriate certificate by using wolfSSL_get_ciphersuite_info and wolfSSL_get_sigalg_info to determine the ciphersuite and sigalgs but this is still on the roadmap. ? - ap_wpa2_eap_ttls_optional_ocsp_unknown ????? - wolfSSL does not support recovering from an OCSP error dynamically. ? - OCSP tests with sha256 ????? - wolfSSL only works with sha-1 (although it can be changed to sha256 at compile time) signatures in OCSP responses. Being signature algorithm agnostic is in our backlog for a while now. The wolfSSL dev branch to use for testing is https://github.com/julek-wolfssl/wolfssl/tree/hostap. The wolfSSL configuration I used is: ??? --enable-wpas --enable-wpas-dpp --enable-brainpool --with-eccminsz=192 --enable-tlsv10 --enable-oldtls 'CPPFLAGS=-DWOLFSSL_STATIC_RSA' Sincerely Juliusz On 04/11/2023 18:01, Jouni Malinen wrote: > Thanks, applied the full series with some cleanup and fixes. The last > patches in this series were quite inconvenient to review since there was > a mix of refactoring and functional changes in the same patch and also > cases where some FIPS cases were first removed and then re-added in a > later patch in the same series. I ended up splitting those quite > differently for the commit to make the real changes easier to > understand. > > This broke the build with wolfSSL 5.2.0 which was the version I used > when testing the functionality last time. I ended up adding a simple > wrapper function based on the library version to try to work around that > for now. I'm not sure whether 5.2.0 is considered supported anymore, so > it might be fine to remove that wrapper if that is considered obsolete. > > In addition, I ran the full hwsim test case set with wolfSSL 5.5.4 on > the STA side and OpenSSL on the AP and authentication server. The > following test cases are failing likely due to some missing > functionality. I did not look at the details, though. > > ap_wpa2_eap_fast_binary_pac > ap_wpa2_eap_fast_binary_pac_errors > ap_wpa2_eap_fast_cipher_suites > ap_wpa2_eap_fast_eap_aka > ap_wpa2_eap_fast_eap_sim > ap_wpa2_eap_fast_gtc_auth_prov > ap_wpa2_eap_fast_mschapv2_unauth_prov > ap_wpa2_eap_fast_pac_file > ap_wpa2_eap_fast_pac_lifetime > ap_wpa2_eap_fast_pac_refresh > ap_wpa2_eap_fast_pac_truncate > ap_wpa2_eap_fast_prf_oom > ap_wpa2_eap_fast_prov > ap_wpa2_eap_fast_server_oom > ap_wpa2_eap_fast_text_pac_errors > ap_wpa2_eap_tls_ec_and_rsa > ap_wpa2_eap_tls_intermediate_ca_ocsp > ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked > ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked_sha1 > ap_wpa2_eap_tls_rsa_and_ec > ap_wpa2_eap_tls_versions > ap_wpa2_eap_tls_versions_server > ap_wpa2_eap_ttls_expired_cert > ap_wpa2_eap_ttls_ignore_expired_cert > ap_wpa2_eap_ttls_ocsp_revoked > ap_wpa2_eap_ttls_ocsp_unknown > ap_wpa2_eap_ttls_optional_ocsp_unknown > ap_wpa2_eap_ttls_pap_check_cert_subject_neg > dpp_peer_intro_failures > dpp_pkex_hostapd_initiator > eap_fast_proto_phase2 > eap_fast_tlv_nak_oom > eap_mschapv2_errors > eap_proto_fast_errors > eap_teap_tls_cs_sha1 > eap_teap_tls_cs_sha256 > eap_teap_tls_cs_sha384 > fils_sk_pfs_25 > openssl_ecdh_curves > owe_invalid_assoc_resp > sae_bignum_failure > sae_no_ffc_by_default > sae_password_ffc > sae_pwe_failure > sae_pwe_group_14 > sae_pwe_group_15 > sae_pwe_group_16 > sae_pwe_group_23 > sae_pwe_group_24 > sae_pwe_group_25 > sae_pwe_group_5 > sae_reflection_attack_ffc > sae_reflection_attack_ffc_internal > sigma_dut_ap_cipher_ccmp_128 > sigma_dut_ap_cipher_ccmp_256 > sigma_dut_ap_cipher_ccmp_gcmp_1 > sigma_dut_ap_cipher_ccmp_gcmp_2 > sigma_dut_ap_cipher_gcmp_128 > sigma_dut_ap_cipher_gcmp_256 > sigma_dut_ap_cipher_gcmp_256_group_ccmp > sigma_dut_ap_suite_b > sigma_dut_eap_aka > sigma_dut_eap_ttls > sigma_dut_eap_ttls_uosc > sigma_dut_eap_ttls_uosc_ca_mistrust > sigma_dut_suite_b > suite_b > suite_b_192 > suite_b_192_mic_failure > suite_b_192_okc > suite_b_192_pmkid_failure > suite_b_192_pmksa_caching_roam > suite_b_192_radius > suite_b_192_rsa > suite_b_192_rsa_dhe > suite_b_192_rsa_ecdhe > suite_b_192_rsa_insufficient_dh > suite_b_192_rsa_insufficient_key > suite_b_192_rsa_radius > suite_b_192_rsa_tls_13 > suite_b_mic_failure > suite_b_pmkid_failure > suite_b_radius > From rmekonnen at chromium.org Fri Apr 5 00:36:48 2024 From: rmekonnen at chromium.org (Ruth Mekonnen) Date: Fri, 5 Apr 2024 07:36:48 +0000 Subject: [PATCH] Add non-PSC channels to 6GHz scan request Message-ID: <20240405073708.3846816-1-rmekonnen@chromium.org> When non_coloc_6ghz = false, the STA is expected to scan for colocated APs. However, if the colocated AP is on a non-PSC channel, it will not be detected during the 6GHz-only scan because the frequency list is limited to PSC channels. Even when the NL80211_SCAN_FLAG_COLOCATED_6GHZ is set, the cfg80211 only scans a subset of the channels in the original 6GHz scan request. Therefore, this patch adds non-PSC channels to the original 6GHz scan request. Signed-off-by: Ruth Mekonnen --- wpa_supplicant/events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ca2794638..955e944e0 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2582,7 +2582,7 @@ static int wpas_trigger_6ghz_scan(struct wpa_supplicant *wpa_s, params.ssids[j] = data->scan_info.ssids[j]; params.num_ssids = data->scan_info.num_ssids; wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, - true, !wpa_s->last_scan_non_coloc_6ghz, false); + true, false, false); if (!wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, true)) { os_free(params.freqs); return 1; -- 2.44.0.478.gd926399ef9-goog From rmekonnen at chromium.org Fri Apr 5 00:52:22 2024 From: rmekonnen at chromium.org (Ruth Mekonnen) Date: Fri, 5 Apr 2024 07:52:22 +0000 Subject: [PATCH] Emit ScanInProgress6GHz property Message-ID: <20240405075227.3877310-1-rmekonnen@chromium.org> Expose whether a 6GHz scan is in progress with the ScanInProgress6GHz property, so that platforms can choose not to disconnect while the 6GHz scan is in progress. Once the 6GHz scan has completed the ScanInProgress6GHz property is reset to false. Signed-off-by: Ruth Mekonnen --- doc/dbus.doxygen | 5 +++++ wpa_supplicant/dbus/dbus_new.c | 9 +++++++++ wpa_supplicant/dbus/dbus_new.h | 1 + wpa_supplicant/dbus/dbus_new_handlers.c | 21 +++++++++++++++++++++ wpa_supplicant/dbus/dbus_new_handlers.h | 1 + wpa_supplicant/events.c | 7 +++++++ wpa_supplicant/notify.c | 8 ++++++++ wpa_supplicant/notify.h | 1 + wpa_supplicant/wpa_supplicant_i.h | 1 + 9 files changed, 54 insertions(+) diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen index 9f72614d4..3c689cf55 100644 --- a/doc/dbus.doxygen +++ b/doc/dbus.doxygen @@ -883,6 +883,11 @@ fi.w1.wpa_supplicant1.CreateInterface.

The most recent roam success or failure.

+
  • +

    ScanInProgress6GHz - b - (read)

    +

    Whether a 6GHz scan is currently in progress.

    +
  • +
  • SessionLength - u - (read)

    The most recent BSS session length in milliseconds.

    diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 8bd6a9a43..7e11f1c33 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -2395,6 +2395,9 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_PROP_ROAM_COMPLETE: prop = "RoamComplete"; break; + case WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ: + prop = "ScanInProgress6GHz"; + break; case WPAS_DBUS_PROP_SESSION_LENGTH: prop = "SessionLength"; break; @@ -3982,6 +3985,12 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL, NULL }, + { + "ScanInProgress6GHz", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_scan_in_progress_6ghz, + NULL, + NULL + }, { "SessionLength", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_session_length, diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 952bb422a..1db5fe8ae 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -36,6 +36,7 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_ASSOC_STATUS_CODE, WPAS_DBUS_PROP_ROAM_TIME, WPAS_DBUS_PROP_ROAM_COMPLETE, + WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ, WPAS_DBUS_PROP_SESSION_LENGTH, WPAS_DBUS_PROP_BSS_TM_STATUS, WPAS_DBUS_PROP_MAC_ADDRESS, diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 3897d98f4..ee8a7c4bd 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -3832,6 +3832,27 @@ dbus_bool_t wpas_dbus_getter_roam_complete( &roam_complete, error); } +/** + * wpas_dbus_getter_scan_in_progress_6ghz - Get whether a 6ghz scan is in + * progress + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "ScanInProgress6GHz" property. + */ +dbus_bool_t wpas_dbus_getter_scan_in_progress_6ghz( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_bool_t scan_in_progress_6ghz = wpa_s->scan_in_progress_6ghz ? TRUE : FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &scan_in_progress_6ghz, error); +} + /** * wpas_dbus_getter_session_length - Get most recent BSS session length diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index acd6af7ff..7faf70a77 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -173,6 +173,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_auth_status_code); DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code); DECLARE_ACCESSOR(wpas_dbus_getter_roam_time); DECLARE_ACCESSOR(wpas_dbus_getter_roam_complete); +DECLARE_ACCESSOR(wpas_dbus_getter_scan_in_progress_6ghz); DECLARE_ACCESSOR(wpas_dbus_getter_session_length); DECLARE_ACCESSOR(wpas_dbus_getter_bss_tm_status); DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ca2794638..dae3f6fbb 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2402,6 +2402,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_supplicant_notify_scanning(wpa_s, 0); + if (wpa_s->scan_in_progress_6ghz) { + wpa_s->scan_in_progress_6ghz = false; + wpas_notify_scan_in_progress_6ghz(wpa_s); + } + scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1, NULL); @@ -2584,6 +2589,8 @@ static int wpas_trigger_6ghz_scan(struct wpa_supplicant *wpa_s, wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, true, !wpa_s->last_scan_non_coloc_6ghz, false); if (!wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, true)) { + wpa_s->scan_in_progress_6ghz = true; + wpas_notify_scan_in_progress_6ghz(wpa_s); os_free(params.freqs); return 1; } diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 6d5dbcc29..1e5c43ba0 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -167,6 +167,14 @@ void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s) wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ROAM_COMPLETE); } +void wpas_notify_scan_in_progress_6ghz(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ); +} + void wpas_notify_session_length(struct wpa_supplicant *wpa_s) { diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index f6c7ac4e2..9749e9cfc 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -30,6 +30,7 @@ void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_roam_time(struct wpa_supplicant *wpa_s); void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s); +void wpas_notify_scan_in_progress_6ghz(struct wpa_supplicant *wpa_s); void wpas_notify_session_length(struct wpa_supplicant *wpa_s); void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 426d077d2..58c833e3e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1579,6 +1579,7 @@ struct wpa_supplicant { bool wps_scan_done; /* Set upon receiving scan results event */ bool supp_pbc_active; /* Set for interface when PBC is triggered */ bool wps_overlap; + bool scan_in_progress_6ghz; /* Set upon a 6GHz scan being triggered */ #ifdef CONFIG_PASN struct pasn_data pasn; -- 2.44.0.478.gd926399ef9-goog From andrei.otcheretianski at intel.com Mon Apr 8 06:06:56 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:06:56 +0300 Subject: [PATCH 2/7] wpa_supplicant: Do not roam to an associated link In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-2-andrei.otcheretianski@intel.com> From: Ilan Peer When considering to roam to a different BSS and the connection is an MLD connection, do not roam to a BSS which is already included in the MLD connection. Signed-off-by: Ilan Peer --- wpa_supplicant/events.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ca27946384..bfe3d85e8f 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2167,6 +2167,20 @@ int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, MAC2STR(selected->bssid), selected->freq, selected->level, selected->snr, selected->est_throughput); + if (wpa_s->valid_links) { + u8 i; + + for_each_link(wpa_s->valid_links, i) { + if (ether_addr_equal(wpa_s->links[i].bssid, + selected->bssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: associated to selected BSS link_id=%u", + i); + return 0; + } + } + } + if (wpa_s->current_ssid->bssid_set && ether_addr_equal(selected->bssid, wpa_s->current_ssid->bssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS " -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:06:55 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:06:55 +0300 Subject: [PATCH 1/7] tests: Wait after removing a BSS Message-ID: <20240408130701.531194-1-andrei.otcheretianski@intel.com> From: Ilan Peer When a BSS is removed, the flow continues without actually waiting for the AP to be stopped. This is racy in flows that actually expect the AP to be stopped, e.g., test_ap_bss_add_remove(). Try to mitigate such cases by adding a short sleep after the AP is removed. Signed-off-by: Ilan Peer --- tests/hwsim/hostapd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/hwsim/hostapd.py b/tests/hwsim/hostapd.py index 007812274b..ebb2e328af 100644 --- a/tests/hwsim/hostapd.py +++ b/tests/hwsim/hostapd.py @@ -766,6 +766,9 @@ def remove_bss(apdev, ifname=None): hapd_global = HostapdGlobal(apdev) hapd_global.remove(ifname) + # wait little to make sure the AP stops beaconing + time.sleep(0.1) + def terminate(apdev): try: hostname = apdev['hostname'] -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:06:57 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:06:57 +0300 Subject: [PATCH 3/7] SME: MLD: Deauthenticate when failing to parse ML element In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-3-andrei.otcheretianski@intel.com> From: Ilan Peer If parsing the basic ML element in the authenticate frame is failed, instead of only disassociating, completely deauthenticate so all state machines would be in a consistent state. Signed-off-by: Ilan Peer --- wpa_supplicant/sme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index f08184f981..0115caf8c5 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2156,7 +2156,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) MAC2STR(wpa_s->pending_bssid), WLAN_REASON_DEAUTH_LEAVING); wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); - wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); return; } -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:06:58 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:06:58 +0300 Subject: [PATCH 4/7] SME: Explicitly deauthenticate on authentication/association timeout In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-4-andrei.otcheretianski@intel.com> From: Ilan Peer This would clear the local driver state and also the AP state, and would allow clean connection establishment next time. This is needed in cases where the AP sends the association response frame and sets the station state to associated, but the underlying driver, e.g., mac80211, fails to parse the association response and drops it, eventually clearing the association flow and sending a timeout event to user space. Signed-off-by: Ilan Peer --- wpa_supplicant/sme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 0115caf8c5..775fe9ea03 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -2887,7 +2887,7 @@ void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); - wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -2896,7 +2896,7 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); - wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:06:59 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:06:59 +0300 Subject: [PATCH 5/7] SME: MLD: Use AP MLD address with deauthenticate In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-5-andrei.otcheretianski@intel.com> From: Ilan Peer When the authentication is an MLD authentication need to use the AP MLD address when requesting the driver to deauthenticate. In addition, in case of failure handling an authentication frame from the AP MLD, clear the MLD state only after the deauthentication is done. Signed-off-by: Ilan Peer --- wpa_supplicant/sme.c | 5 +++-- wpa_supplicant/wpa_supplicant.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 775fe9ea03..5766cdbf4e 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -508,8 +508,6 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s, return 0; out: - wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state"); - wpas_reset_mlo_info(wpa_s); return -1; } @@ -2158,6 +2156,9 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_printf(MSG_DEBUG, + "MLD: Authentication - clearing MLD state"); + wpas_reset_mlo_info(wpa_s); return; } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index ab71e2f273..54501b7636 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -4588,9 +4588,16 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, reason_code, reason2str(reason_code), wpa_supplicant_state_txt(wpa_s->wpa_state)); - if (!is_zero_ether_addr(wpa_s->pending_bssid) && - (wpa_s->wpa_state == WPA_AUTHENTICATING || - wpa_s->wpa_state == WPA_ASSOCIATING)) + wpa_dbg(wpa_s, MSG_DEBUG, + "deauthenticate: valid_links=0x%x, ap_mld_addr=" MACSTR, + wpa_s->valid_links, MAC2STR(wpa_s->ap_mld_addr)); + + if (wpa_s->valid_links && + !is_zero_ether_addr(wpa_s->ap_mld_addr)) + addr = wpa_s->ap_mld_addr; + else if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) addr = wpa_s->pending_bssid; else if (!is_zero_ether_addr(wpa_s->bssid)) addr = wpa_s->bssid; -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:07:00 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:07:00 +0300 Subject: [PATCH 6/7] ctrl_iface: allow sending ML probe without AP MLD ID In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-6-andrei.otcheretianski@intel.com> From: Benjamin Berg If one sends a probe request to a non-TX BSSID, then no AP MLD ID should be included in the request. Permit mld_id to be -1 so that it is not a required argument and can be left out. Signed-off-by: Benjamin Berg --- wpa_supplicant/ctrl_iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index d0fda4cd9e..bc013ad995 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -12118,7 +12118,7 @@ static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd) } } - if (mld_id < 0 || is_zero_ether_addr(bssid)) { + if (is_zero_ether_addr(bssid)) { wpa_printf(MSG_DEBUG, "MLD: Failed parsing ML probe request arguments"); return -1; -- 2.43.0 From andrei.otcheretianski at intel.com Mon Apr 8 06:07:01 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Mon, 8 Apr 2024 16:07:01 +0300 Subject: [PATCH 7/7] wpa_supplicant: Do not allow fast associate before scanning 6 GHz In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: <20240408130701.531194-7-andrei.otcheretianski@intel.com> From: Ilan Peer In case the channel map was updated to include the 6 GHz but these channels were not scanned yet, do not allow fast associate. Signed-off-by: Ilan Peer --- wpa_supplicant/events.c | 10 ++++++---- wpa_supplicant/scan.c | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index bfe3d85e8f..96054f264a 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2852,6 +2852,9 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) wpa_s->conf->scan_res_valid_for_connect)) { wpa_printf(MSG_DEBUG, "Fast associate: Old scan results"); return -1; + } else if (wpa_s->crossed_6ghz_dom) { + wpa_printf(MSG_DEBUG, "Fast associate: crossed 6 GHz dom"); + return -1; } return wpas_select_network_from_last_scan(wpa_s, 0, 1, false, NULL); @@ -5234,11 +5237,10 @@ void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, wpa_dbg(ifs, MSG_DEBUG, "Channel list changed - restart sched_scan"); wpas_scan_restart_sched_scan(ifs); - } else if (ifs->scanning && !was_6ghz_enabled && - ifs->is_6ghz_enabled) { - /* Look for APs in the 6 GHz band */ + } else if (!was_6ghz_enabled && ifs->is_6ghz_enabled) { wpa_dbg(ifs, MSG_INFO, - "Channel list changed - trigger 6 GHz-only scan"); + "Channel list changed: 6 GHz are enabled"); + ifs->crossed_6ghz_dom = true; } } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 2db4d8b910..e591f22507 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -322,6 +322,12 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } wpa_s->last_scan_all_chan = !params->freqs; wpa_s->last_scan_non_coloc_6ghz = params->non_coloc_6ghz; + + if (wpa_s->crossed_6ghz_dom) { + wpa_printf(MSG_DEBUG, "First scan after crossing 6 GHz domain"); + wpa_s->crossed_6ghz_dom = false; + } + if (!ctx || radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb, ctx) < 0) { -- 2.43.0 From satyaprakash.developer.unix at gmail.com Tue Apr 9 08:51:01 2024 From: satyaprakash.developer.unix at gmail.com (Satya Prakash Prasad) Date: Tue, 9 Apr 2024 21:21:01 +0530 Subject: EAP TLS - Hostapd Message-ID: Hi, Referring to https://github.com/prplfoundation/hostap/tree/master - please guide me code example - how after authentication successful read / write data exchange will happen. I was referring to below peer example code: https://github.com/prplfoundation/hostap/blob/master/eap_example/eap_example_peer.c After the EAP success case, how peer / server side will read / write data - which APIs code implements the same - also how do we verify that EAP authentication was successful before each write / data? EAP-TLS Peer EAP-TLS Server EAP-Request/ <-------- Identity EAP-Response/ Identity (Privacy-Friendly) --------> EAP-Request/ EAP-Type=EAP-TLS <-------- (TLS Start) EAP-Response/ EAP-Type=EAP-TLS (TLS ClientHello) --------> EAP-Request/ EAP-Type=EAP-TLS (TLS ServerHello, TLS EncryptedExtensions, TLS CertificateRequest, TLS Certificate, TLS CertificateVerify, <-------- TLS Finished) EAP-Response/ EAP-Type=EAP-TLS (TLS Certificate, TLS CertificateVerify, TLS Finished) --------> EAP-Request/ EAP-Type=EAP-TLS <-------- (TLS Application Data 0x00) EAP-Response/ EAP-Type=EAP-TLS --------> <-------- EAP-Success Thanks in advance. Regards, Prakash From somerandomstring at gmail.com Tue Apr 9 15:06:38 2024 From: somerandomstring at gmail.com (Lee Harding) Date: Tue, 9 Apr 2024 15:06:38 -0700 Subject: [PATCH] Allow Session-Timeout with PSK RADIUS during 4 Way Handshake In-Reply-To: References: Message-ID: I've been reading the code and testing the PSK via RADIUS functionality in hostap and discovered what I believe to be a bug. When the RADIUS response includes a Session-Timeout and is otherwise valid (an Access-Accept with a valid Tunnel-Password) the association still fails due to the strict comparison of the accepted value with HOSTAPD_ACL_ACCEPT. Apparently this wasn't previously tested. The patch below allows a packet containing a valid Session-Timeout attribute to be accepted by extending the "success" comparison to include HOSTAPD_ACL_ACCEPT_TIMEOUT. Signed-off-by: Lee Harding Diff inline below: diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index e723ae74b..7b3b0137f 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -596,7 +596,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (query->radius_psk) { struct sta_info *sta; - bool success = cache->accepted == HOSTAPD_ACL_ACCEPT; + bool success = cache->accepted == HOSTAPD_ACL_ACCEPT + || cache->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT; sta = ap_get_sta(hapd, query->addr); if (!sta || !sta->wpa_sm) { From mcr at sandelman.ca Wed Apr 10 05:11:25 2024 From: mcr at sandelman.ca (Michael Richardson) Date: Wed, 10 Apr 2024 08:11:25 -0400 Subject: EAP TLS - Hostapd In-Reply-To: References: Message-ID: <4476.1712751085@obiwan.sandelman.ca> Satya Prakash Prasad wrote: > After the EAP success case, how peer / server side will read / write > data - which APIs code implements the same - also how do we verify that > EAP authentication was successful before each write / data? After the handshake, session keys are derived from the EAP session using the MSK, and installed into the supplicant (and authenticator) kernel. But, perhaps I mis-undertand your question, are you trying to send more traffic within the EAP-TLS connection? -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 511 bytes Desc: not available URL: From lorenzo at kernel.org Wed Apr 10 06:32:18 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Wed, 10 Apr 2024 15:32:18 +0200 Subject: [PATCH v4 0/4] Introduce Automated Frequency Coordination (AFC) support Message-ID: Introduce Automated Frequency Coordination (AFC) support for UNII-5 and UNII-7 6GHz bands. Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. AFCD and AFC hostapd client are tested with AFC DUT Test Harness [0]. Changes since v3: - fix compilation errors when IEEE80211_AX is not enabled - fix a leftover crash stopping the interface - make sure rx buffer is null-terminated Changes since v2: - multiple fixes testing with AFC DUT Test Harness Changes since v1: - use unix socket instead of a tcp one Changes since rfc: - move curl connection to a dedicated afc daemon Lorenzo Bianconi (4): hostapd: afcd: add AFC daemon support hostapd: export hostapd_is_usable_chans utility routine hostapd: ap: add AFC client support hostapd: update TPE IE according to AFC afc/.gitignore | 1 + afc/Makefile | 31 ++ afc/afc-reply.json | 215 +++++++++ afc/afcd.c | 292 ++++++++++++ hostapd/Makefile | 8 + hostapd/config_file.c | 261 +++++++++++ hostapd/defconfig | 3 + hostapd/hostapd.conf | 42 ++ src/ap/afc.c | 1015 +++++++++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 16 + src/ap/ap_config.h | 47 ++ src/ap/hostapd.c | 16 + src/ap/hostapd.h | 54 +++ src/ap/hw_features.c | 4 +- src/ap/hw_features.h | 6 + src/ap/ieee802_11.c | 47 +- 16 files changed, 2039 insertions(+), 19 deletions(-) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile create mode 100644 afc/afc-reply.json create mode 100644 afc/afcd.c create mode 100644 src/ap/afc.c -- 2.44.0 From lorenzo at kernel.org Wed Apr 10 06:32:19 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Wed, 10 Apr 2024 15:32:19 +0200 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: References: Message-ID: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> Introduce Automated Frequency Coordination Daemon (AFCD) support for UNII-5 and UNII-7 6GHz bands. AFCD will be used by hostapd AFC client in order to forward the AFC request to the AFC coordinator and decouple AFC connection management from hostapd. AFC is required for Standard Power Devices (SPDs) to determine a lists of channels and EIRP/PSD powers that are available in the 6GHz spectrum. AFCD is tested with AFC DUT Test Harness [0]. Add afc-reply.json as reference for replies from the AFC coordinator. [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- afc/.gitignore | 1 + afc/Makefile | 31 +++++ afc/afc-reply.json | 215 +++++++++++++++++++++++++++++++++ afc/afcd.c | 292 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile create mode 100644 afc/afc-reply.json create mode 100644 afc/afcd.c diff --git a/afc/.gitignore b/afc/.gitignore new file mode 100644 index 000000000..8d8cca905 --- /dev/null +++ b/afc/.gitignore @@ -0,0 +1 @@ +afcd diff --git a/afc/Makefile b/afc/Makefile new file mode 100644 index 000000000..a83bd01db --- /dev/null +++ b/afc/Makefile @@ -0,0 +1,31 @@ +ALL=afcd + +include ../src/build.rules + +CFLAGS += -I../src/utils +CFLAGS += -I../src + +OBJS=afcd.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif +OBJS += ../src/utils/os_$(CONFIG_OS).o + +LIBS += -lcurl + +_OBJS_VAR := OBJS +include ../src/objs.mk +afcd: $(OBJS) + $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS) + @$(E) " LD " $@ + +clean: common-clean + rm -f core *~ diff --git a/afc/afc-reply.json b/afc/afc-reply.json new file mode 100644 index 000000000..35882c74c --- /dev/null +++ b/afc/afc-reply.json @@ -0,0 +1,215 @@ +{ + "availableSpectrumInquiryResponses":[ + { + "availabilityExpireTime":"2023-02-23T12:53:18Z", + "availableChannelInfo":[ + { + "channelCfi":[ + 1, + 5, + 9, + 13, + 17, + 21, + 25, + 29, + 33, + 37, + 41, + 45, + 49, + 53, + 57, + 61, + 65, + 69, + 73, + 77, + 81, + 85, + 89, + 93, + 117, + 121, + 125, + 129, + 133, + 137, + 141, + 145, + 149, + 153, + 157, + 161, + 165, + 169, + 173, + 177, + 181 + ], + "globalOperatingClass":131, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 3, + 11, + 19, + 27, + 35, + 43, + 51, + 59, + 67, + 75, + 83, + 91, + 123, + 131, + 139, + 147, + 155, + 163, + 171, + 179 + ], + "globalOperatingClass":132, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 7, + 23, + 39, + 55, + 71, + 87, + 135, + 151, + 167 + ], + "globalOperatingClass":133, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 15, + 47, + 79, + 143 + ], + "globalOperatingClass":134, + "maxEirp":[ + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + ], + "globalOperatingClass":135, + "maxEirp":[ + ] + } + ], + "availableFrequencyInfo":[ + { + "frequencyRange":{ + "highFrequency":6425, + "lowFrequency":5925 + }, + "maxPSD":3.98970004336019 + }, + { + "frequencyRange":{ + "highFrequency":6865, + "lowFrequency":6525 + }, + "maxPSD":3.98970004336019 + } + ], + "requestId":"11235814", + "response":{ + "responseCode":0, + "shortDescription":"Success" + }, + "rulesetId":"US_47_CFR_PART_15_SUBPART_E" + } + ], + "version":"1.1" +} diff --git a/afc/afcd.c b/afc/afcd.c new file mode 100644 index 000000000..445db7c39 --- /dev/null +++ b/afc/afcd.c @@ -0,0 +1,292 @@ +/* + * Automated Frequency Coordination Daemon + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "utils/includes.h" +#include "utils/common.h" + +#define CURL_TIMEOUT 60 +#define AFCD_SOCK "afcd.sock" + +struct curl_ctx { + char *buf; + size_t buf_len; +}; + +static volatile bool exiting; + +static char *path = "/var/run"; +static char *bearer_token; +static char *url; +static int port = 443; + + +static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct curl_ctx *ctx = userdata; + char *buf; + + buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1); + if (!buf) + return 0; + + ctx->buf = buf; + os_memcpy(buf + ctx->buf_len, ptr, size * nmemb); + buf[ctx->buf_len + size * nmemb] = '\0'; + ctx->buf_len += size * nmemb; + + return size * nmemb; +} + + +static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request) +{ + struct curl_slist *headers = NULL; + CURL *curl; + int ret; + + wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url); + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (!curl) + return -ENOMEM; + + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, + "Content-Type: application/json"); + headers = curl_slist_append(headers, "charset: utf-8"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_PORT, port); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + afcd_curl_cb_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1"); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + if (bearer_token) + curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); + + ret = curl_easy_perform(curl); + if (ret != CURLE_OK) + wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s", + curl_easy_strerror(ret)); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return ret == CURLE_OK ? 0 : -EINVAL; +} + + +static void handle_term(int sig) +{ + wpa_printf(MSG_ERROR, "Received signal %d", sig); + exiting = true; +} + + +static void usage(void) +{ + wpa_printf(MSG_ERROR, + "%s:\n" + "afcd -u [-p][-t][-D][-P][-dB]", + __func__); +} + + +#define BUFSIZE 8192 +static int afcd_server_run(void) +{ + size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK); + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + int sockfd, ret = 0; + char *fname = NULL; + unsigned char *buf; + fd_set read_set; + + if (len >= sizeof(addr.sun_path)) + return -EINVAL; + + if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) + return -EINVAL; + + buf = os_malloc(BUFSIZE); + if (!buf) + return -ENOMEM; + + fname = os_malloc(len + 1); + if (!fname) { + ret = -ENOMEM; + goto free_buf; + } + + os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK); + fname[len] = '\0'; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating socket"); + ret = -errno; + goto unlink; + } + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed to bind socket"); + ret = -errno; + goto close; + } + + if (listen(sockfd, 10) < 0) { + wpa_printf(MSG_ERROR, "Failed to listen on socket"); + ret = -errno; + goto close; + } + + FD_ZERO(&read_set); + while (!exiting) { + socklen_t addr_len = sizeof(addr); + struct sockaddr_in6 c_addr; + struct timeval timeout = { + .tv_sec = 1, + }; + struct curl_ctx ctx = {}; + int fd; + + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Select failed on socket"); + ret = -errno; + break; + } + continue; + } + + if (!FD_ISSET(sockfd, &read_set)) + continue; + + fd = accept(sockfd, (struct sockaddr *)&c_addr, + &addr_len); + if (fd < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Failed accepting connections"); + ret = -errno; + break; + } + continue; + } + + os_memset(buf, 0, BUFSIZE); + if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) { + close(fd); + continue; + } + + wpa_printf(MSG_DEBUG, "Received request: %s", buf); + if (!afcd_send_request(&ctx, buf)) { + wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf); + send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL); + free(ctx.buf); + } + close(fd); + } +close: + close(sockfd); +unlink: + unlink(fname); + os_free(fname); +free_buf: + os_free(buf); + + return ret; +} + + +int main(int argc, char **argv) +{ + bool daemonize = false; + char *pid_file = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + int c = getopt(argc, argv, "u:p:t:D:P:hdB"); + + if (c < 0) + break; + + switch (c) { + case 'h': + usage(); + return 0; + case 'B': + daemonize = true; + break; + case 'D': + path = optarg; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 'u': + url = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'd': + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 't': + bearer_token = optarg; + break; + default: + usage(); + return -EINVAL; + } + } + + if (!url) { + usage(); + return -EINVAL; + } + + if (daemonize && os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -EINVAL; + } + + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); + + return afcd_server_run(); +} -- 2.44.0 From lorenzo at kernel.org Wed Apr 10 06:32:20 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Wed, 10 Apr 2024 15:32:20 +0200 Subject: [PATCH v4 2/4] hostapd: export hostapd_is_usable_chans utility routine In-Reply-To: References: Message-ID: <41a1c05a7a573828cd05359379d4e0383a6ed82b.1712755468.git.lorenzo@kernel.org> This is a preliminary patch to introduce AFC support. Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- src/ap/hw_features.c | 2 +- src/ap/hw_features.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index fd401d78a..e652d7504 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -995,7 +995,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface) * 0 = not usable * -1 = not currently usable due to 6 GHz NO-IR */ -static int hostapd_is_usable_chans(struct hostapd_iface *iface) +int hostapd_is_usable_chans(struct hostapd_iface *iface) { int secondary_freq; struct hostapd_channel_data *pri_chan; diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index c682c6d20..eeffb1abd 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface); int hostapd_hw_skip_mode(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); int hostapd_determine_mode(struct hostapd_iface *iface); +int hostapd_is_usable_chans(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface) return 0; } +static inline int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + return 1; +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ -- 2.44.0 From lorenzo at kernel.org Wed Apr 10 06:32:22 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Wed, 10 Apr 2024 15:32:22 +0200 Subject: [PATCH v4 4/4] hostapd: update TPE IE according to AFC In-Reply-To: References: Message-ID: <376e7c7722b77f65fa42d269e3a9d34b57ff7e6b.1712755468.git.lorenzo@kernel.org> Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- src/ap/afc.c | 37 +++++++++++++++++++++++++++++++++++ src/ap/hostapd.h | 9 +++++++++ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++----------------- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/ap/afc.c b/src/ap/afc.c index 40af3cb4a..7ba26f07c 100644 --- a/src/ap/afc.c +++ b/src/ap/afc.c @@ -976,3 +976,40 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface) chan->flag |= HOSTAPD_CHAN_DISABLED; } } + + +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + int i; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return -EINVAL; + + if (!iface->afc.data_valid) + return -EINVAL; + + if (psd) { + for (i = 0; i < iface->afc.num_freq_range; i++) { + struct afc_freq_range_elem *f; + + f = &iface->afc.freq_range[i]; + if (iface->freq >= f->low_freq && + iface->freq <= f->high_freq) { + *power = 2 * f->max_psd; + return 0; + } + } + } else { + for (i = 0; i < iface->afc.num_chan_info; i++) { + struct afc_chan_info_elem *c; + + c = &iface->afc.chan_info_list[i]; + if (c->chan == iface->conf->channel) { + *power = 2 * c->power; + return 0; + } + } + } + return -EINVAL; +} diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index b74dc75f6..de57249f8 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -730,10 +730,19 @@ struct hostapd_iface { /* hostapd.c */ #ifdef CONFIG_AFC +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power); int hostapd_afc_handle_request(struct hostapd_iface *iface); void hostapd_afc_stop(struct hostapd_iface *iface); void hostap_afc_disable_channels(struct hostapd_iface *iface); #else +static inline int +hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + return -EINVAL; +} + static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) { return 1; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index fcb2d14cf..1ec584b39 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -7113,42 +7113,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) */ if (is_6ghz_op_class(iconf->op_class)) { enum max_tx_pwr_interpretation tx_pwr_intrpn; + int err, max_eirp_psd, max_eirp_power; /* Same Maximum Transmit Power for all 20 MHz bands */ tx_pwr_count = 0; tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD; /* Default Transmit Power Envelope for Global Operating Class */ - if (hapd->iconf->reg_def_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_def_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + err = hostap_afc_get_chan_max_eirp_power(iface, true, + &max_eirp_psd); + if (err < 0) { + if (hapd->iconf->reg_def_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, - REG_DEFAULT_CLIENT, tx_pwr); + REG_DEFAULT_CLIENT, max_eirp_psd); /* Indoor Access Point must include an additional TPE for * subordinate devices */ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) { - /* TODO: Extract PSD limits from channel data */ - if (hapd->iconf->reg_sub_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + if (err < 0) { + /* non-AFC connection */ + if (hapd->iconf->reg_sub_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, REG_SUBORDINATE_CLIENT, - tx_pwr); + max_eirp_psd); } - if (iconf->reg_def_cli_eirp != -1 && - he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) - eid = hostapd_add_tpe_info( - eid, tx_pwr_count, REGULATORY_CLIENT_EIRP, - REG_DEFAULT_CLIENT, - hapd->iconf->reg_def_cli_eirp); + if (hostap_afc_get_chan_max_eirp_power(iface, false, + &max_eirp_power)) { + max_eirp_power = iconf->reg_def_cli_eirp; + if (max_eirp_power == -1 || + !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return eid; + } - return eid; + return hostapd_add_tpe_info(eid, tx_pwr_count, + REGULATORY_CLIENT_EIRP, + REG_DEFAULT_CLIENT, + max_eirp_power); } #endif /* CONFIG_IEEE80211AX */ -- 2.44.0 From lorenzo at kernel.org Wed Apr 10 06:32:21 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Wed, 10 Apr 2024 15:32:21 +0200 Subject: [PATCH v4 3/4] hostapd: ap: add AFC client support In-Reply-To: References: Message-ID: Introduce Automated Frequency Coordination (AFC) support for UNII-5 and UNII-7 6GHz bands. AFC client will connect to AFCD providing AP related parameter for AFC coordinator (e.g. geolocation, supported frequencies, ..). AFC is required for Standard Power Devices (SPDs) to determine a lists of channels and EIRP/PSD powers that are available in the 6GHz spectrum. AFC hostapd client is tested with AFC DUT Test Harness [0]. [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- hostapd/Makefile | 8 + hostapd/config_file.c | 261 +++++++++++ hostapd/defconfig | 3 + hostapd/hostapd.conf | 42 ++ src/ap/afc.c | 978 ++++++++++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 16 + src/ap/ap_config.h | 47 ++ src/ap/hostapd.c | 16 + src/ap/hostapd.h | 45 ++ src/ap/hw_features.c | 2 + 10 files changed, 1418 insertions(+) create mode 100644 src/ap/afc.c diff --git a/hostapd/Makefile b/hostapd/Makefile index ca4439234..78171025e 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -104,6 +104,14 @@ CFLAGS += -DCONFIG_TAXONOMY OBJS += ../src/ap/taxonomy.o endif +ifdef CONFIG_IEEE80211AX +ifdef CONFIG_AFC +CFLAGS += -DCONFIG_AFC +OBJS += ../src/ap/afc.o +LIBS += -ljson-c +endif +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += hapd_module_tests.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 56b2df3ae..c674e8ef7 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1281,6 +1281,190 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) return 0; } + +#ifdef CONFIG_AFC +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos) +{ + struct cert_id *c = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + c[i].rulset = os_malloc(os_strlen(pos) + 1); + if (!c[i].rulset) + goto error; + + os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + c[i].id = os_malloc(os_strlen(pos) + 1); + if (!c[i].id) + goto error; + + os_strlcpy(c[i].id, pos, os_strlen(pos) + 1); + pos = p; + } + + conf->afc.n_cert_ids = count; + conf->afc.cert_ids = c; + + return 0; + +error: + for (i = 0; i < count; i++) { + os_free(c[i].rulset); + os_free(c[i].id); + } + os_free(c); + + return -ENOMEM; +} + + +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data, + unsigned int *n_linear_polygon_data, + char *pos) +{ + struct afc_linear_polygon *d = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p, *end; + + d = os_realloc_array(d, count + 1, sizeof(*d)); + if (!d) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + d[i].longitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + d[i].latitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + } + + *n_linear_polygon_data = count; + *data = d; + + return 0; + +error: + os_free(d); + return -ENOMEM; +} + + +static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos) +{ + struct afc_freq_range *f = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + f[i].low_freq = atoi(pos); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + f[i].high_freq = atoi(pos); + pos = p; + } + + conf->afc.n_freq_range = count; + conf->afc.freq_range = f; + + return 0; + +error: + os_free(f); + return -ENOMEM; +} + + +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos) +{ + unsigned int *oc = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + oc = os_realloc_array(oc, count + 1, sizeof(*oc)); + if (!oc) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + oc[i] = atoi(pos); + pos = p; + } + + conf->afc.n_op_class = count; + conf->afc.op_class = oc; + + return 0; +} +#endif /* CONFIG_AFC */ #endif /* CONFIG_IEEE80211AX */ @@ -3862,6 +4046,83 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->unsol_bcast_probe_resp_interval = val; +#ifdef CONFIG_AFC + } else if (os_strcmp(buf, "afcd_sock") == 0) { + conf->afc.socket = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.socket) + return 1; + + os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_version") == 0) { + conf->afc.request.version = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.version) + return 1; + + os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_id") == 0) { + conf->afc.request.id = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.id) + return 1; + + os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_serial_number") == 0) { + conf->afc.request.sn = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.sn) + return 1; + + os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_cert_ids") == 0) { + if (hostapd_afc_parse_cert_ids(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_location_type") == 0) { + conf->afc.location.type = atoi(pos); + if (conf->afc.location.type != ELLIPSE && + conf->afc.location.type != LINEAR_POLYGON && + conf->afc.location.type != RADIAL_POLYGON) + return 1; + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) { + if (hostapd_afc_parse_position_data( + &conf->afc.location.linear_polygon_data, + &conf->afc.location.n_linear_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) { + if (hostapd_afc_parse_position_data( + (struct afc_linear_polygon **) + &conf->afc.location.radial_polygon_data, + &conf->afc.location.n_radial_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_major_axis") == 0) { + conf->afc.location.major_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_minor_axis") == 0) { + conf->afc.location.minor_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_orientation") == 0) { + conf->afc.location.orientation = atoi(pos); + } else if (os_strcmp(buf, "afc_height") == 0) { + char *end; + + conf->afc.location.height = strtod(pos, &end); + if (*end) + return 1; + } else if (os_strcmp(buf, "afc_height_type") == 0) { + conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.location.height_type) + return 1; + + os_strlcpy(conf->afc.location.height_type, pos, + os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) { + conf->afc.location.vertical_tolerance = atoi(pos); + } else if (os_strcmp(buf, "afc_min_power") == 0) { + conf->afc.min_power = atoi(pos); + } else if (os_strcmp(buf, "afc_freq_range") == 0) { + if (hostapd_afc_parse_freq_range(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_op_class") == 0) { + if (hostapd_afc_parse_op_class(conf, pos)) + return 1; +#endif /* CONFIG_AFC */ } else if (os_strcmp(buf, "mbssid") == 0) { int mbssid = atoi(pos); if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { diff --git a/hostapd/defconfig b/hostapd/defconfig index 550db697b..66bf894eb 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -425,3 +425,6 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Enable Automated Frequency Coordination for 6GHz outdoor +#CONFIG_AFC=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index d80abcac0..0d10998af 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0 # Valid range: 0..20 TUs; default is 0 (disabled) #unsol_bcast_probe_resp_interval=0 +##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands ####### + +# AFC daemon connection socket +#afcd_sock=/var/run/afcd.sock + +# AFC request identification parameters +#afc_request_version=1.1 +#afc_request_id=11235813 +#afc_serial_number=abcdefg +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000 +# +# AFC location type: +# 0 = ellipse +# 1 = linear polygon +# 2 = radial polygon +#afc_location_type=0 +# +# AFC ellipse or linear polygon coordinations +#afc_linear_polygon=-122.984157:37.425056 +# +# AFC radial polygon coordinations +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33 +# +# AFC ellipse major/minor axis and orientation +#afc_major_axis=100 +#afc_minor_axis=50 +#afc_orientation=70 +# +# AFC device elevation parameters +#afc_height=3.0 +#afc_height_type=AGL +#afc_vertical_tolerance=7 +# +# AFC minimum desired TX power (dbm) +#afc_min_power=24 +# +# AFC request frequency ranges +#afc_freq_range=5925:6425,6525:6875 +# +# AFC request operation classes +#afc_op_class=131,132,133,134,136 + ##### IEEE 802.11be related configuration ##################################### #ieee80211be: Whether IEEE 802.11be (EHT) is enabled diff --git a/src/ap/afc.c b/src/ap/afc.c new file mode 100644 index 000000000..40af3cb4a --- /dev/null +++ b/src/ap/afc.c @@ -0,0 +1,978 @@ +/* + * Automated Frequency Coordination + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "acs.h" +#include "hw_features.h" + +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ +#define HOSTAPD_AFC_BUFSIZE 4096 + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); + + +static struct json_object * +hostapd_afc_build_location_request(struct hostapd_iface *iface) +{ + struct json_object *location_obj, *center_obj, *ellipse_obj; + struct json_object *elevation_obj, *str_obj; + struct hostapd_config *iconf = iface->conf; + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); + + location_obj = json_object_new_object(); + if (!location_obj) + return NULL; + + if (iconf->afc.location.type != LINEAR_POLYGON) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[0]; + + ellipse_obj = json_object_new_object(); + if (!ellipse_obj) + goto error; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_object_add(ellipse_obj, "center", center_obj); + + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", str_obj); + } + + switch (iconf->afc.location.type) { + case LINEAR_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + outer_boundary_obj = json_object_new_object(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(location_obj, "linearPolygon", + outer_boundary_obj); + ellipse_obj = json_object_new_array(); + if (!ellipse_obj) + goto error; + + json_object_object_add(outer_boundary_obj, "outerBoundary", + ellipse_obj); + for (i = 0; + i < iconf->afc.location.n_linear_polygon_data; i++) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[i]; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_array_add(ellipse_obj, center_obj); + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", + str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", + str_obj); + } + break; + } + case RADIAL_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + json_object_object_add(location_obj, "radialPolygon", + ellipse_obj); + + outer_boundary_obj = json_object_new_array(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(ellipse_obj, "outerBoundary", + outer_boundary_obj); + for (i = 0; + i < iconf->afc.location.n_radial_polygon_data; i++) { + struct afc_radial_polygon *rp = + &iconf->afc.location.radial_polygon_data[i]; + struct json_object *angle_obj; + + angle_obj = json_object_new_object(); + if (!angle_obj) + goto error; + + json_object_array_add(outer_boundary_obj, angle_obj); + + str_obj = json_object_new_double(rp->angle); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "angle", str_obj); + str_obj = json_object_new_double(rp->length); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "length", str_obj); + } + break; + } + case ELLIPSE: + default: + json_object_object_add(location_obj, "ellipse", ellipse_obj); + + str_obj = json_object_new_int(iconf->afc.location.major_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "majorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.minor_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "minorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.orientation); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "orientation", str_obj); + break; + } + + elevation_obj = json_object_new_object(); + if (!elevation_obj) + goto error; + + json_object_object_add(location_obj, "elevation", + elevation_obj); + str_obj = json_object_new_double(iconf->afc.location.height); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "height", str_obj); + str_obj = json_object_new_string(iconf->afc.location.height_type); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "heightType", str_obj); + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "verticalUncertainty", + str_obj); + str_obj = json_object_new_int(is_ap_indoor); + if (!str_obj) + goto error; + + json_object_object_add(location_obj, "indoorDeployment", str_obj); + + return location_obj; + +error: + json_object_put(location_obj); + return NULL; +} + + +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class) +{ + struct json_object *chan_list_obj, *str_obj; + const struct oper_class_map *oper_class; + int chan_offset, chan; + + oper_class = get_oper_class(NULL, op_class); + if (!oper_class) + return NULL; + + chan_list_obj = json_object_new_array(); + if (!chan_list_obj) + return NULL; + + switch (op_class) { + case 132: /* 40MHz */ + chan_offset = 2; + break; + case 133: /* 80MHz */ + chan_offset = 6; + break; + case 134: /* 160MHz */ + chan_offset = 14; + break; + default: + chan_offset = 0; + break; + } + + for (chan = oper_class->min_chan; chan <= oper_class->max_chan; + chan += oper_class->inc) { + str_obj = json_object_new_int(chan + chan_offset); + if (!str_obj) { + json_object_put(chan_list_obj); + return NULL; + } + json_object_array_add(chan_list_obj, str_obj); + } + + return chan_list_obj; +} + + +static struct json_object * +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface) +{ + struct json_object *op_class_list_obj, *str_obj; + struct hostapd_config *iconf = iface->conf; + int i; + + op_class_list_obj = json_object_new_array(); + if (!op_class_list_obj) + return NULL; + + for (i = 0; i < iconf->afc.n_op_class; i++) { + struct json_object *op_class_obj, *chan_list_obj; + u8 op_class = iconf->afc.op_class[i]; + + if (!is_6ghz_op_class(op_class)) + continue; + + op_class_obj = json_object_new_object(); + if (!op_class_obj) + goto error; + + json_object_array_add(op_class_list_obj, op_class_obj); + str_obj = json_object_new_int(op_class); + if (!str_obj) + goto error; + + json_object_object_add(op_class_obj, "globalOperatingClass", + str_obj); + + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class); + if (!chan_list_obj) + goto error; + + json_object_object_add(op_class_obj, "channelCfi", + chan_list_obj); + } + + return op_class_list_obj; + +error: + json_object_put(op_class_list_obj); + return NULL; +} + + +static struct json_object * +hostapd_afc_build_request(struct hostapd_iface *iface) +{ + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj; + struct json_object *s2_obj, *str_obj, *location_obj; + struct hostapd_config *iconf = iface->conf; + struct json_object *op_class_list_obj; + int i; + + l1_obj = json_object_new_object(); + if (!l1_obj) + return NULL; + + if (iconf->afc.request.version) { + str_obj = json_object_new_string(iconf->afc.request.version); + if (!str_obj) + goto error; + + json_object_object_add(l1_obj, "version", str_obj); + } + + la1_obj = json_object_new_array(); + if (!la1_obj) + goto error; + + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests", + la1_obj); + l2_obj = json_object_new_object(); + if (!l2_obj) + goto error; + + json_object_array_add(la1_obj, l2_obj); + if (iconf->afc.request.id) { + str_obj = json_object_new_string(iconf->afc.request.id); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "requestId", str_obj); + } + + s2_obj = json_object_new_object(); + if (!s2_obj) + goto error; + + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj); + if (iconf->afc.request.sn) { + str_obj = json_object_new_string(iconf->afc.request.sn); + if (!str_obj) + goto error; + + json_object_object_add(s2_obj, "serialNumber", str_obj); + } + + la2_obj = json_object_new_array(); + if (!la2_obj) + goto error; + + json_object_object_add(s2_obj, "certificationId", la2_obj); + for (i = 0; i < iconf->afc.n_cert_ids; i++) { + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(la2_obj, obj); + str_obj = + json_object_new_string(iconf->afc.cert_ids[i].rulset); + if (!str_obj) + goto error; + + json_object_object_add(obj, "rulesetId", str_obj); + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id); + if (!str_obj) + goto error; + + json_object_object_add(obj, "id", str_obj); + } + + location_obj = hostapd_afc_build_location_request(iface); + if (!location_obj) + goto error; + + json_object_object_add(l2_obj, "location", location_obj); + str_obj = json_object_new_int(iconf->afc.min_power); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "minDesiredPower", str_obj); + + if (iconf->afc.n_freq_range) { + struct json_object *freq_obj; + + freq_obj = json_object_new_array(); + if (!freq_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredFrequencyRange", + freq_obj); + for (i = 0; i < iconf->afc.n_freq_range; i++) { + struct afc_freq_range *fr = &iconf->afc.freq_range[i]; + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(freq_obj, obj); + str_obj = json_object_new_int(fr->low_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "lowFrequency", str_obj); + str_obj = json_object_new_int(fr->high_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "highFrequency", str_obj); + } + } + + op_class_list_obj = hostapd_afc_build_req_chan_list(iface); + if (!op_class_list_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj); + + wpa_printf(MSG_DEBUG, "Pending AFC request: %s", + json_object_get_string(l1_obj)); + + return l1_obj; + +error: + json_object_put(l1_obj); + + return NULL; +} + + +static int +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct afc_freq_range_elem *f = NULL; + struct json_object *obj; + int i, count = 0; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableFrequencyInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct json_object *range_elem_obj, *freq_range_obj; + struct json_object *high_freq_obj, *low_freq_obj; + struct json_object *max_psd_obj; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!range_elem_obj) + continue; + + if (!json_object_object_get_ex(range_elem_obj, + "frequencyRange", + &freq_range_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "lowFrequency", + &low_freq_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "highFrequency", + &high_freq_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxPsd", + &max_psd_obj) && + !json_object_object_get_ex(range_elem_obj, "maxPSD", + &max_psd_obj)) + continue; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + f[count].low_freq = json_object_get_int(low_freq_obj); + f[count].high_freq = json_object_get_int(high_freq_obj); + f[count++].max_psd = json_object_get_int(max_psd_obj); + } + iface->afc.freq_range = f; + iface->afc.num_freq_range = count; + + return 0; +} + + +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list, + int *chan_list_size, u8 op_class, + int center_chan, int power) +{ + int num_low_subchan, ch, count = *chan_list_size; + struct afc_chan_info_elem *c = *chan_list; + + switch (op_class) { + case 132: /* 40MHz */ + num_low_subchan = 2; + break; + case 133: /* 80MHz */ + num_low_subchan = 6; + break; + case 134: /* 160MHz */ + num_low_subchan = 14; + break; + default: + num_low_subchan = 0; + break; + } + + for (ch = center_chan - num_low_subchan; + ch <= center_chan + num_low_subchan; ch += 4) { + int i; + + for (i = 0; i < count; i++) { + if (c[i].chan == ch) + break; + } + + if (i == count) { + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + c[count].chan = ch; + c[count++].power = power; + } + } + + *chan_list_size = count; + *chan_list = c; + + return 0; +} + + +static int +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct afc_chan_info_elem *c = NULL; + struct json_object *obj; + int i, count = 0; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableChannelInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct json_object *range_elem_obj, *op_class_obj; + struct json_object *chan_cfi_obj, *max_eirp_obj; + int ch, op_class; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!range_elem_obj) + continue; + + if (!json_object_object_get_ex(range_elem_obj, + "globalOperatingClass", + &op_class_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxEirp", + &max_eirp_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "channelCfi", + &chan_cfi_obj)) + continue; + + op_class = json_object_get_int(op_class_obj); + for (ch = 0; + ch < json_object_array_length(chan_cfi_obj); ch++) { + struct json_object *pwr_obj; + struct json_object *ch_obj; + int channel, power; + + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch); + if (!ch_obj) + continue; + + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch); + if (!pwr_obj) + continue; + + channel = json_object_get_int(ch_obj); + power = json_object_get_int(pwr_obj); + + hostad_afc_update_chan_info(&c, &count, op_class, + channel, power); + } + iface->afc.chan_info_list = c; + iface->afc.num_chan_info = count; + } + + return 0; +} + + +static int hostad_afc_get_timeout(struct json_object *obj) +{ + time_t t, now; + struct tm tm; + + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec) <= 0) + return HOSTAPD_AFC_TIMEOUT; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_isdst = -1; + t = mktime(&tm); + time(&now); + + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100; +} + + +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) +{ + struct json_object *payload_obj, *reply_obj, *version_obj; + struct hostapd_config *iconf = iface->conf; + int i, request_timeout = -1, ret = -EINVAL; + + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply); + payload_obj = json_tokener_parse(reply); + if (!payload_obj) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) + return -EINVAL; + + if (iconf->afc.request.version && + os_strcmp(iconf->afc.request.version, + json_object_get_string(version_obj))) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, + "availableSpectrumInquiryResponses", + &reply_obj)) + return -EINVAL; + + for (i = 0; i < json_object_array_length(reply_obj); i++) { + struct json_object *reply_elem_obj, *obj, *status_obj; + int j, status = -EINVAL; + + reply_elem_obj = json_object_array_get_idx(reply_obj, i); + if (!reply_elem_obj) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "requestId", + &obj)) + continue; + + if (iconf->afc.request.id && + os_strcmp(iconf->afc.request.id, + json_object_get_string(obj))) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId", + &obj)) + continue; + + for (j = 0; j < iconf->afc.n_cert_ids; j++) { + if (!os_strcmp(iconf->afc.cert_ids[j].rulset, + json_object_get_string(obj))) + break; + } + + if (j == iconf->afc.n_cert_ids) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "response", + &obj)) + continue; + + if (json_object_object_get_ex(obj, "shortDescription", + &status_obj)) + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s", + i, json_object_get_string(status_obj)); + + if (json_object_object_get_ex(obj, "responseCode", + &status_obj)) + status = json_object_get_int(status_obj); + + if (status < 0) + continue; + + if (hostad_afc_parse_available_freq_info(iface, + reply_elem_obj) || + hostad_afc_parse_available_chan_info(iface, + reply_elem_obj)) + continue; + + if (json_object_object_get_ex(reply_elem_obj, + "availabilityExpireTime", + &obj)) { + int timeout = hostad_afc_get_timeout(obj); + + if (request_timeout < 0 || timeout < request_timeout) + request_timeout = timeout; + } + + ret = status; + } + + iface->afc.data_valid = true; + iface->afc.timeout = request_timeout; + if (iface->afc.timeout < 0) + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + + return ret; +} + + +static int hostapd_afc_send_receive(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + json_object *request_obj = NULL; + struct timeval sock_timeout = { + .tv_sec = 5, + }; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + char buf[HOSTAPD_AFC_BUFSIZE] = {}; + const char *request; + int sockfd, ret; + fd_set read_set; + + if (iface->afc.data_valid) { + /* AFC data already downloaded from the server */ + return 0; + } + + if (!iconf->afc.socket) { + wpa_printf(MSG_ERROR, "Missing AFC socket string"); + return -EINVAL; + } + + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", + iconf->afc.socket); + return -EINVAL; + } + + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating AFC socket"); + return sockfd; + } + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed connecting AFC socket"); + ret = -EIO; + goto close_sock; + } + + request_obj = hostapd_afc_build_request(iface); + if (!request_obj) { + ret = -ENOMEM; + goto close_sock; + } + + request = json_object_to_json_string(request_obj); + if (send(sockfd, request, strlen(request), 0) < 0) { + wpa_printf(MSG_ERROR, "Failed sending AFC request"); + ret = -EIO; + goto close_sock; + } + + FD_ZERO(&read_set); + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { + wpa_printf(MSG_ERROR, "Select failed on AFC socket"); + ret = -errno; + goto close_sock; + } + + if (!FD_ISSET(sockfd, &read_set)) { + ret = -EIO; + goto close_sock; + } + + ret = recv(sockfd, buf, sizeof(buf) - 1, 0); + if (ret <= 0) + goto close_sock; + + ret = hostapd_afc_parse_reply(iface, buf); +close_sock: + json_object_put(request_obj); + close(sockfd); + + return ret; +} + + +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) +{ + const struct oper_class_map *oper_class; + int ch; + + oper_class = get_oper_class(NULL, iface->conf->op_class); + if (!oper_class) + return false; + + for (ch = oper_class->min_chan; ch <= oper_class->max_chan; + ch += oper_class->inc) { + struct hostapd_hw_modes *mode = iface->current_mode; + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if (chan->chan == ch && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + return true; + } + } + + return false; +} + + +int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + int ret; + + /* AFC is required just for standard power AP */ + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return 1; + + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) + return 1; + + if (iface->state == HAPD_IFACE_ACS) + return 1; + + ret = hostapd_afc_send_receive(iface); + if (ret < 0) { + /* + * If the connection to the AFCD failed, resched for a + * future attempt. + */ + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret); + if (ret == -EIO) + ret = 0; + goto resched; + } + + hostap_afc_disable_channels(iface); + if (!hostapd_afc_has_usable_chans(iface)) + goto resched; + + /* Trigger an ACS freq scan */ + iconf->channel = 0; + iface->freq = 0; + + if (acs_init(iface) != HOSTAPD_CHAN_ACS) { + wpa_printf(MSG_ERROR, "Could not start ACS"); + ret = -EINVAL; + } + +resched: + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); + + return ret; +} + + +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) +{ + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); + + iface->afc.num_freq_range = 0; + iface->afc.num_chan_info = 0; + + iface->afc.chan_info_list = NULL; + iface->afc.freq_range = NULL; + + iface->afc.data_valid = false; +} + + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + bool restart_iface = true; + + hostapd_afc_delete_data_from_server(iface); + if (iface->state != HAPD_IFACE_ENABLED) { + /* Hostapd is not fully enabled yet, toggle the interface */ + goto restart_interface; + } + + if (hostapd_afc_send_receive(iface) < 0 || + hostapd_get_hw_features(iface)) { + restart_iface = false; + goto restart_interface; + } + + if (hostapd_is_usable_chans(iface)) + goto resched; + + restart_iface = hostapd_afc_has_usable_chans(iface); + if (restart_iface) { + /* Trigger an ACS freq scan */ + iface->conf->channel = 0; + iface->freq = 0; + } + +restart_interface: + hostapd_disable_iface(iface); + if (restart_iface) + hostapd_enable_iface(iface); +resched: + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostapd_afc_stop(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode; + int i; + + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A) + return; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return; + + if (!iface->afc.data_valid) + return; + + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A]; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + int j; + + if (!is_6ghz_freq(chan->freq)) + continue; + + for (j = 0; j < iface->afc.num_freq_range; j++) { + if (chan->freq >= iface->afc.freq_range[j].low_freq && + chan->freq <= iface->afc.freq_range[j].high_freq) + break; + } + + if (j != iface->afc.num_freq_range) + continue; + + for (j = 0; j < iface->afc.num_chan_info; j++) { + if (chan->chan == iface->afc.chan_info_list[j].chan) + break; + } + + if (j != iface->afc.num_chan_info) + continue; + + chan->flag |= HOSTAPD_CHAN_DISABLED; + } +} diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 1a18df617..ca67aeb41 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf) #endif /* CONFIG_ACS */ wpabuf_free(conf->lci); wpabuf_free(conf->civic); +#ifdef CONFIG_AFC + os_free(conf->afc.socket); + os_free(conf->afc.request.version); + os_free(conf->afc.request.id); + os_free(conf->afc.request.sn); + for (i = 0; i < conf->afc.n_cert_ids; i++) { + os_free(conf->afc.cert_ids[i].rulset); + os_free(conf->afc.cert_ids[i].id); + } + os_free(conf->afc.cert_ids); + os_free(conf->afc.location.height_type); + os_free(conf->afc.location.linear_polygon_data); + os_free(conf->afc.location.radial_polygon_data); + os_free(conf->afc.freq_range); + os_free(conf->afc.op_class); +#endif /* CONFIG_AFC */ os_free(conf); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 754d55331..2330163c4 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1225,6 +1225,53 @@ struct hostapd_config { MBSSID_ENABLED = 1, ENHANCED_MBSSID_ENABLED = 2, } mbssid; + +#ifdef CONFIG_AFC + struct { + char *socket; + struct { + char *version; + char *id; + char *sn; + } request; + unsigned int n_cert_ids; + struct cert_id { + char *rulset; + char *id; + } *cert_ids; + struct { + enum afc_location_type { + ELLIPSE, + LINEAR_POLYGON, + RADIAL_POLYGON, + } type; + unsigned int n_linear_polygon_data; + struct afc_linear_polygon { + double longitude; + double latitude; + } *linear_polygon_data; + unsigned int n_radial_polygon_data; + struct afc_radial_polygon { + double length; + double angle; + } *radial_polygon_data; + int major_axis; + int minor_axis; + int orientation; + double height; + char *height_type; + int vertical_tolerance; + } location; + unsigned int n_freq_range; + struct afc_freq_range { + int low_freq; + int high_freq; + } *freq_range; + unsigned int n_op_class; + unsigned int *op_class; + int min_power; + } afc; +#endif /* CONFIG_AFC */ }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index f8cb6432d..940a2a0af 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -714,6 +714,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + hostapd_afc_stop(iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, NULL); @@ -2454,6 +2455,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } #endif /* CONFIG_MESH */ +#ifdef CONFIG_IEEE80211AX + /* check AFC for 6GHz channels. */ + res = hostapd_afc_handle_request(iface); + if (res <= 0) { + if (res < 0) + goto fail; + return res; + } +#endif /* CONFIG_IEEE80211AX */ + if (!delay_apply_cfg && hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, @@ -2852,6 +2863,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) hostapd_set_state(iface, HAPD_IFACE_DISABLED); + hostapd_afc_stop(iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; iface->is_no_ir = false; @@ -2925,6 +2937,10 @@ void hostapd_interface_free(struct hostapd_iface *iface) __func__, iface->bss[j]); os_free(iface->bss[j]); } +#ifdef CONFIG_AFC + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); +#endif hostapd_cleanup_iface(iface); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index affe4f604..b74dc75f6 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -700,9 +700,54 @@ struct hostapd_iface { /* Configured freq of interface is NO_IR */ bool is_no_ir; + +#ifdef CONFIG_AFC + struct { + int timeout; + unsigned int num_freq_range; + struct afc_freq_range_elem { + int low_freq; + int high_freq; + /** + * max eirp power spectral density received from + * the AFC coordinator for this band + */ + int max_psd; + } *freq_range; + unsigned int num_chan_info; + struct afc_chan_info_elem { + int chan; + /** + * max eirp power received from the AFC coordinator + * for this channel + */ + int power; + } *chan_info_list; + bool data_valid; + } afc; +#endif /* CONFIG_AFC */ }; /* hostapd.c */ +#ifdef CONFIG_AFC +int hostapd_afc_handle_request(struct hostapd_iface *iface); +void hostapd_afc_stop(struct hostapd_iface *iface); +void hostap_afc_disable_channels(struct hostapd_iface *iface); +#else +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + return 1; +} + +static inline void hostapd_afc_stop(struct hostapd_iface *iface) +{ +} + +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ +} +#endif /* CONFIG_AFC */ + int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index e652d7504..222f3dc05 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) iface->hw_features = modes; iface->num_hw_features = num_modes; + hostap_afc_disable_channels(iface); + for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; int dfs_enabled = hapd->iconf->ieee80211h && -- 2.44.0 From satyaprakash.developer.unix at gmail.com Wed Apr 10 09:11:18 2024 From: satyaprakash.developer.unix at gmail.com (Satya Prakash Prasad) Date: Wed, 10 Apr 2024 21:41:18 +0530 Subject: EAP TLS - Hostapd In-Reply-To: <4476.1712751085@obiwan.sandelman.ca> References: <4476.1712751085@obiwan.sandelman.ca> Message-ID: Hi, Many thanks for the information as provided. Yes we are trying data in-between server and peer after the secure connection is established.. In the same reference, what are the EAP Peer APIs used for sending and reading data in between EAP peer <-> Server after a successful secured connection and how do they verify an active connection (other end is available)? How do we know the connection is over / closed at peer side? How is data extracted from EAP TLS secure connection messages? Thanks in advance. Regards, Prakash On Wed, Apr 10, 2024 at 5:41?PM Michael Richardson wrote: > > > Satya Prakash Prasad wrote: > > After the EAP success case, how peer / server side will read / write > > data - which APIs code implements the same - also how do we verify that > > EAP authentication was successful before each write / data? > > After the handshake, session keys are derived from the EAP session using the > MSK, and installed into the supplicant (and authenticator) kernel. > > But, perhaps I mis-undertand your question, are you trying to send more > traffic within the EAP-TLS connection? > > From aland at deployingradius.com Wed Apr 10 10:55:47 2024 From: aland at deployingradius.com (Alan DeKok) Date: Wed, 10 Apr 2024 13:55:47 -0400 Subject: EAP TLS - Hostapd In-Reply-To: References: <4476.1712751085@obiwan.sandelman.ca> Message-ID: <91E927FF-A4B7-44AF-AFB9-73BB1328A521@deployingradius.com> On Apr 10, 2024, at 12:11 PM, Satya Prakash Prasad wrote: > Many thanks for the information as provided. Yes we are trying data > in-between server and peer after the secure connection is > established.. This is more of a protocol question than a hostap question. EAP-TLS does not support sending data inside of the TLS tunnel. You might be able to send some data inside of a TTLS tunnel, but that is very limited. EAP-TLS, TTLS, etc. are not designed as general-purpose transport protocols. Perhaps you could describe what data you need to send, and why. Alan DeKok. From gs-lists-hostap at gluelogic.com Wed Apr 10 16:03:57 2024 From: gs-lists-hostap at gluelogic.com (Glenn Strauss) Date: Wed, 10 Apr 2024 19:03:57 -0400 Subject: EAP TLS - Hostapd In-Reply-To: <91E927FF-A4B7-44AF-AFB9-73BB1328A521@deployingradius.com> References: <4476.1712751085@obiwan.sandelman.ca> <91E927FF-A4B7-44AF-AFB9-73BB1328A521@deployingradius.com> Message-ID: On Wed, Apr 10, 2024 at 01:55:47PM -0400, Alan DeKok wrote: > On Apr 10, 2024, at 12:11 PM, Satya Prakash Prasad wrote: > > Many thanks for the information as provided. Yes we are trying data > > in-between server and peer after the secure connection is > > established.. > > This is more of a protocol question than a hostap question. > > EAP-TLS does not support sending data inside of the TLS tunnel. > > You might be able to send some data inside of a TTLS tunnel, but that is very limited. EAP-TLS, TTLS, etc. are not designed as general-purpose transport protocols. > > Perhaps you could describe what data you need to send, and why. > > Alan DeKok. Satya has not previously written *anything* with hostap code, and has not previously written *anything* with mbedtls. Satya is an undergraduate student and their questions appear to be part of a group project. > On Wed, Mar 27, 2024 at 09:35:15PM +0530, Satya Prakash Prasad wrote: > > I am an IT student doing this project to gain knowledge and experience.on > > Arduino Arm board. ** ** Please do continue to help them if you like! ** After sending a personal email to me, I asked for more info. Satya provided this: > On Tue, Mar 26, 2024 at 19:45:35PM +0530, Satya Prakash Prasad wrote: > We are building EAP functionality / feature using MBedTLS into our embedded > device so there is no concept of process - it's just FreeRTOS running in it > Our device will act as a peer / client device where any authorized > supplicant / other device can connect to access our device information. > > So we are running EAP as in a thread and on a connection we need to > authorize the connection to allow access to our device parameters. Whether naivety or not, I felt they displayed little respect for the **time** of the professionals to which the questions have been sent. (Case in point, I am providing the context to this thread, which they failed to do, even after having failed to do the same when contacting me privately two weeks ago.) After I pointed Satya to tls_init() description in hostap/src/crypto/tls.h: > > > On Wed, Mar 27, 2024 at 02:44:14PM +0530, Satya Prakash Prasad wrote: > > > > Hi Glenn, > > > > > > > > Many thanks for your prompt reply and indeed this is my first effort in > > > > programming with MbedTLS and EAP modules. > > > > > > > > As rightly stated I am not able to understand the description of the > > > > interface for tls_init() and have no relationship with prplfoundation - > > > > it's just that for an embedded product being developed as part of > > > > undergraduate program project we are planning to integrate EAP TLS > > > > functionality using third party's code available. > > > > > > > > Hence I also do not have much idea on mbedtls_ssl_context either but need > > > > to self educate on the same. Can you please help us how to start and > > > > understand each API description / implementation? > > > > > > > > Please guide us accordingly. > > > > > > > > Thanks in advance and please let me know incase of any issues or concerns. > > > > > > > > Regards, > > > > Prakash From satyaprakash.developer.unix at gmail.com Wed Apr 10 17:59:54 2024 From: satyaprakash.developer.unix at gmail.com (Satya Prakash Prasad) Date: Thu, 11 Apr 2024 06:29:54 +0530 Subject: EAP TLS - Hostapd In-Reply-To: References: <4476.1712751085@obiwan.sandelman.ca> <91E927FF-A4B7-44AF-AFB9-73BB1328A521@deployingradius.com> Message-ID: Hi, Many thanks to Glenn and others for providing information. As I understand then after the successful authentication the supplicant is then allowed into the network . I was assuming that the device will send a request like to read / write to a file - If my understanding is now correct please confirm who will validate each time that after successful authentication for supplicant authentication has been done. Please do provide some information as to what happens after the supplicant successful authentication, specifically how each time the supplicant is verified as a verified device. Regards, Prakash On Thu, Apr 11, 2024 at 4:34?AM Glenn Strauss wrote: > > On Wed, Apr 10, 2024 at 01:55:47PM -0400, Alan DeKok wrote: > > On Apr 10, 2024, at 12:11 PM, Satya Prakash Prasad wrote: > > > Many thanks for the information as provided. Yes we are trying data > > > in-between server and peer after the secure connection is > > > established.. > > > > This is more of a protocol question than a hostap question. > > > > EAP-TLS does not support sending data inside of the TLS tunnel. > > > > You might be able to send some data inside of a TTLS tunnel, but that is very limited. EAP-TLS, TTLS, etc. are not designed as general-purpose transport protocols. > > > > Perhaps you could describe what data you need to send, and why. > > > > Alan DeKok. > > Satya has not previously written *anything* with hostap code, > and has not previously written *anything* with mbedtls. > > Satya is an undergraduate student and their questions appear to > be part of a group project. > > > On Wed, Mar 27, 2024 at 09:35:15PM +0530, Satya Prakash Prasad wrote: > > > I am an IT student doing this project to gain knowledge and experience.on > > > Arduino Arm board. > > ** > ** Please do continue to help them if you like! > ** > > After sending a personal email to me, I asked for more info. > Satya provided this: > > > On Tue, Mar 26, 2024 at 19:45:35PM +0530, Satya Prakash Prasad wrote: > > We are building EAP functionality / feature using MBedTLS into our embedded > > device so there is no concept of process - it's just FreeRTOS running in it > > Our device will act as a peer / client device where any authorized > > supplicant / other device can connect to access our device information. > > > > So we are running EAP as in a thread and on a connection we need to > > authorize the connection to allow access to our device parameters. > > Whether naivety or not, I felt they displayed little respect for the > **time** of the professionals to which the questions have been sent. > (Case in point, I am providing the context to this thread, which they > failed to do, even after having failed to do the same when contacting > me privately two weeks ago.) > > After I pointed Satya to tls_init() description in > hostap/src/crypto/tls.h: > > > > > On Wed, Mar 27, 2024 at 02:44:14PM +0530, Satya Prakash Prasad wrote: > > > > > Hi Glenn, > > > > > > > > > > Many thanks for your prompt reply and indeed this is my first effort in > > > > > programming with MbedTLS and EAP modules. > > > > > > > > > > As rightly stated I am not able to understand the description of the > > > > > interface for tls_init() and have no relationship with prplfoundation - > > > > > it's just that for an embedded product being developed as part of > > > > > undergraduate program project we are planning to integrate EAP TLS > > > > > functionality using third party's code available. > > > > > > > > > > Hence I also do not have much idea on mbedtls_ssl_context either but need > > > > > to self educate on the same. Can you please help us how to start and > > > > > understand each API description / implementation? > > > > > > > > > > Please guide us accordingly. > > > > > > > > > > Thanks in advance and please let me know incase of any issues or concerns. > > > > > > > > > > Regards, > > > > > Prakash From jesus.manzano at galgus.ai Sun Apr 14 06:19:25 2024 From: jesus.manzano at galgus.ai (=?UTF-8?Q?Jes=C3=BAs_Fern=C3=A1ndez_Manzano?=) Date: Sun, 14 Apr 2024 15:19:25 +0200 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> References: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> Message-ID: <9c773d9b-7a67-453c-9e0a-4366665e7e33@galgus.ai> Hi all, El 10/4/24 a las 15:32, Lorenzo Bianconi escribi?: > + headers = curl_slist_append(headers, "charset: utf-8"); After building the headers with curl_slist_append() and sending the message with curl_easy_perform(), you must free them with curl_slist_free_all() or the memory will be leaked. See: - https://curl.se/libcurl/c/curl_slist_append.html - https://curl.se/libcurl/c/curl_slist_free_all.html Regards, Jes?s From chaitanya.mgit at gmail.com Sun Apr 14 10:28:29 2024 From: chaitanya.mgit at gmail.com (Krishna Chaitanya) Date: Sun, 14 Apr 2024 22:58:29 +0530 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> References: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> Message-ID: On Wed, Apr 10, 2024 at 7:05?PM Lorenzo Bianconi wrote: > + "requestId":"11235814", 11235813 to match the default request ID > + > +static void usage(void) > +{ > + wpa_printf(MSG_ERROR, > + "%s:\n" > + "afcd -u [-p][-t][-D][-P][-dB]", > + __func__); > +} missing `-d` usage > + Also, for setting `Content-Length` I had to use the below script, should we add this as well or at least put it in a README? $ cat afc_server_sim.sh #!/bin/bash # Define the JSON content json_content=$(cat afc-reply.json) # Calculate the length of the JSON content content_length=$(echo "$json_content" | wc -m) # Create the HTTP response http_response="HTTP/1.1 200 OK Content-Type: application/json Content-Length: $content_length $json_content" # Write the HTTP response to a file echo -e "$http_response" > response.txt # Use netcat to listen on a specific port and send the response to any incoming requests while true; do nc -l 9999 < response.txt; done ### END OF SCRIPT## and then I run `sudo ./afcd -u http://localhost -p 9999 -ddd` From arun.jose.wg at bp.renesas.com Mon Apr 15 01:48:21 2024 From: arun.jose.wg at bp.renesas.com (arun.jose.wg at bp.renesas.com) Date: Mon, 15 Apr 2024 14:18:21 +0530 Subject: [PATCH v1 0/1] Made WPA_EVENT_CHANNEL_SWITCH event accessible to action scripts Message-ID: <20240415084822.611328-1-arun.jose.wg@bp.renesas.com> From: "arun.jose" Modified the code in wpa_cli.c to make the channel switch complete event, WPA_EVENT_CHANNEL_SWITCH accessible to the action script. Tested-by: arun.jose arun.jose (1): wpa_cli: Modified to make the event WPA_EVENT_CHANNEL_SWITCH accessible to action scripts wpa_supplicant/wpa_cli.c | 2 ++ 1 file changed, 2 insertions(+) -- 2.25.1 From arun.jose.wg at bp.renesas.com Mon Apr 15 01:48:22 2024 From: arun.jose.wg at bp.renesas.com (arun.jose.wg at bp.renesas.com) Date: Mon, 15 Apr 2024 14:18:22 +0530 Subject: [PATCH v1 1/1] wpa_cli: Modified to make the event WPA_EVENT_CHANNEL_SWITCH accessible to action scripts In-Reply-To: <20240415084822.611328-1-arun.jose.wg@bp.renesas.com> References: <20240415084822.611328-1-arun.jose.wg@bp.renesas.com> Message-ID: <20240415084822.611328-2-arun.jose.wg@bp.renesas.com> From: "arun.jose" Modified the code in wpa_cli.c to make the channel switch complete event, WPA_EVENT_CHANNEL_SWITCH accessible to the action script. Signed-off-by: arun.jose --- wpa_supplicant/wpa_cli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 60f85624f..7d4cb4564 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -4380,6 +4380,8 @@ static void wpa_cli_action_process(const char *msg) } } else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH_STARTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_starts(pos, AP_EVENT_ENABLED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_starts(pos, AP_EVENT_DISABLED)) { -- 2.25.1 From bapclenet at gmail.com Thu Apr 11 07:18:36 2024 From: bapclenet at gmail.com (Baptiste Clenet) Date: Thu, 11 Apr 2024 16:18:36 +0200 Subject: wpa_config_get_line can badly parse a password with # and " Message-ID: Hi, Here is a password that wpa_config_get_line fails to parse sae_password="0Y11D#E$4C"P/A;A4D#2x6D"" I made an example with several password on https://onlinegdb.com/9YFBl7Zba By patch in this example I mentionned patch from xinpeng wang (sha:aca4d4963a65e49614ed8cd52836a2619775c1f6) I suggest to revert the patch and to not allow " in comment # I didn't find any pattern that match my password and what xinpeng wang wanted (a=b #"abc") I attach a patch that revert xinpeng wang patch. What do you think? Feel free to include my patch, I'm not used to send patch as email Thanks, -- Baptiste -------------- next part -------------- A non-text attachment was scrubbed... Name: Changes-On-147f83.patch Type: text/x-patch Size: 768 bytes Desc: not available URL: From chaitanya.mgit at gmail.com Sun Apr 14 10:18:43 2024 From: chaitanya.mgit at gmail.com (Krishna Chaitanya) Date: Sun, 14 Apr 2024 22:48:43 +0530 Subject: [PATCH v4 3/4] hostapd: ap: add AFC client support In-Reply-To: References: Message-ID: On Wed, Apr 10, 2024 at 7:05?PM Lorenzo Bianconi wrote: > > Introduce Automated Frequency Coordination (AFC) support for UNII-5 and > UNII-7 6GHz bands. > AFC client will connect to AFCD providing AP related parameter for AFC > coordinator (e.g. geolocation, supported frequencies, ..). > AFC is required for Standard Power Devices (SPDs) to determine a lists > of channels and EIRP/PSD powers that are available in the 6GHz spectrum. > AFC hostapd client is tested with AFC DUT Test Harness [0]. > > [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main > > Tested-by: Felix Fietkau > Tested-by: Allen Ye > Signed-off-by: Lorenzo Bianconi > --- > hostapd/Makefile | 8 + > hostapd/config_file.c | 261 +++++++++++ > hostapd/defconfig | 3 + > hostapd/hostapd.conf | 42 ++ > src/ap/afc.c | 978 ++++++++++++++++++++++++++++++++++++++++++ > src/ap/ap_config.c | 16 + > src/ap/ap_config.h | 47 ++ > src/ap/hostapd.c | 16 + > src/ap/hostapd.h | 45 ++ > src/ap/hw_features.c | 2 + > 10 files changed, 1418 insertions(+) > create mode 100644 src/ap/afc.c > > diff --git a/hostapd/Makefile b/hostapd/Makefile > index ca4439234..78171025e 100644 > --- a/hostapd/Makefile > +++ b/hostapd/Makefile > @@ -104,6 +104,14 @@ CFLAGS += -DCONFIG_TAXONOMY > OBJS += ../src/ap/taxonomy.o > endif > > +ifdef CONFIG_IEEE80211AX > +ifdef CONFIG_AFC > +CFLAGS += -DCONFIG_AFC > +OBJS += ../src/ap/afc.o > +LIBS += -ljson-c > +endif > +endif > + > ifdef CONFIG_MODULE_TESTS > CFLAGS += -DCONFIG_MODULE_TESTS > OBJS += hapd_module_tests.o > diff --git a/hostapd/config_file.c b/hostapd/config_file.c > index 56b2df3ae..c674e8ef7 100644 > --- a/hostapd/config_file.c > +++ b/hostapd/config_file.c > @@ -1281,6 +1281,190 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) > return 0; > } > > + > +#ifdef CONFIG_AFC > +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos) > +{ > + struct cert_id *c = NULL; > + int i, count = 0; > + > + while (pos && pos[0]) { > + char *p; > + > + c = os_realloc_array(c, count + 1, sizeof(*c)); > + if (!c) > + return -ENOMEM; > + > + i = count; > + count++; > + > + p = os_strchr(pos, ':'); > + if (!p) > + goto error; > + > + *p++ = '\0'; > + if (!p || !p[0]) > + goto error; > + > + c[i].rulset = os_malloc(os_strlen(pos) + 1); > + if (!c[i].rulset) > + goto error; > + > + os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1); > + pos = p; > + p = os_strchr(pos, ','); > + if (p) > + *p++ = '\0'; > + > + c[i].id = os_malloc(os_strlen(pos) + 1); > + if (!c[i].id) > + goto error; > + > + os_strlcpy(c[i].id, pos, os_strlen(pos) + 1); > + pos = p; > + } > + > + conf->afc.n_cert_ids = count; > + conf->afc.cert_ids = c; > + > + return 0; > + > +error: > + for (i = 0; i < count; i++) { > + os_free(c[i].rulset); > + os_free(c[i].id); > + } > + os_free(c); > + > + return -ENOMEM; > +} > + > + > +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data, > + unsigned int *n_linear_polygon_data, > + char *pos) > +{ > + struct afc_linear_polygon *d = NULL; > + int i, count = 0; > + > + while (pos && pos[0]) { > + char *p, *end; > + > + d = os_realloc_array(d, count + 1, sizeof(*d)); > + if (!d) > + return -ENOMEM; > + > + i = count; > + count++; > + > + p = os_strchr(pos, ':'); > + if (!p) > + goto error; > + > + *p++ = '\0'; > + if (!p || !p[0]) > + goto error; > + > + d[i].longitude = strtod(pos, &end); > + if (*end) > + goto error; > + > + pos = p; > + p = os_strchr(pos, ','); > + if (p) > + *p++ = '\0'; > + > + d[i].latitude = strtod(pos, &end); > + if (*end) > + goto error; > + > + pos = p; > + } > + > + *n_linear_polygon_data = count; > + *data = d; > + > + return 0; > + > +error: > + os_free(d); > + return -ENOMEM; > +} > + > + > +static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos) > +{ > + struct afc_freq_range *f = NULL; > + int i, count = 0; > + > + while (pos && pos[0]) { > + char *p; > + > + f = os_realloc_array(f, count + 1, sizeof(*f)); > + if (!f) > + return -ENOMEM; > + > + i = count; > + count++; > + > + p = os_strchr(pos, ':'); > + if (!p) > + goto error; > + > + *p++ = '\0'; > + if (!p || !p[0]) > + goto error; > + > + f[i].low_freq = atoi(pos); > + pos = p; > + p = os_strchr(pos, ','); > + if (p) > + *p++ = '\0'; > + > + f[i].high_freq = atoi(pos); > + pos = p; > + } > + > + conf->afc.n_freq_range = count; > + conf->afc.freq_range = f; > + > + return 0; > + > +error: > + os_free(f); > + return -ENOMEM; > +} > + > + > +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos) > +{ > + unsigned int *oc = NULL; > + int i, count = 0; > + > + while (pos && pos[0]) { > + char *p; > + > + oc = os_realloc_array(oc, count + 1, sizeof(*oc)); > + if (!oc) > + return -ENOMEM; > + > + i = count; > + count++; > + > + p = os_strchr(pos, ','); > + if (p) > + *p++ = '\0'; > + > + oc[i] = atoi(pos); > + pos = p; > + } > + > + conf->afc.n_op_class = count; > + conf->afc.op_class = oc; > + > + return 0; > +} > +#endif /* CONFIG_AFC */ > #endif /* CONFIG_IEEE80211AX */ > > > @@ -3862,6 +4046,83 @@ static int hostapd_config_fill(struct hostapd_config *conf, > return 1; > } > bss->unsol_bcast_probe_resp_interval = val; > +#ifdef CONFIG_AFC > + } else if (os_strcmp(buf, "afcd_sock") == 0) { > + conf->afc.socket = os_malloc(os_strlen(pos) + 1); > + if (!conf->afc.socket) > + return 1; > + > + os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1); > + } else if (os_strcmp(buf, "afc_request_version") == 0) { > + conf->afc.request.version = os_malloc(os_strlen(pos) + 1); > + if (!conf->afc.request.version) > + return 1; > + > + os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1); > + } else if (os_strcmp(buf, "afc_request_id") == 0) { > + conf->afc.request.id = os_malloc(os_strlen(pos) + 1); > + if (!conf->afc.request.id) > + return 1; > + > + os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1); > + } else if (os_strcmp(buf, "afc_serial_number") == 0) { > + conf->afc.request.sn = os_malloc(os_strlen(pos) + 1); > + if (!conf->afc.request.sn) > + return 1; > + > + os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1); > + } else if (os_strcmp(buf, "afc_cert_ids") == 0) { > + if (hostapd_afc_parse_cert_ids(conf, pos)) > + return 1; > + } else if (os_strcmp(buf, "afc_location_type") == 0) { > + conf->afc.location.type = atoi(pos); > + if (conf->afc.location.type != ELLIPSE && > + conf->afc.location.type != LINEAR_POLYGON && > + conf->afc.location.type != RADIAL_POLYGON) > + return 1; > + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) { > + if (hostapd_afc_parse_position_data( > + &conf->afc.location.linear_polygon_data, > + &conf->afc.location.n_linear_polygon_data, > + pos)) > + return 1; > + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) { > + if (hostapd_afc_parse_position_data( > + (struct afc_linear_polygon **) > + &conf->afc.location.radial_polygon_data, > + &conf->afc.location.n_radial_polygon_data, > + pos)) > + return 1; > + } else if (os_strcmp(buf, "afc_major_axis") == 0) { > + conf->afc.location.major_axis = atoi(pos); > + } else if (os_strcmp(buf, "afc_minor_axis") == 0) { > + conf->afc.location.minor_axis = atoi(pos); > + } else if (os_strcmp(buf, "afc_orientation") == 0) { > + conf->afc.location.orientation = atoi(pos); > + } else if (os_strcmp(buf, "afc_height") == 0) { > + char *end; > + > + conf->afc.location.height = strtod(pos, &end); > + if (*end) > + return 1; > + } else if (os_strcmp(buf, "afc_height_type") == 0) { > + conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1); > + if (!conf->afc.location.height_type) > + return 1; > + > + os_strlcpy(conf->afc.location.height_type, pos, > + os_strlen(pos) + 1); > + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) { > + conf->afc.location.vertical_tolerance = atoi(pos); > + } else if (os_strcmp(buf, "afc_min_power") == 0) { > + conf->afc.min_power = atoi(pos); > + } else if (os_strcmp(buf, "afc_freq_range") == 0) { > + if (hostapd_afc_parse_freq_range(conf, pos)) > + return 1; > + } else if (os_strcmp(buf, "afc_op_class") == 0) { > + if (hostapd_afc_parse_op_class(conf, pos)) > + return 1; > +#endif /* CONFIG_AFC */ > } else if (os_strcmp(buf, "mbssid") == 0) { > int mbssid = atoi(pos); > if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { > diff --git a/hostapd/defconfig b/hostapd/defconfig > index 550db697b..66bf894eb 100644 > --- a/hostapd/defconfig > +++ b/hostapd/defconfig > @@ -425,3 +425,6 @@ CONFIG_DPP2=y > > # Wi-Fi Aware unsynchronized service discovery (NAN USD) > #CONFIG_NAN_USD=y > + > +# Enable Automated Frequency Coordination for 6GHz outdoor > +#CONFIG_AFC=y > diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf > index d80abcac0..0d10998af 100644 > --- a/hostapd/hostapd.conf > +++ b/hostapd/hostapd.conf > @@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0 > # Valid range: 0..20 TUs; default is 0 (disabled) > #unsol_bcast_probe_resp_interval=0 > > +##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands ####### > + > +# AFC daemon connection socket > +#afcd_sock=/var/run/afcd.sock > + > +# AFC request identification parameters > +#afc_request_version=1.1 > +#afc_request_id=11235813 > +#afc_serial_number=abcdefg > +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000 > +# > +# AFC location type: > +# 0 = ellipse > +# 1 = linear polygon > +# 2 = radial polygon > +#afc_location_type=0 > +# > +# AFC ellipse or linear polygon coordinations > +#afc_linear_polygon=-122.984157:37.425056 > +# > +# AFC radial polygon coordinations > +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33 > +# > +# AFC ellipse major/minor axis and orientation > +#afc_major_axis=100 > +#afc_minor_axis=50 > +#afc_orientation=70 > +# > +# AFC device elevation parameters > +#afc_height=3.0 > +#afc_height_type=AGL > +#afc_vertical_tolerance=7 > +# > +# AFC minimum desired TX power (dbm) > +#afc_min_power=24 > +# > +# AFC request frequency ranges > +#afc_freq_range=5925:6425,6525:6875 > +# > +# AFC request operation classes > +#afc_op_class=131,132,133,134,136 > + > ##### IEEE 802.11be related configuration ##################################### > > #ieee80211be: Whether IEEE 802.11be (EHT) is enabled > diff --git a/src/ap/afc.c b/src/ap/afc.c > new file mode 100644 > index 000000000..40af3cb4a > --- /dev/null > +++ b/src/ap/afc.c > @@ -0,0 +1,978 @@ > +/* > + * Automated Frequency Coordination > + * Copyright (c) 2024, Lorenzo Bianconi > + * > + * This software may be distributed under the terms of the BSD license. > + * See README for more details. > + */ > + > +#include > +#include > +#include > + > +#include "utils/includes.h" > +#include "utils/common.h" > +#include "utils/eloop.h" > +#include "hostapd.h" > +#include "acs.h" > +#include "hw_features.h" > + > +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 > +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ > +#define HOSTAPD_AFC_BUFSIZE 4096 With the afc-reply.json, the response is 4842 bytes, so, this needs to be increased. > + > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); > + > + > +static struct json_object * > +hostapd_afc_build_location_request(struct hostapd_iface *iface) > +{ > + struct json_object *location_obj, *center_obj, *ellipse_obj; > + struct json_object *elevation_obj, *str_obj; > + struct hostapd_config *iconf = iface->conf; > + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); > + > + location_obj = json_object_new_object(); > + if (!location_obj) > + return NULL; > + > + if (iconf->afc.location.type != LINEAR_POLYGON) { > + struct afc_linear_polygon *lp = > + &iconf->afc.location.linear_polygon_data[0]; > + > + ellipse_obj = json_object_new_object(); > + if (!ellipse_obj) > + goto error; > + > + center_obj = json_object_new_object(); > + if (!center_obj) > + goto error; > + > + json_object_object_add(ellipse_obj, "center", center_obj); > + > + str_obj = json_object_new_double(lp->longitude); If the config file doesn't have the entries, then these pointers NULL and hostapd crashes. > + if (!str_obj) > + goto error; > + > + json_object_object_add(center_obj, "longitude", str_obj); > + str_obj = json_object_new_double(lp->latitude); > + if (!str_obj) > + goto error; > + > + json_object_object_add(center_obj, "latitude", str_obj); > + } > + > + switch (iconf->afc.location.type) { > + case LINEAR_POLYGON: { > + struct json_object *outer_boundary_obj; > + int i; > + > + outer_boundary_obj = json_object_new_object(); > + if (!outer_boundary_obj) > + goto error; > + > + json_object_object_add(location_obj, "linearPolygon", > + outer_boundary_obj); > + ellipse_obj = json_object_new_array(); > + if (!ellipse_obj) > + goto error; > + > + json_object_object_add(outer_boundary_obj, "outerBoundary", > + ellipse_obj); > + for (i = 0; > + i < iconf->afc.location.n_linear_polygon_data; i++) { > + struct afc_linear_polygon *lp = > + &iconf->afc.location.linear_polygon_data[i]; > + center_obj = json_object_new_object(); > + if (!center_obj) > + goto error; > + > + json_object_array_add(ellipse_obj, center_obj); > + str_obj = json_object_new_double(lp->longitude); > + if (!str_obj) > + goto error; > + > + json_object_object_add(center_obj, "longitude", > + str_obj); > + str_obj = json_object_new_double(lp->latitude); > + if (!str_obj) > + goto error; > + > + json_object_object_add(center_obj, "latitude", > + str_obj); > + } > + break; > + } > + case RADIAL_POLYGON: { > + struct json_object *outer_boundary_obj; > + int i; > + > + json_object_object_add(location_obj, "radialPolygon", > + ellipse_obj); > + > + outer_boundary_obj = json_object_new_array(); > + if (!outer_boundary_obj) > + goto error; > + > + json_object_object_add(ellipse_obj, "outerBoundary", > + outer_boundary_obj); > + for (i = 0; > + i < iconf->afc.location.n_radial_polygon_data; i++) { > + struct afc_radial_polygon *rp = > + &iconf->afc.location.radial_polygon_data[i]; > + struct json_object *angle_obj; > + > + angle_obj = json_object_new_object(); > + if (!angle_obj) > + goto error; > + > + json_object_array_add(outer_boundary_obj, angle_obj); > + > + str_obj = json_object_new_double(rp->angle); > + if (!str_obj) > + goto error; > + > + json_object_object_add(angle_obj, "angle", str_obj); > + str_obj = json_object_new_double(rp->length); > + if (!str_obj) > + goto error; > + > + json_object_object_add(angle_obj, "length", str_obj); > + } > + break; > + } > + case ELLIPSE: > + default: > + json_object_object_add(location_obj, "ellipse", ellipse_obj); > + > + str_obj = json_object_new_int(iconf->afc.location.major_axis); > + if (!str_obj) > + goto error; > + > + json_object_object_add(ellipse_obj, "majorAxis", str_obj); > + str_obj = json_object_new_int(iconf->afc.location.minor_axis); > + if (!str_obj) > + goto error; > + > + json_object_object_add(ellipse_obj, "minorAxis", str_obj); > + str_obj = json_object_new_int(iconf->afc.location.orientation); > + if (!str_obj) > + goto error; > + > + json_object_object_add(ellipse_obj, "orientation", str_obj); > + break; > + } > + > + elevation_obj = json_object_new_object(); > + if (!elevation_obj) > + goto error; > + > + json_object_object_add(location_obj, "elevation", > + elevation_obj); > + str_obj = json_object_new_double(iconf->afc.location.height); > + if (!str_obj) > + goto error; > + > + json_object_object_add(elevation_obj, "height", str_obj); > + str_obj = json_object_new_string(iconf->afc.location.height_type); > + if (!str_obj) > + goto error; > + > + json_object_object_add(elevation_obj, "heightType", str_obj); > + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance); > + if (!str_obj) > + goto error; > + > + json_object_object_add(elevation_obj, "verticalUncertainty", > + str_obj); > + str_obj = json_object_new_int(is_ap_indoor); > + if (!str_obj) > + goto error; > + > + json_object_object_add(location_obj, "indoorDeployment", str_obj); > + > + return location_obj; > + > +error: > + json_object_put(location_obj); > + return NULL; > +} > + > + > +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class) > +{ > + struct json_object *chan_list_obj, *str_obj; > + const struct oper_class_map *oper_class; > + int chan_offset, chan; > + > + oper_class = get_oper_class(NULL, op_class); > + if (!oper_class) > + return NULL; > + > + chan_list_obj = json_object_new_array(); > + if (!chan_list_obj) > + return NULL; > + > + switch (op_class) { > + case 132: /* 40MHz */ > + chan_offset = 2; > + break; > + case 133: /* 80MHz */ > + chan_offset = 6; > + break; > + case 134: /* 160MHz */ > + chan_offset = 14; > + break; > + default: > + chan_offset = 0; > + break; > + } > + > + for (chan = oper_class->min_chan; chan <= oper_class->max_chan; > + chan += oper_class->inc) { > + str_obj = json_object_new_int(chan + chan_offset); > + if (!str_obj) { > + json_object_put(chan_list_obj); > + return NULL; > + } > + json_object_array_add(chan_list_obj, str_obj); > + } > + > + return chan_list_obj; > +} > + > + > +static struct json_object * > +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface) > +{ > + struct json_object *op_class_list_obj, *str_obj; > + struct hostapd_config *iconf = iface->conf; > + int i; > + > + op_class_list_obj = json_object_new_array(); > + if (!op_class_list_obj) > + return NULL; > + > + for (i = 0; i < iconf->afc.n_op_class; i++) { > + struct json_object *op_class_obj, *chan_list_obj; > + u8 op_class = iconf->afc.op_class[i]; > + > + if (!is_6ghz_op_class(op_class)) > + continue; > + > + op_class_obj = json_object_new_object(); > + if (!op_class_obj) > + goto error; > + > + json_object_array_add(op_class_list_obj, op_class_obj); > + str_obj = json_object_new_int(op_class); > + if (!str_obj) > + goto error; > + > + json_object_object_add(op_class_obj, "globalOperatingClass", > + str_obj); > + > + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class); > + if (!chan_list_obj) > + goto error; > + > + json_object_object_add(op_class_obj, "channelCfi", > + chan_list_obj); > + } > + > + return op_class_list_obj; > + > +error: > + json_object_put(op_class_list_obj); > + return NULL; > +} > + > + > +static struct json_object * > +hostapd_afc_build_request(struct hostapd_iface *iface) > +{ > + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj; > + struct json_object *s2_obj, *str_obj, *location_obj; > + struct hostapd_config *iconf = iface->conf; > + struct json_object *op_class_list_obj; > + int i; > + > + l1_obj = json_object_new_object(); > + if (!l1_obj) > + return NULL; > + > + if (iconf->afc.request.version) { > + str_obj = json_object_new_string(iconf->afc.request.version); > + if (!str_obj) > + goto error; > + > + json_object_object_add(l1_obj, "version", str_obj); > + } > + > + la1_obj = json_object_new_array(); > + if (!la1_obj) > + goto error; > + > + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests", > + la1_obj); > + l2_obj = json_object_new_object(); > + if (!l2_obj) > + goto error; > + > + json_object_array_add(la1_obj, l2_obj); > + if (iconf->afc.request.id) { > + str_obj = json_object_new_string(iconf->afc.request.id); > + if (!str_obj) > + goto error; > + > + json_object_object_add(l2_obj, "requestId", str_obj); > + } > + > + s2_obj = json_object_new_object(); > + if (!s2_obj) > + goto error; > + > + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj); > + if (iconf->afc.request.sn) { > + str_obj = json_object_new_string(iconf->afc.request.sn); > + if (!str_obj) > + goto error; > + > + json_object_object_add(s2_obj, "serialNumber", str_obj); > + } > + > + la2_obj = json_object_new_array(); > + if (!la2_obj) > + goto error; > + > + json_object_object_add(s2_obj, "certificationId", la2_obj); > + for (i = 0; i < iconf->afc.n_cert_ids; i++) { > + struct json_object *obj; > + > + obj = json_object_new_object(); > + if (!obj) > + goto error; > + > + json_object_array_add(la2_obj, obj); > + str_obj = > + json_object_new_string(iconf->afc.cert_ids[i].rulset); > + if (!str_obj) > + goto error; > + > + json_object_object_add(obj, "rulesetId", str_obj); > + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id); > + if (!str_obj) > + goto error; > + > + json_object_object_add(obj, "id", str_obj); > + } > + > + location_obj = hostapd_afc_build_location_request(iface); > + if (!location_obj) > + goto error; > + > + json_object_object_add(l2_obj, "location", location_obj); > + str_obj = json_object_new_int(iconf->afc.min_power); > + if (!str_obj) > + goto error; > + > + json_object_object_add(l2_obj, "minDesiredPower", str_obj); > + > + if (iconf->afc.n_freq_range) { > + struct json_object *freq_obj; > + > + freq_obj = json_object_new_array(); > + if (!freq_obj) > + goto error; > + > + json_object_object_add(l2_obj, "inquiredFrequencyRange", > + freq_obj); > + for (i = 0; i < iconf->afc.n_freq_range; i++) { > + struct afc_freq_range *fr = &iconf->afc.freq_range[i]; > + struct json_object *obj; > + > + obj = json_object_new_object(); > + if (!obj) > + goto error; > + > + json_object_array_add(freq_obj, obj); > + str_obj = json_object_new_int(fr->low_freq); > + if (!str_obj) > + goto error; > + > + json_object_object_add(obj, "lowFrequency", str_obj); > + str_obj = json_object_new_int(fr->high_freq); > + if (!str_obj) > + goto error; > + > + json_object_object_add(obj, "highFrequency", str_obj); > + } > + } > + > + op_class_list_obj = hostapd_afc_build_req_chan_list(iface); > + if (!op_class_list_obj) > + goto error; > + > + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj); > + > + wpa_printf(MSG_DEBUG, "Pending AFC request: %s", > + json_object_get_string(l1_obj)); > + > + return l1_obj; > + > +error: > + json_object_put(l1_obj); > + > + return NULL; > +} > + > + > +static int > +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, > + struct json_object *reply_elem_obj) > +{ > + struct afc_freq_range_elem *f = NULL; > + struct json_object *obj; > + int i, count = 0; > + > + if (!json_object_object_get_ex(reply_elem_obj, > + "availableFrequencyInfo", &obj)) > + return 0; > + > + for (i = 0; i < json_object_array_length(obj); i++) { > + struct json_object *range_elem_obj, *freq_range_obj; > + struct json_object *high_freq_obj, *low_freq_obj; > + struct json_object *max_psd_obj; > + > + range_elem_obj = json_object_array_get_idx(obj, i); > + if (!range_elem_obj) > + continue; > + > + if (!json_object_object_get_ex(range_elem_obj, > + "frequencyRange", > + &freq_range_obj)) > + continue; > + > + if (!json_object_object_get_ex(freq_range_obj, > + "lowFrequency", > + &low_freq_obj)) > + continue; > + > + if (!json_object_object_get_ex(freq_range_obj, > + "highFrequency", > + &high_freq_obj)) > + continue; > + > + if (!json_object_object_get_ex(range_elem_obj, "maxPsd", > + &max_psd_obj) && > + !json_object_object_get_ex(range_elem_obj, "maxPSD", > + &max_psd_obj)) > + continue; > + > + f = os_realloc_array(f, count + 1, sizeof(*f)); > + if (!f) > + return -ENOMEM; > + > + f[count].low_freq = json_object_get_int(low_freq_obj); > + f[count].high_freq = json_object_get_int(high_freq_obj); > + f[count++].max_psd = json_object_get_int(max_psd_obj); > + } > + iface->afc.freq_range = f; > + iface->afc.num_freq_range = count; > + > + return 0; > +} > + > + > +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list, > + int *chan_list_size, u8 op_class, > + int center_chan, int power) > +{ > + int num_low_subchan, ch, count = *chan_list_size; > + struct afc_chan_info_elem *c = *chan_list; > + > + switch (op_class) { > + case 132: /* 40MHz */ > + num_low_subchan = 2; > + break; > + case 133: /* 80MHz */ > + num_low_subchan = 6; > + break; > + case 134: /* 160MHz */ > + num_low_subchan = 14; > + break; > + default: > + num_low_subchan = 0; > + break; > + } > + > + for (ch = center_chan - num_low_subchan; > + ch <= center_chan + num_low_subchan; ch += 4) { > + int i; > + > + for (i = 0; i < count; i++) { > + if (c[i].chan == ch) > + break; > + } > + > + if (i == count) { > + c = os_realloc_array(c, count + 1, sizeof(*c)); > + if (!c) > + return -ENOMEM; > + > + c[count].chan = ch; > + c[count++].power = power; > + } > + } > + > + *chan_list_size = count; > + *chan_list = c; > + > + return 0; > +} > + > + > +static int > +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, > + struct json_object *reply_elem_obj) > +{ > + struct afc_chan_info_elem *c = NULL; > + struct json_object *obj; > + int i, count = 0; > + > + if (!json_object_object_get_ex(reply_elem_obj, > + "availableChannelInfo", &obj)) > + return 0; > + > + for (i = 0; i < json_object_array_length(obj); i++) { > + struct json_object *range_elem_obj, *op_class_obj; > + struct json_object *chan_cfi_obj, *max_eirp_obj; > + int ch, op_class; > + > + range_elem_obj = json_object_array_get_idx(obj, i); > + if (!range_elem_obj) > + continue; > + > + if (!json_object_object_get_ex(range_elem_obj, > + "globalOperatingClass", > + &op_class_obj)) > + continue; > + > + if (!json_object_object_get_ex(range_elem_obj, "maxEirp", > + &max_eirp_obj)) > + continue; > + > + if (!json_object_object_get_ex(range_elem_obj, "channelCfi", > + &chan_cfi_obj)) > + continue; > + > + op_class = json_object_get_int(op_class_obj); > + for (ch = 0; > + ch < json_object_array_length(chan_cfi_obj); ch++) { > + struct json_object *pwr_obj; > + struct json_object *ch_obj; > + int channel, power; > + > + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch); > + if (!ch_obj) > + continue; > + > + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch); > + if (!pwr_obj) > + continue; > + > + channel = json_object_get_int(ch_obj); > + power = json_object_get_int(pwr_obj); > + > + hostad_afc_update_chan_info(&c, &count, op_class, > + channel, power); > + } > + iface->afc.chan_info_list = c; > + iface->afc.num_chan_info = count; > + } > + > + return 0; > +} > + > + > +static int hostad_afc_get_timeout(struct json_object *obj) > +{ > + time_t t, now; > + struct tm tm; > + > + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ", > + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, > + &tm.tm_min, &tm.tm_sec) <= 0) > + return HOSTAPD_AFC_TIMEOUT; > + > + tm.tm_year -= 1900; > + tm.tm_mon -= 1; > + tm.tm_isdst = -1; > + t = mktime(&tm); > + time(&now); > + > + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100; > +} > + > + > +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) > +{ > + struct json_object *payload_obj, *reply_obj, *version_obj; > + struct hostapd_config *iconf = iface->conf; > + int i, request_timeout = -1, ret = -EINVAL; > + > + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply); > + payload_obj = json_tokener_parse(reply); > + if (!payload_obj) > + return -EINVAL; This function could use some error logs, esp. as its related to parsing. > + > + if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) > + return -EINVAL; > + > + if (iconf->afc.request.version && > + os_strcmp(iconf->afc.request.version, > + json_object_get_string(version_obj))) > + return -EINVAL; > + > + if (!json_object_object_get_ex(payload_obj, > + "availableSpectrumInquiryResponses", > + &reply_obj)) > + return -EINVAL; > + > + for (i = 0; i < json_object_array_length(reply_obj); i++) { > + struct json_object *reply_elem_obj, *obj, *status_obj; > + int j, status = -EINVAL; > + > + reply_elem_obj = json_object_array_get_idx(reply_obj, i); > + if (!reply_elem_obj) > + continue; > + > + if (!json_object_object_get_ex(reply_elem_obj, "requestId", > + &obj)) > + continue; > + > + if (iconf->afc.request.id && > + os_strcmp(iconf->afc.request.id, > + json_object_get_string(obj))) > + continue; > + > + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId", > + &obj)) > + continue; > + > + for (j = 0; j < iconf->afc.n_cert_ids; j++) { > + if (!os_strcmp(iconf->afc.cert_ids[j].rulset, > + json_object_get_string(obj))) > + break; > + } > + > + if (j == iconf->afc.n_cert_ids) > + continue; > + > + if (!json_object_object_get_ex(reply_elem_obj, "response", > + &obj)) > + continue; > + > + if (json_object_object_get_ex(obj, "shortDescription", > + &status_obj)) > + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s", > + i, json_object_get_string(status_obj)); > + > + if (json_object_object_get_ex(obj, "responseCode", > + &status_obj)) > + status = json_object_get_int(status_obj); > + > + if (status < 0) > + continue; > + > + if (hostad_afc_parse_available_freq_info(iface, > + reply_elem_obj) || > + hostad_afc_parse_available_chan_info(iface, > + reply_elem_obj)) > + continue; > + > + if (json_object_object_get_ex(reply_elem_obj, > + "availabilityExpireTime", > + &obj)) { > + int timeout = hostad_afc_get_timeout(obj); > + > + if (request_timeout < 0 || timeout < request_timeout) > + request_timeout = timeout; > + } > + > + ret = status; > + } > + > + iface->afc.data_valid = true; > + iface->afc.timeout = request_timeout; > + if (iface->afc.timeout < 0) > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; > + > + return ret; > +} > + > + > +static int hostapd_afc_send_receive(struct hostapd_iface *iface) > +{ > + struct hostapd_config *iconf = iface->conf; > + json_object *request_obj = NULL; > + struct timeval sock_timeout = { > + .tv_sec = 5, > + }; > + struct sockaddr_un addr = { > + .sun_family = AF_UNIX, > +#ifdef __FreeBSD__ > + .sun_len = sizeof(addr), > +#endif /* __FreeBSD__ */ > + }; > + char buf[HOSTAPD_AFC_BUFSIZE] = {}; > + const char *request; > + int sockfd, ret; > + fd_set read_set; > + > + if (iface->afc.data_valid) { > + /* AFC data already downloaded from the server */ > + return 0; > + } > + > + if (!iconf->afc.socket) { > + wpa_printf(MSG_ERROR, "Missing AFC socket string"); > + return -EINVAL; > + } > + > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; > + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { > + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", > + iconf->afc.socket); > + return -EINVAL; > + } > + > + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); > + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); > + if (sockfd < 0) { > + wpa_printf(MSG_ERROR, "Failed creating AFC socket"); > + return sockfd; > + } > + > + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { > + wpa_printf(MSG_ERROR, "Failed connecting AFC socket"); > + ret = -EIO; > + goto close_sock; > + } > + > + request_obj = hostapd_afc_build_request(iface); > + if (!request_obj) { > + ret = -ENOMEM; > + goto close_sock; > + } > + > + request = json_object_to_json_string(request_obj); > + if (send(sockfd, request, strlen(request), 0) < 0) { > + wpa_printf(MSG_ERROR, "Failed sending AFC request"); > + ret = -EIO; > + goto close_sock; > + } > + > + FD_ZERO(&read_set); > + FD_SET(sockfd, &read_set); > + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { > + wpa_printf(MSG_ERROR, "Select failed on AFC socket"); > + ret = -errno; > + goto close_sock; > + } > + > + if (!FD_ISSET(sockfd, &read_set)) { > + ret = -EIO; > + goto close_sock; > + } > + > + ret = recv(sockfd, buf, sizeof(buf) - 1, 0); > + if (ret <= 0) > + goto close_sock; > + > + ret = hostapd_afc_parse_reply(iface, buf); > +close_sock: > + json_object_put(request_obj); > + close(sockfd); > + > + return ret; > +} > + > + > +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) > +{ > + const struct oper_class_map *oper_class; > + int ch; > + > + oper_class = get_oper_class(NULL, iface->conf->op_class); > + if (!oper_class) > + return false; > + > + for (ch = oper_class->min_chan; ch <= oper_class->max_chan; > + ch += oper_class->inc) { > + struct hostapd_hw_modes *mode = iface->current_mode; > + int i; > + > + for (i = 0; i < mode->num_channels; i++) { > + struct hostapd_channel_data *chan = &mode->channels[i]; > + > + if (chan->chan == ch && > + !(chan->flag & HOSTAPD_CHAN_DISABLED)) > + return true; > + } > + } > + > + return false; > +} > + > + > +int hostapd_afc_handle_request(struct hostapd_iface *iface) > +{ > + struct hostapd_config *iconf = iface->conf; > + int ret; > + > + /* AFC is required just for standard power AP */ > + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) > + return 1; > + > + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) > + return 1; > + > + if (iface->state == HAPD_IFACE_ACS) > + return 1; > + > + ret = hostapd_afc_send_receive(iface); > + if (ret < 0) { > + /* > + * If the connection to the AFCD failed, resched for a > + * future attempt. > + */ > + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret); > + if (ret == -EIO) > + ret = 0; > + goto resched; > + } > + > + hostap_afc_disable_channels(iface); > + if (!hostapd_afc_has_usable_chans(iface)) > + goto resched; > + > + /* Trigger an ACS freq scan */ > + iconf->channel = 0; > + iface->freq = 0; > + > + if (acs_init(iface) != HOSTAPD_CHAN_ACS) { > + wpa_printf(MSG_ERROR, "Could not start ACS"); > + ret = -EINVAL; > + } > + > +resched: > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); > + eloop_register_timeout(iface->afc.timeout, 0, > + hostapd_afc_timeout_handler, iface, NULL); > + > + return ret; > +} > + > + > +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) > +{ > + os_free(iface->afc.chan_info_list); > + os_free(iface->afc.freq_range); > + > + iface->afc.num_freq_range = 0; > + iface->afc.num_chan_info = 0; > + > + iface->afc.chan_info_list = NULL; > + iface->afc.freq_range = NULL; > + > + iface->afc.data_valid = false; > +} > + > + > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) > +{ > + struct hostapd_iface *iface = eloop_ctx; > + bool restart_iface = true; > + > + hostapd_afc_delete_data_from_server(iface); > + if (iface->state != HAPD_IFACE_ENABLED) { > + /* Hostapd is not fully enabled yet, toggle the interface */ > + goto restart_interface; > + } > + > + if (hostapd_afc_send_receive(iface) < 0 || > + hostapd_get_hw_features(iface)) { > + restart_iface = false; > + goto restart_interface; > + } > + > + if (hostapd_is_usable_chans(iface)) > + goto resched; > + > + restart_iface = hostapd_afc_has_usable_chans(iface); > + if (restart_iface) { > + /* Trigger an ACS freq scan */ > + iface->conf->channel = 0; > + iface->freq = 0; > + } > + > +restart_interface: > + hostapd_disable_iface(iface); > + if (restart_iface) > + hostapd_enable_iface(iface); > +resched: > + eloop_register_timeout(iface->afc.timeout, 0, > + hostapd_afc_timeout_handler, iface, NULL); > +} > + > + > +void hostapd_afc_stop(struct hostapd_iface *iface) > +{ > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); > +} > + > + > +void hostap_afc_disable_channels(struct hostapd_iface *iface) > +{ > + struct hostapd_hw_modes *mode; > + int i; > + > + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A) > + return; > + > + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) > + return; > + > + if (!iface->afc.data_valid) > + return; > + > + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A]; > + for (i = 0; i < mode->num_channels; i++) { > + struct hostapd_channel_data *chan = &mode->channels[i]; > + int j; > + > + if (!is_6ghz_freq(chan->freq)) > + continue; > + > + for (j = 0; j < iface->afc.num_freq_range; j++) { > + if (chan->freq >= iface->afc.freq_range[j].low_freq && > + chan->freq <= iface->afc.freq_range[j].high_freq) > + break; > + } > + > + if (j != iface->afc.num_freq_range) > + continue; > + > + for (j = 0; j < iface->afc.num_chan_info; j++) { > + if (chan->chan == iface->afc.chan_info_list[j].chan) > + break; > + } > + > + if (j != iface->afc.num_chan_info) > + continue; > + > + chan->flag |= HOSTAPD_CHAN_DISABLED; Please add a debug print with channel info. > + } > +} > diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c > index 1a18df617..ca67aeb41 100644 > --- a/src/ap/ap_config.c > +++ b/src/ap/ap_config.c > @@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf) > #endif /* CONFIG_ACS */ > wpabuf_free(conf->lci); > wpabuf_free(conf->civic); > +#ifdef CONFIG_AFC > + os_free(conf->afc.socket); > + os_free(conf->afc.request.version); > + os_free(conf->afc.request.id); > + os_free(conf->afc.request.sn); > + for (i = 0; i < conf->afc.n_cert_ids; i++) { > + os_free(conf->afc.cert_ids[i].rulset); > + os_free(conf->afc.cert_ids[i].id); > + } > + os_free(conf->afc.cert_ids); > + os_free(conf->afc.location.height_type); > + os_free(conf->afc.location.linear_polygon_data); > + os_free(conf->afc.location.radial_polygon_data); > + os_free(conf->afc.freq_range); > + os_free(conf->afc.op_class); > +#endif /* CONFIG_AFC */ > > os_free(conf); > } > diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h > index 754d55331..2330163c4 100644 > --- a/src/ap/ap_config.h > +++ b/src/ap/ap_config.h > @@ -1225,6 +1225,53 @@ struct hostapd_config { > MBSSID_ENABLED = 1, > ENHANCED_MBSSID_ENABLED = 2, > } mbssid; > + > +#ifdef CONFIG_AFC > + struct { > + char *socket; > + struct { > + char *version; > + char *id; > + char *sn; > + } request; > + unsigned int n_cert_ids; > + struct cert_id { > + char *rulset; > + char *id; > + } *cert_ids; > + struct { > + enum afc_location_type { > + ELLIPSE, > + LINEAR_POLYGON, > + RADIAL_POLYGON, > + } type; > + unsigned int n_linear_polygon_data; > + struct afc_linear_polygon { > + double longitude; > + double latitude; > + } *linear_polygon_data; > + unsigned int n_radial_polygon_data; > + struct afc_radial_polygon { > + double length; > + double angle; > + } *radial_polygon_data; > + int major_axis; > + int minor_axis; > + int orientation; > + double height; > + char *height_type; > + int vertical_tolerance; > + } location; > + unsigned int n_freq_range; > + struct afc_freq_range { > + int low_freq; > + int high_freq; > + } *freq_range; > + unsigned int n_op_class; > + unsigned int *op_class; > + int min_power; > + } afc; > +#endif /* CONFIG_AFC */ > }; > > > diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c > index f8cb6432d..940a2a0af 100644 > --- a/src/ap/hostapd.c > +++ b/src/ap/hostapd.c > @@ -714,6 +714,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) > static void hostapd_cleanup_iface(struct hostapd_iface *iface) > { > wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); > + hostapd_afc_stop(iface); > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); > eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, > NULL); > @@ -2454,6 +2455,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, > } > #endif /* CONFIG_MESH */ > > +#ifdef CONFIG_IEEE80211AX > + /* check AFC for 6GHz channels. */ > + res = hostapd_afc_handle_request(iface); > + if (res <= 0) { > + if (res < 0) > + goto fail; > + return res; > + } > +#endif /* CONFIG_IEEE80211AX */ > + > if (!delay_apply_cfg && > hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, > hapd->iconf->channel, > @@ -2852,6 +2863,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) > > hostapd_set_state(iface, HAPD_IFACE_DISABLED); > > + hostapd_afc_stop(iface); > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); > iface->wait_channel_update = 0; > iface->is_no_ir = false; > @@ -2925,6 +2937,10 @@ void hostapd_interface_free(struct hostapd_iface *iface) > __func__, iface->bss[j]); > os_free(iface->bss[j]); > } > +#ifdef CONFIG_AFC > + os_free(iface->afc.chan_info_list); > + os_free(iface->afc.freq_range); > +#endif > hostapd_cleanup_iface(iface); > } > > diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h > index affe4f604..b74dc75f6 100644 > --- a/src/ap/hostapd.h > +++ b/src/ap/hostapd.h > @@ -700,9 +700,54 @@ struct hostapd_iface { > > /* Configured freq of interface is NO_IR */ > bool is_no_ir; > + > +#ifdef CONFIG_AFC > + struct { > + int timeout; > + unsigned int num_freq_range; > + struct afc_freq_range_elem { > + int low_freq; > + int high_freq; > + /** > + * max eirp power spectral density received from > + * the AFC coordinator for this band > + */ > + int max_psd; > + } *freq_range; > + unsigned int num_chan_info; > + struct afc_chan_info_elem { > + int chan; > + /** > + * max eirp power received from the AFC coordinator > + * for this channel > + */ > + int power; > + } *chan_info_list; > + bool data_valid; > + } afc; > +#endif /* CONFIG_AFC */ > }; > > /* hostapd.c */ > +#ifdef CONFIG_AFC > +int hostapd_afc_handle_request(struct hostapd_iface *iface); > +void hostapd_afc_stop(struct hostapd_iface *iface); > +void hostap_afc_disable_channels(struct hostapd_iface *iface); > +#else > +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) > +{ > + return 1; > +} > + > +static inline void hostapd_afc_stop(struct hostapd_iface *iface) > +{ > +} > + > +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) > +{ > +} > +#endif /* CONFIG_AFC */ > + > int hostapd_for_each_interface(struct hapd_interfaces *interfaces, > int (*cb)(struct hostapd_iface *iface, > void *ctx), void *ctx); > diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c > index e652d7504..222f3dc05 100644 > --- a/src/ap/hw_features.c > +++ b/src/ap/hw_features.c > @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) > iface->hw_features = modes; > iface->num_hw_features = num_modes; > > + hostap_afc_disable_channels(iface); > + > for (i = 0; i < num_modes; i++) { > struct hostapd_hw_modes *feature = &modes[i]; > int dfs_enabled = hapd->iconf->ieee80211h && > -- From michael-cy.lee at mediatek.com Mon Apr 8 23:50:23 2024 From: michael-cy.lee at mediatek.com (Michael-CY Lee) Date: Tue, 9 Apr 2024 14:50:23 +0800 Subject: [PATCH] wpa_supplicant: MLD STA: find partner links by BSSID and SSID Message-ID: <20240409065023.30664-1-michael-cy.lee@mediatek.com> MLD STA finds MLD AP's partner links by BSSID from the scan results. However, if the scan results contain BSSs with same BSSID but different BSS information, the MLD STA might assign a wrong BSS to one of the MLD AP's partner links. This patch avoids the problem by using both BSSID and SSID to find the MLD AP's partner links. Signed-off-by: Michael-CY Lee --- wpa_supplicant/bss.c | 9 +++++++-- wpa_supplicant/sme.c | 14 ++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 289035310..4b8a98adf 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -1529,8 +1529,13 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "MLD: Reported link not part of MLD"); } else if (!(BIT(link_id) & *seen)) { - struct wpa_bss *neigh_bss = - wpa_bss_get_bssid(wpa_s, pos + 1); + struct wpa_bss *neigh_bss; + + if (ssid) + neigh_bss = wpa_bss_get(wpa_s, pos + 1, ssid->ssid, + ssid->ssid_len); + else + neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1); *seen |= BIT(link_id); wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u", diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index f08184f98..0643774a5 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s, #ifdef CONFIG_TESTING_OPTIONS static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) + struct wpa_bss *bss, + struct wpa_ssid *ssid) { unsigned int low, high, i; @@ -456,7 +457,7 @@ found: MAC2STR(wpa_s->links[i].bssid)); /* Get the BSS entry and do the switch */ - bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid); + bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, ssid->ssid_len); wpa_s->mlo_assoc_link_id = i; return bss; @@ -515,7 +516,7 @@ out: static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) + struct wpa_bss *bss, struct wpa_ssid *ssid) { u8 i; @@ -533,7 +534,8 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s, if (bss->mld_link_id == i) wpa_s->links[i].bss = bss; else - wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid); + wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, + ssid->ssid_len); } } @@ -576,10 +578,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, NULL, ssid, NULL) && bss->valid_links) { wpa_printf(MSG_DEBUG, "MLD: In authentication"); - wpas_sme_set_mlo_links(wpa_s, bss); + wpas_sme_set_mlo_links(wpa_s, bss, ssid); #ifdef CONFIG_TESTING_OPTIONS - bss = wpas_ml_connect_pref(wpa_s, bss); + bss = wpas_ml_connect_pref(wpa_s, bss, ssid); if (wpa_s->conf->mld_force_single_link) { wpa_printf(MSG_DEBUG, "MLD: Force single link"); -- 2.25.1 From nchernikov at maxlinear.com Tue Apr 9 00:44:03 2024 From: nchernikov at maxlinear.com (Nikita Chernikov) Date: Tue, 9 Apr 2024 07:44:03 +0000 Subject: [PATCH] Advertise 6G RNR IE from nr_db also in probe response Message-ID: From ccf6b23c40b4a05d10147c2bddc19e59ba751c73 Mon Sep 17 00:00:00 2001 From: Nikita Chernikov Date: Tue, 2 Apr 2024 14:45:39 +0300 Subject: [PATCH] Advertise 6G RNR IE from nr_db also in probe response To: hostap at lists.infradead.org Currently, RNR elements built from nr_db are only advertised in beacon. To comply with 11AX standard, RNR IE of 6G should also be advertised in probe response. Also, replace magic numbers with definitions in nr_db RNR element construction. Signed-off-by: Nikita Chernikov --- src/ap/ieee802_11.c | 39 +++++++++++++++++++++++++-------------- src/ap/neighbor_db.h | 4 ++++ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index fcb2d14cf..a992379b6 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -50,6 +50,7 @@ #include "ieee802_11.h" #include "dfs.h" #include "mbo_ap.h" +#include "neighbor_db.h" #include "rrm.h" #include "taxonomy.h" #include "fils_hlp.h" @@ -7301,14 +7302,19 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd, - size_t *current_len) + size_t *current_len, unsigned int frame_stype) { struct hostapd_neighbor_entry *nr; size_t total_len = 0, len = *current_len; dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { - if (!nr->nr || wpabuf_len(nr->nr) < 12) + if (!nr->nr || wpabuf_len(nr->nr) < HOSTAPD_NEIGHBOR_DB_NR_MAX_LEN) + continue; + + /* Skip building RNR element from nr_db for non 6G VAPs in probe response */ + if (!(is_6ghz_op_class(wpabuf_head_u8(nr->nr)[HOSTAPD_NEIGHBOR_DB_NR_OPCLASS])) && + (frame_stype == WLAN_FC_STYPE_PROBE_RESP)) continue; if (nr->short_ssid == hapd->conf->ssid.short_ssid) @@ -7484,11 +7490,11 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type) switch (type) { case WLAN_FC_STYPE_BEACON: - if (hapd->conf->rnr) - total_len += hostapd_eid_nr_db_len(hapd, ¤t_len); /* fallthrough */ - case WLAN_FC_STYPE_PROBE_RESP: + if (hapd->conf->rnr) + total_len += hostapd_eid_nr_db_len(hapd, ¤t_len, type); + if (mode == COLOCATED_LOWER_BAND || ap_mld) total_len += hostapd_eid_rnr_multi_iface_len(hapd, @@ -7517,7 +7523,7 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type) static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid, - size_t *current_len) + size_t *current_len, unsigned int frame_stype) { struct hostapd_neighbor_entry *nr; size_t len = *current_len; @@ -7525,7 +7531,12 @@ static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid, dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) { - if (!nr->nr || wpabuf_len(nr->nr) < 12) + if (!nr->nr || wpabuf_len(nr->nr) < HOSTAPD_NEIGHBOR_DB_NR_MAX_LEN) + continue; + + /* Skip building RNR element from nr_db for non 6G VAPs in probe response */ + if (!(is_6ghz_op_class(wpabuf_head_u8(nr->nr)[HOSTAPD_NEIGHBOR_DB_NR_OPCLASS])) && + (frame_stype == WLAN_FC_STYPE_PROBE_RESP)) continue; if (nr->short_ssid == hapd->conf->ssid.short_ssid) @@ -7544,9 +7555,9 @@ static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid, /* TBTT Information Length */ *eid++ = RNR_TBTT_INFO_LEN; /* Operating Class */ - *eid++ = wpabuf_head_u8(nr->nr)[10]; + *eid++ = wpabuf_head_u8(nr->nr)[HOSTAPD_NEIGHBOR_DB_NR_OPCLASS]; /* Channel Number */ - *eid++ = wpabuf_head_u8(nr->nr)[11]; + *eid++ = wpabuf_head_u8(nr->nr)[HOSTAPD_NEIGHBOR_DB_NR_CHANNEL]; len += RNR_TBTT_HEADER_LEN; /* TBTT Information Set */ /* TBTT Information field */ @@ -7758,11 +7769,11 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type) switch (type) { case WLAN_FC_STYPE_BEACON: - if (hapd->conf->rnr) - eid = hostapd_eid_nr_db(hapd, eid, ¤t_len); /* fallthrough */ - case WLAN_FC_STYPE_PROBE_RESP: + if (hapd->conf->rnr) + eid = hostapd_eid_nr_db(hapd, eid, ¤t_len, type); + if (mode == COLOCATED_LOWER_BAND || ap_mld) eid = hostapd_eid_rnr_multi_iface(hapd, eid, ¤t_len); @@ -8087,7 +8098,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, } add_rnr = hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && - frame_stype == WLAN_FC_STYPE_BEACON && + ((frame_stype == WLAN_FC_STYPE_BEACON) || (frame_stype == WLAN_FC_STYPE_PROBE_RESP)) && rnr_eid && rnr_count && rnr_offset && rnr_len; while (bss_index < hapd->iface->num_bss) { @@ -8128,7 +8139,7 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, cur_len = 0; if (hapd->conf->rnr) - rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len); + rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len, frame_stype); if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND) rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid, &cur_len); diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h index 53f714203..621b94187 100644 --- a/src/ap/neighbor_db.h +++ b/src/ap/neighbor_db.h @@ -10,6 +10,10 @@ #ifndef NEIGHBOR_DB_H #define NEIGHBOR_DB_H +#define HOSTAPD_NEIGHBOR_DB_NR_OPCLASS 10 +#define HOSTAPD_NEIGHBOR_DB_NR_CHANNEL 11 +#define HOSTAPD_NEIGHBOR_DB_NR_MAX_LEN 12 + struct hostapd_neighbor_entry * hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid); -- 2.17.1 From j at w1.fi Mon Apr 15 13:39:02 2024 From: j at w1.fi (Jouni Malinen) Date: Mon, 15 Apr 2024 23:39:02 +0300 Subject: [PATCH 1/7] driver_nl80211: update drv->ifindex on removing the first BSS In-Reply-To: <20240404105217.12478-1-nbd@nbd.name> References: <20240404105217.12478-1-nbd@nbd.name> Message-ID: On Thu, Apr 04, 2024 at 12:52:11PM +0200, Felix Fietkau wrote: > Otherwise it will point at the ifindex of the just removed BSS Thanks, all seven patches applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Mon Apr 15 13:39:18 2024 From: j at w1.fi (Jouni Malinen) Date: Mon, 15 Apr 2024 23:39:18 +0300 Subject: [PATCH] build: de-duplicate _DIRS before calling mkdir In-Reply-To: <20240404130327.80024-1-nbd@nbd.name> References: <20240404130327.80024-1-nbd@nbd.name> Message-ID: On Thu, Apr 04, 2024 at 03:03:27PM +0200, Felix Fietkau wrote: > If the build path is long, the contents of the _DIRS variable can be very long, > since it repeats the same directories very often. > In some cases, this has triggered an "Argument list too long" build error. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Tue Apr 16 01:05:52 2024 From: j at w1.fi (Jouni Malinen) Date: Tue, 16 Apr 2024 11:05:52 +0300 Subject: [PATCH 05/22] hostapd: MLO: handle auth/assoc on link address In-Reply-To: <20240328181652.2956122-6-quic_adisi@quicinc.com> References: <20240328181652.2956122-1-quic_adisi@quicinc.com> <20240328181652.2956122-6-quic_adisi@quicinc.com> Message-ID: On Thu, Mar 28, 2024 at 11:46:35PM +0530, Aditya Kumar Singh wrote: > Modify authentication and association frames to be always sent with link > address as A1 and A3 for ease of Tx status handling. It would be nice to get a bit more complete commit message that explains why this can be done. Surely there was some reason for the previous more complex design.. > diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > @@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, > - os_memcpy(reply->bssid, bssid, ETH_ALEN); > + os_memcpy(reply->bssid, sa, ETH_ALEN); This removes the only use of bssid in send_auth_reply(). Is that really correct for all cases? It might be, but again, this needs more justification in the commit message. In addition, this should remove the bssid argument from this function and potentially in a number of calling functions as well since all the would now be unused. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Tue Apr 16 01:24:38 2024 From: j at w1.fi (Jouni Malinen) Date: Tue, 16 Apr 2024 11:24:38 +0300 Subject: [PATCH 1/3] hostapd: add support for testing probe response elements In-Reply-To: <20240328140657.c09d8b9b6542.I8fda4243e4210c203472f3d40953575428fbdd04@changeid> References: <20240328140657.c09d8b9b6542.I8fda4243e4210c203472f3d40953575428fbdd04@changeid> Message-ID: On Thu, Mar 28, 2024 at 02:06:58PM +0100, Johannes Berg wrote: > Add support for additional (vendor) elements to be added > to only probe response frames, for testing. Thanks, all three patches applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Tue Apr 16 01:25:25 2024 From: j at w1.fi (Jouni Malinen) Date: Tue, 16 Apr 2024 11:25:25 +0300 Subject: [PATCH 00/22] hostapd: add cohosted MLO support In-Reply-To: <20240328181652.2956122-1-quic_adisi@quicinc.com> References: <20240328181652.2956122-1-quic_adisi@quicinc.com> Message-ID: On Thu, Mar 28, 2024 at 11:46:30PM +0530, Aditya Kumar Singh wrote: > Currently MLO is being supported with an assumption of only single BSS per > link in the hostapd conf file. This needs to be extended when cohosted ML > BSS exist in the same config file. > > Extend the support for cohosted BSSes along with certain improvements in > wpa state machine, rekeying areas. Thanks, patches 1-4 applied as an initial step. -- Jouni Malinen PGP id EFC895FA From quic_adisi at quicinc.com Tue Apr 16 02:31:14 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 16 Apr 2024 15:01:14 +0530 Subject: [PATCH 05/22] hostapd: MLO: handle auth/assoc on link address In-Reply-To: References: <20240328181652.2956122-1-quic_adisi@quicinc.com> <20240328181652.2956122-6-quic_adisi@quicinc.com> Message-ID: <1cf3385f-a9d7-4397-967b-ae399c61ebcd@quicinc.com> On 4/16/24 13:35, Jouni Malinen wrote: > On Thu, Mar 28, 2024 at 11:46:35PM +0530, Aditya Kumar Singh wrote: >> Modify authentication and association frames to be always sent with link >> address as A1 and A3 for ease of Tx status handling. > > It would be nice to get a bit more complete commit message that explains > why this can be done. Surely there was some reason for the previous more > complex design.. > Okay. So, if we see mlme_event_mgmt_tx_status(), link_id will be only filled if the frame was the last transmitted on the whole drv level. For single MLD this approach is fine. But when considering multiple MLDs, there could be cases where multiple packets are sent out by various interfaces (BSS) under same drv. Now while handling the Tx status, only one interface will get the proper link_id. Rest all will get -1, and event will be routed to first BSS always. Now there is a possibility of frame getting dropped due to this since it is not routed to correct link hapd. Hence to make it convenient, it is better to pass A1, A3 as link address. Since via A3, in get_hapd_bssid(), your hapd->own_addr would match and proper link hapd will be selected even when link_id is not present. >> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c > >> @@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, >> - os_memcpy(reply->bssid, bssid, ETH_ALEN); >> + os_memcpy(reply->bssid, sa, ETH_ALEN); > > This removes the only use of bssid in send_auth_reply(). Is that really > correct for all cases? It might be, but again, this needs more > justification in the commit message. As said above, in order to ensure link_address only returns back in A3 during Tx status, we have to ensure we are filling link address in the packet being sent. Hence the above change. This was the place where issue was there when handling co-hosted MLDs hence made changes only here as of now. With more thorough testing, if needed will change as other places as well. > In addition, this should remove the > bssid argument from this function and potentially in a number of calling > functions as well since all the would now be unused. Yeah sure. So if you agree, I will send out a clean up patch separately? From jsoloveckis at maxlinear.com Tue Apr 16 04:47:36 2024 From: jsoloveckis at maxlinear.com (Jurijs Soloveckis) Date: Tue, 16 Apr 2024 11:47:36 +0000 Subject: [PATCH] Set correct center segment indexes in channel switch fallback Message-ID: Set correct center segment indexes in channel switch fallback for non-5GHz band Signed-off-by: Jurijs Soloveckis --- src/ap/hostapd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index f8cb6432d..cee33ffcf 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4419,15 +4419,15 @@ void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params) { - int seg0_idx = 0, seg1_idx = 0; + u8 seg0_idx = 0, seg1_idx = 0; enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); if (freq_params->center_freq1) - seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5; + ieee80211_freq_to_chan(freq_params->center_freq1, &seg0_idx); if (freq_params->center_freq2) - seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5; + ieee80211_freq_to_chan(freq_params->center_freq2, &seg1_idx); switch (freq_params->bandwidth) { case 0: -- 2.17.1 From lorenzo at kernel.org Tue Apr 16 08:22:40 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 17:22:40 +0200 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: References: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> Message-ID: > On Wed, Apr 10, 2024 at 7:05?PM Lorenzo Bianconi wrote: > > + "requestId":"11235814", > 11235813 to match the default request ID > > + > > +static void usage(void) > > +{ > > + wpa_printf(MSG_ERROR, > > + "%s:\n" > > + "afcd -u [-p][-t][-D][-P][-dB]", > > + __func__); > > +} > missing `-d` usage what do you mean? > > + > Also, for setting `Content-Length` I had to use the below script, > should we add this as well > or at least put it in a README? ack, thx. I will fix the afc-reply example. Regards, Lorenzo > > $ cat afc_server_sim.sh > #!/bin/bash > > # Define the JSON content > json_content=$(cat afc-reply.json) > > # Calculate the length of the JSON content > content_length=$(echo "$json_content" | wc -m) > > # Create the HTTP response > http_response="HTTP/1.1 200 OK > Content-Type: application/json > Content-Length: $content_length > > $json_content" > > # Write the HTTP response to a file > echo -e "$http_response" > response.txt > > # Use netcat to listen on a specific port and send the response to any > incoming requests > while true; do nc -l 9999 < response.txt; done > ### END OF SCRIPT## > and then I run `sudo ./afcd -u http://localhost -p 9999 -ddd` -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: not available URL: From lorenzo at kernel.org Tue Apr 16 08:23:08 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 17:23:08 +0200 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: <9c773d9b-7a67-453c-9e0a-4366665e7e33@galgus.ai> References: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> <9c773d9b-7a67-453c-9e0a-4366665e7e33@galgus.ai> Message-ID: On Apr 14, Jes?s Fern?ndez Manzano wrote: > Hi all, > > El 10/4/24 a las 15:32, Lorenzo Bianconi escribi?: > > + headers = curl_slist_append(headers, "charset: utf-8"); > > After building the headers with curl_slist_append() and sending the message > with curl_easy_perform(), you must free them with curl_slist_free_all() or > the memory will be leaked. See: > > - https://curl.se/libcurl/c/curl_slist_append.html > - https://curl.se/libcurl/c/curl_slist_free_all.html ack, thx. I will fix it. Regartds, Lorenzo > > Regards, > Jes?s -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: not available URL: From lorenzo at kernel.org Tue Apr 16 09:16:51 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 18:16:51 +0200 Subject: [PATCH v5 0/4] Introduce Automated Frequency Coordination (AFC) support Message-ID: Introduce Automated Frequency Coordination (AFC) support for UNII-5 and UNII-7 6GHz bands. Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. AFCD and AFC hostapd client are tested with AFC DUT Test Harness [0]. Changes since v4: - fix curl header parsing - increase afc buffer size - fix afc-reply.txt - add some more error logging - add sanity check for some afc config file data Changes since v3: - fix compilation errors when IEEE80211_AX is not enabled - fix a leftover crash stopping the interface - make sure rx buffer is null-terminated Changes since v2: - multiple fixes testing with AFC DUT Test Harness Changes since v1: - use unix socket instead of a tcp one Changes since rfc: - move curl connection to a dedicated afc daemon Lorenzo Bianconi (4): hostapd: afcd: add AFC daemon support hostapd: export hostapd_is_usable_chans utility routine hostapd: ap: add AFC client support hostapd: update TPE IE according to AFC afc/.gitignore | 1 + afc/Makefile | 31 ++ afc/afc-reply.txt | 219 +++++++++ afc/afcd.c | 306 ++++++++++++ hostapd/Makefile | 8 + hostapd/config_file.c | 261 +++++++++++ hostapd/defconfig | 3 + hostapd/hostapd.conf | 42 ++ src/ap/afc.c | 1033 +++++++++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 16 + src/ap/ap_config.h | 47 ++ src/ap/hostapd.c | 16 + src/ap/hostapd.h | 54 +++ src/ap/hw_features.c | 4 +- src/ap/hw_features.h | 6 + src/ap/ieee802_11.c | 47 +- 16 files changed, 2075 insertions(+), 19 deletions(-) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile create mode 100644 afc/afc-reply.txt create mode 100644 afc/afcd.c create mode 100644 src/ap/afc.c -- 2.44.0 From lorenzo at kernel.org Tue Apr 16 09:16:53 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 18:16:53 +0200 Subject: [PATCH v5 2/4] hostapd: export hostapd_is_usable_chans utility routine In-Reply-To: References: Message-ID: <938de434185c629b1b0d8b23fec621229d68d8f8.1713283884.git.lorenzo@kernel.org> This is a preliminary patch to introduce AFC support. Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- src/ap/hw_features.c | 2 +- src/ap/hw_features.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index c4556603d..85e67080d 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -1008,7 +1008,7 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface) * 0 = not usable * -1 = not currently usable due to 6 GHz NO-IR */ -static int hostapd_is_usable_chans(struct hostapd_iface *iface) +int hostapd_is_usable_chans(struct hostapd_iface *iface) { int secondary_freq; struct hostapd_channel_data *pri_chan; diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index c682c6d20..eeffb1abd 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -30,6 +30,7 @@ void hostapd_stop_setup_timers(struct hostapd_iface *iface); int hostapd_hw_skip_mode(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); int hostapd_determine_mode(struct hostapd_iface *iface); +int hostapd_is_usable_chans(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -103,6 +104,11 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface) return 0; } +static inline int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + return 1; +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ -- 2.44.0 From lorenzo at kernel.org Tue Apr 16 09:16:52 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 18:16:52 +0200 Subject: [PATCH v5 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: References: Message-ID: <0a67c48fcb2da589fe971d46f5380e189c8f8fe8.1713283884.git.lorenzo@kernel.org> Introduce Automated Frequency Coordination Daemon (AFCD) support for UNII-5 and UNII-7 6GHz bands. AFCD will be used by hostapd AFC client in order to forward the AFC request to the AFC coordinator and decouple AFC connection management from hostapd. AFC is required for Standard Power Devices (SPDs) to determine a lists of channels and EIRP/PSD powers that are available in the 6GHz spectrum. AFCD is tested with AFC DUT Test Harness [0]. Add afc-reply.json as reference for replies from the AFC coordinator. [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- afc/.gitignore | 1 + afc/Makefile | 31 +++++ afc/afc-reply.txt | 219 +++++++++++++++++++++++++++++++++ afc/afcd.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+) create mode 100644 afc/.gitignore create mode 100644 afc/Makefile create mode 100644 afc/afc-reply.txt create mode 100644 afc/afcd.c diff --git a/afc/.gitignore b/afc/.gitignore new file mode 100644 index 000000000..8d8cca905 --- /dev/null +++ b/afc/.gitignore @@ -0,0 +1 @@ +afcd diff --git a/afc/Makefile b/afc/Makefile new file mode 100644 index 000000000..a83bd01db --- /dev/null +++ b/afc/Makefile @@ -0,0 +1,31 @@ +ALL=afcd + +include ../src/build.rules + +CFLAGS += -I../src/utils +CFLAGS += -I../src + +OBJS=afcd.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif +OBJS += ../src/utils/os_$(CONFIG_OS).o + +LIBS += -lcurl + +_OBJS_VAR := OBJS +include ../src/objs.mk +afcd: $(OBJS) + $(Q)$(LDO) $(LDFLAGS) -o afcd $(OBJS) $(LIBS) + @$(E) " LD " $@ + +clean: common-clean + rm -f core *~ diff --git a/afc/afc-reply.txt b/afc/afc-reply.txt new file mode 100644 index 000000000..581b78fa2 --- /dev/null +++ b/afc/afc-reply.txt @@ -0,0 +1,219 @@ +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 4843 + +{ + "availableSpectrumInquiryResponses":[ + { + "availabilityExpireTime":"2023-02-23T12:53:18Z", + "availableChannelInfo":[ + { + "channelCfi":[ + 1, + 5, + 9, + 13, + 17, + 21, + 25, + 29, + 33, + 37, + 41, + 45, + 49, + 53, + 57, + 61, + 65, + 69, + 73, + 77, + 81, + 85, + 89, + 93, + 117, + 121, + 125, + 129, + 133, + 137, + 141, + 145, + 149, + 153, + 157, + 161, + 165, + 169, + 173, + 177, + 181 + ], + "globalOperatingClass":131, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 3, + 11, + 19, + 27, + 35, + 43, + 51, + 59, + 67, + 75, + 83, + 91, + 123, + 131, + 139, + 147, + 155, + 163, + 171, + 179 + ], + "globalOperatingClass":132, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 7, + 23, + 39, + 55, + 71, + 87, + 135, + 151, + 167 + ], + "globalOperatingClass":133, + "maxEirp":[ + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + 15, + 47, + 79, + 143 + ], + "globalOperatingClass":134, + "maxEirp":[ + 5, + 5, + 5, + 5 + ] + }, + { + "channelCfi":[ + ], + "globalOperatingClass":135, + "maxEirp":[ + ] + } + ], + "availableFrequencyInfo":[ + { + "frequencyRange":{ + "highFrequency":6425, + "lowFrequency":5925 + }, + "maxPSD":3.98970004336019 + }, + { + "frequencyRange":{ + "highFrequency":6865, + "lowFrequency":6525 + }, + "maxPSD":3.98970004336019 + } + ], + "requestId":"11235814", + "response":{ + "responseCode":0, + "shortDescription":"Success" + }, + "rulesetId":"US_47_CFR_PART_15_SUBPART_E" + } + ], + "version":"1.1" +} diff --git a/afc/afcd.c b/afc/afcd.c new file mode 100644 index 000000000..572933d09 --- /dev/null +++ b/afc/afcd.c @@ -0,0 +1,306 @@ +/* + * Automated Frequency Coordination Daemon + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "utils/includes.h" +#include "utils/common.h" + +#define CURL_TIMEOUT 60 +#define AFCD_SOCK "afcd.sock" + +struct curl_ctx { + char *buf; + size_t buf_len; +}; + +static volatile bool exiting; + +static char *path = "/var/run"; +static char *bearer_token; +static char *url; +static int port = 443; + + +static size_t afcd_curl_cb_write(void *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct curl_ctx *ctx = userdata; + char *buf; + + buf = os_realloc(ctx->buf, ctx->buf_len + size * nmemb + 1); + if (!buf) + return 0; + + ctx->buf = buf; + os_memcpy(buf + ctx->buf_len, ptr, size * nmemb); + buf[ctx->buf_len + size * nmemb] = '\0'; + ctx->buf_len += size * nmemb; + + return size * nmemb; +} + + +static int afcd_send_request(struct curl_ctx *ctx, unsigned char *request) +{ + struct curl_slist *headers = NULL, *tmp; + CURL *curl; + int ret; + + wpa_printf(MSG_DEBUG, "Sending AFC request to %s", url); + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (!curl) + return -ENOMEM; + + headers = curl_slist_append(headers, "Accept: application/json"); + if (!headers) + return -ENOMEM; + + tmp = curl_slist_append(headers, "Content-Type: application/json"); + if (!tmp) { + curl_slist_free_all(headers); + return -ENOMEM; + } + headers = tmp; + + tmp = curl_slist_append(headers, "charset: utf-8"); + if (!tmp) { + curl_slist_free_all(headers); + return -ENOMEM; + } + headers = tmp; + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_PORT, port); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + afcd_curl_cb_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1"); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + if (bearer_token) + curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, bearer_token); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); + + ret = curl_easy_perform(curl); + if (ret != CURLE_OK) + wpa_printf(MSG_ERROR, "curl_easy_perform failed: %s", + curl_easy_strerror(ret)); + + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return ret == CURLE_OK ? 0 : -EINVAL; +} + + +static void handle_term(int sig) +{ + wpa_printf(MSG_ERROR, "Received signal %d", sig); + exiting = true; +} + + +static void usage(void) +{ + wpa_printf(MSG_ERROR, + "%s:\n" + "afcd -u [-p][-t][-D][-P][-dB]", + __func__); +} + + +#define BUFSIZE 8192 +static int afcd_server_run(void) +{ + size_t len = os_strlen(path) + 1 + os_strlen(AFCD_SOCK); + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + int sockfd, ret = 0; + char *fname = NULL; + unsigned char *buf; + fd_set read_set; + + if (len >= sizeof(addr.sun_path)) + return -EINVAL; + + if (mkdir(path, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) + return -EINVAL; + + buf = os_malloc(BUFSIZE); + if (!buf) + return -ENOMEM; + + fname = os_malloc(len + 1); + if (!fname) { + ret = -ENOMEM; + goto free_buf; + } + + os_snprintf(fname, len + 1, "%s/%s", path, AFCD_SOCK); + fname[len] = '\0'; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating socket"); + ret = -errno; + goto unlink; + } + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed to bind socket"); + ret = -errno; + goto close; + } + + if (listen(sockfd, 10) < 0) { + wpa_printf(MSG_ERROR, "Failed to listen on socket"); + ret = -errno; + goto close; + } + + FD_ZERO(&read_set); + while (!exiting) { + socklen_t addr_len = sizeof(addr); + struct sockaddr_in6 c_addr; + struct timeval timeout = { + .tv_sec = 1, + }; + struct curl_ctx ctx = {}; + int fd; + + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &timeout) < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Select failed on socket"); + ret = -errno; + break; + } + continue; + } + + if (!FD_ISSET(sockfd, &read_set)) + continue; + + fd = accept(sockfd, (struct sockaddr *)&c_addr, + &addr_len); + if (fd < 0) { + if (errno != EINTR) { + wpa_printf(MSG_ERROR, + "Failed accepting connections"); + ret = -errno; + break; + } + continue; + } + + os_memset(buf, 0, BUFSIZE); + if (recv(fd, buf, BUFSIZE - 1, 0) <= 0) { + close(fd); + continue; + } + + wpa_printf(MSG_DEBUG, "Received request: %s", buf); + if (!afcd_send_request(&ctx, buf)) { + wpa_printf(MSG_DEBUG, "Received reply: %s", ctx.buf); + send(fd, ctx.buf, ctx.buf_len, MSG_NOSIGNAL); + free(ctx.buf); + } + close(fd); + } +close: + close(sockfd); +unlink: + unlink(fname); + os_free(fname); +free_buf: + os_free(buf); + + return ret; +} + + +int main(int argc, char **argv) +{ + bool daemonize = false; + char *pid_file = NULL; + + if (os_program_init()) + return -1; + + for (;;) { + int c = getopt(argc, argv, "u:p:t:D:P:hdB"); + + if (c < 0) + break; + + switch (c) { + case 'h': + usage(); + return 0; + case 'B': + daemonize = true; + break; + case 'D': + path = optarg; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 'u': + url = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'd': + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 't': + bearer_token = optarg; + break; + default: + usage(); + return -EINVAL; + } + } + + if (!url) { + usage(); + return -EINVAL; + } + + if (daemonize && os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -EINVAL; + } + + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); + + return afcd_server_run(); +} -- 2.44.0 From lorenzo at kernel.org Tue Apr 16 09:16:55 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 18:16:55 +0200 Subject: [PATCH v5 4/4] hostapd: update TPE IE according to AFC In-Reply-To: References: Message-ID: <43c6a5b4dd9e380aa7bda91b2e1854e19c696bb1.1713283884.git.lorenzo@kernel.org> Update Transmit Power Envelope (TPE) IE according to the reply from AFC coordinator on UNII-5 or UNII-7 6GHz bands. Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- src/ap/afc.c | 37 +++++++++++++++++++++++++++++++++++ src/ap/hostapd.h | 9 +++++++++ src/ap/ieee802_11.c | 47 ++++++++++++++++++++++++++++----------------- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/ap/afc.c b/src/ap/afc.c index 21e1e17cb..053c8d267 100644 --- a/src/ap/afc.c +++ b/src/ap/afc.c @@ -994,3 +994,40 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface) chan->freq); } } + + +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + int i; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return -EINVAL; + + if (!iface->afc.data_valid) + return -EINVAL; + + if (psd) { + for (i = 0; i < iface->afc.num_freq_range; i++) { + struct afc_freq_range_elem *f; + + f = &iface->afc.freq_range[i]; + if (iface->freq >= f->low_freq && + iface->freq <= f->high_freq) { + *power = 2 * f->max_psd; + return 0; + } + } + } else { + for (i = 0; i < iface->afc.num_chan_info; i++) { + struct afc_chan_info_elem *c; + + c = &iface->afc.chan_info_list[i]; + if (c->chan == iface->conf->channel) { + *power = 2 * c->power; + return 0; + } + } + } + return -EINVAL; +} diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 88d111022..5e8e10449 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -740,10 +740,19 @@ struct hostapd_iface { /* hostapd.c */ #ifdef CONFIG_AFC +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power); int hostapd_afc_handle_request(struct hostapd_iface *iface); void hostapd_afc_stop(struct hostapd_iface *iface); void hostap_afc_disable_channels(struct hostapd_iface *iface); #else +static inline int +hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + return -EINVAL; +} + static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) { return 1; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 85a39d5ff..df07405ed 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -7104,42 +7104,53 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) */ if (is_6ghz_op_class(iconf->op_class)) { enum max_tx_pwr_interpretation tx_pwr_intrpn; + int err, max_eirp_psd, max_eirp_power; /* Same Maximum Transmit Power for all 20 MHz bands */ tx_pwr_count = 0; tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD; /* Default Transmit Power Envelope for Global Operating Class */ - if (hapd->iconf->reg_def_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_def_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + err = hostap_afc_get_chan_max_eirp_power(iface, true, + &max_eirp_psd); + if (err < 0) { + if (hapd->iconf->reg_def_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_def_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, - REG_DEFAULT_CLIENT, tx_pwr); + REG_DEFAULT_CLIENT, max_eirp_psd); /* Indoor Access Point must include an additional TPE for * subordinate devices */ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) { - /* TODO: Extract PSD limits from channel data */ - if (hapd->iconf->reg_sub_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + if (err < 0) { + /* non-AFC connection */ + if (hapd->iconf->reg_sub_cli_eirp_psd != -1) + max_eirp_psd = hapd->iconf->reg_sub_cli_eirp_psd; + else + max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, REG_SUBORDINATE_CLIENT, - tx_pwr); + max_eirp_psd); } - if (iconf->reg_def_cli_eirp != -1 && - he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) - eid = hostapd_add_tpe_info( - eid, tx_pwr_count, REGULATORY_CLIENT_EIRP, - REG_DEFAULT_CLIENT, - hapd->iconf->reg_def_cli_eirp); + if (hostap_afc_get_chan_max_eirp_power(iface, false, + &max_eirp_power)) { + max_eirp_power = iconf->reg_def_cli_eirp; + if (max_eirp_power == -1 || + !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return eid; + } - return eid; + return hostapd_add_tpe_info(eid, tx_pwr_count, + REGULATORY_CLIENT_EIRP, + REG_DEFAULT_CLIENT, + max_eirp_power); } #endif /* CONFIG_IEEE80211AX */ -- 2.44.0 From lorenzo at kernel.org Tue Apr 16 09:16:54 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 18:16:54 +0200 Subject: [PATCH v5 3/4] hostapd: ap: add AFC client support In-Reply-To: References: Message-ID: Introduce Automated Frequency Coordination (AFC) support for UNII-5 and UNII-7 6GHz bands. AFC client will connect to AFCD providing AP related parameter for AFC coordinator (e.g. geolocation, supported frequencies, ..). AFC is required for Standard Power Devices (SPDs) to determine a lists of channels and EIRP/PSD powers that are available in the 6GHz spectrum. AFC hostapd client is tested with AFC DUT Test Harness [0]. [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main Tested-by: Felix Fietkau Tested-by: Allen Ye Signed-off-by: Lorenzo Bianconi --- hostapd/Makefile | 8 + hostapd/config_file.c | 261 +++++++++++ hostapd/defconfig | 3 + hostapd/hostapd.conf | 42 ++ src/ap/afc.c | 996 ++++++++++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 16 + src/ap/ap_config.h | 47 ++ src/ap/hostapd.c | 16 + src/ap/hostapd.h | 45 ++ src/ap/hw_features.c | 2 + 10 files changed, 1436 insertions(+) create mode 100644 src/ap/afc.c diff --git a/hostapd/Makefile b/hostapd/Makefile index ca4439234..78171025e 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -104,6 +104,14 @@ CFLAGS += -DCONFIG_TAXONOMY OBJS += ../src/ap/taxonomy.o endif +ifdef CONFIG_IEEE80211AX +ifdef CONFIG_AFC +CFLAGS += -DCONFIG_AFC +OBJS += ../src/ap/afc.o +LIBS += -ljson-c +endif +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += hapd_module_tests.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 9a8f34e0d..1e9ed49c1 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1281,6 +1281,190 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) return 0; } + +#ifdef CONFIG_AFC +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos) +{ + struct cert_id *c = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + c[i].rulset = os_malloc(os_strlen(pos) + 1); + if (!c[i].rulset) + goto error; + + os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + c[i].id = os_malloc(os_strlen(pos) + 1); + if (!c[i].id) + goto error; + + os_strlcpy(c[i].id, pos, os_strlen(pos) + 1); + pos = p; + } + + conf->afc.n_cert_ids = count; + conf->afc.cert_ids = c; + + return 0; + +error: + for (i = 0; i < count; i++) { + os_free(c[i].rulset); + os_free(c[i].id); + } + os_free(c); + + return -ENOMEM; +} + + +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data, + unsigned int *n_linear_polygon_data, + char *pos) +{ + struct afc_linear_polygon *d = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p, *end; + + d = os_realloc_array(d, count + 1, sizeof(*d)); + if (!d) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + d[i].longitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + d[i].latitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + } + + *n_linear_polygon_data = count; + *data = d; + + return 0; + +error: + os_free(d); + return -ENOMEM; +} + + +static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos) +{ + struct afc_freq_range *f = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + f[i].low_freq = atoi(pos); + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + f[i].high_freq = atoi(pos); + pos = p; + } + + conf->afc.n_freq_range = count; + conf->afc.freq_range = f; + + return 0; + +error: + os_free(f); + return -ENOMEM; +} + + +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos) +{ + unsigned int *oc = NULL; + int i, count = 0; + + while (pos && pos[0]) { + char *p; + + oc = os_realloc_array(oc, count + 1, sizeof(*oc)); + if (!oc) + return -ENOMEM; + + i = count; + count++; + + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + oc[i] = atoi(pos); + pos = p; + } + + conf->afc.n_op_class = count; + conf->afc.op_class = oc; + + return 0; +} +#endif /* CONFIG_AFC */ #endif /* CONFIG_IEEE80211AX */ @@ -3862,6 +4046,83 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->unsol_bcast_probe_resp_interval = val; +#ifdef CONFIG_AFC + } else if (os_strcmp(buf, "afcd_sock") == 0) { + conf->afc.socket = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.socket) + return 1; + + os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_version") == 0) { + conf->afc.request.version = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.version) + return 1; + + os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_request_id") == 0) { + conf->afc.request.id = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.id) + return 1; + + os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_serial_number") == 0) { + conf->afc.request.sn = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.request.sn) + return 1; + + os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_cert_ids") == 0) { + if (hostapd_afc_parse_cert_ids(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_location_type") == 0) { + conf->afc.location.type = atoi(pos); + if (conf->afc.location.type != ELLIPSE && + conf->afc.location.type != LINEAR_POLYGON && + conf->afc.location.type != RADIAL_POLYGON) + return 1; + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) { + if (hostapd_afc_parse_position_data( + &conf->afc.location.linear_polygon_data, + &conf->afc.location.n_linear_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) { + if (hostapd_afc_parse_position_data( + (struct afc_linear_polygon **) + &conf->afc.location.radial_polygon_data, + &conf->afc.location.n_radial_polygon_data, + pos)) + return 1; + } else if (os_strcmp(buf, "afc_major_axis") == 0) { + conf->afc.location.major_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_minor_axis") == 0) { + conf->afc.location.minor_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_orientation") == 0) { + conf->afc.location.orientation = atoi(pos); + } else if (os_strcmp(buf, "afc_height") == 0) { + char *end; + + conf->afc.location.height = strtod(pos, &end); + if (*end) + return 1; + } else if (os_strcmp(buf, "afc_height_type") == 0) { + conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1); + if (!conf->afc.location.height_type) + return 1; + + os_strlcpy(conf->afc.location.height_type, pos, + os_strlen(pos) + 1); + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) { + conf->afc.location.vertical_tolerance = atoi(pos); + } else if (os_strcmp(buf, "afc_min_power") == 0) { + conf->afc.min_power = atoi(pos); + } else if (os_strcmp(buf, "afc_freq_range") == 0) { + if (hostapd_afc_parse_freq_range(conf, pos)) + return 1; + } else if (os_strcmp(buf, "afc_op_class") == 0) { + if (hostapd_afc_parse_op_class(conf, pos)) + return 1; +#endif /* CONFIG_AFC */ } else if (os_strcmp(buf, "mbssid") == 0) { int mbssid = atoi(pos); if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { diff --git a/hostapd/defconfig b/hostapd/defconfig index 550db697b..66bf894eb 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -425,3 +425,6 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Enable Automated Frequency Coordination for 6GHz outdoor +#CONFIG_AFC=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 51ca05e25..50e0384fd 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0 # Valid range: 0..20 TUs; default is 0 (disabled) #unsol_bcast_probe_resp_interval=0 +##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands ####### + +# AFC daemon connection socket +#afcd_sock=/var/run/afcd.sock + +# AFC request identification parameters +#afc_request_version=1.1 +#afc_request_id=11235813 +#afc_serial_number=abcdefg +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000 +# +# AFC location type: +# 0 = ellipse +# 1 = linear polygon +# 2 = radial polygon +#afc_location_type=0 +# +# AFC ellipse or linear polygon coordinations +#afc_linear_polygon=-122.984157:37.425056 +# +# AFC radial polygon coordinations +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33 +# +# AFC ellipse major/minor axis and orientation +#afc_major_axis=100 +#afc_minor_axis=50 +#afc_orientation=70 +# +# AFC device elevation parameters +#afc_height=3.0 +#afc_height_type=AGL +#afc_vertical_tolerance=7 +# +# AFC minimum desired TX power (dbm) +#afc_min_power=24 +# +# AFC request frequency ranges +#afc_freq_range=5925:6425,6525:6875 +# +# AFC request operation classes +#afc_op_class=131,132,133,134,136 + ##### IEEE 802.11be related configuration ##################################### #ieee80211be: Whether IEEE 802.11be (EHT) is enabled diff --git a/src/ap/afc.c b/src/ap/afc.c new file mode 100644 index 000000000..21e1e17cb --- /dev/null +++ b/src/ap/afc.c @@ -0,0 +1,996 @@ +/* + * Automated Frequency Coordination + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include +#include + +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "acs.h" +#include "hw_features.h" + +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ +#define HOSTAPD_AFC_BUFSIZE 8192 + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); + + +static struct json_object * +hostapd_afc_build_location_request(struct hostapd_iface *iface) +{ + struct json_object *location_obj, *center_obj, *ellipse_obj; + struct json_object *elevation_obj, *str_obj; + struct hostapd_config *iconf = iface->conf; + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); + + location_obj = json_object_new_object(); + if (!location_obj) + return NULL; + + if (iconf->afc.location.type != LINEAR_POLYGON) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[0]; + + if (!lp) + goto error; + + ellipse_obj = json_object_new_object(); + if (!ellipse_obj) + goto error; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_object_add(ellipse_obj, "center", center_obj); + + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", str_obj); + } + + switch (iconf->afc.location.type) { + case LINEAR_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + outer_boundary_obj = json_object_new_object(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(location_obj, "linearPolygon", + outer_boundary_obj); + ellipse_obj = json_object_new_array(); + if (!ellipse_obj) + goto error; + + json_object_object_add(outer_boundary_obj, "outerBoundary", + ellipse_obj); + for (i = 0; + i < iconf->afc.location.n_linear_polygon_data; i++) { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[i]; + + center_obj = json_object_new_object(); + if (!center_obj) + goto error; + + json_object_array_add(ellipse_obj, center_obj); + str_obj = json_object_new_double(lp->longitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "longitude", + str_obj); + str_obj = json_object_new_double(lp->latitude); + if (!str_obj) + goto error; + + json_object_object_add(center_obj, "latitude", + str_obj); + } + break; + } + case RADIAL_POLYGON: { + struct json_object *outer_boundary_obj; + int i; + + json_object_object_add(location_obj, "radialPolygon", + ellipse_obj); + + outer_boundary_obj = json_object_new_array(); + if (!outer_boundary_obj) + goto error; + + json_object_object_add(ellipse_obj, "outerBoundary", + outer_boundary_obj); + for (i = 0; + i < iconf->afc.location.n_radial_polygon_data; i++) { + struct afc_radial_polygon *rp = + &iconf->afc.location.radial_polygon_data[i]; + struct json_object *angle_obj; + + angle_obj = json_object_new_object(); + if (!angle_obj) + goto error; + + json_object_array_add(outer_boundary_obj, angle_obj); + + str_obj = json_object_new_double(rp->angle); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "angle", str_obj); + str_obj = json_object_new_double(rp->length); + if (!str_obj) + goto error; + + json_object_object_add(angle_obj, "length", str_obj); + } + break; + } + case ELLIPSE: + default: + json_object_object_add(location_obj, "ellipse", ellipse_obj); + + str_obj = json_object_new_int(iconf->afc.location.major_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "majorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.minor_axis); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "minorAxis", str_obj); + str_obj = json_object_new_int(iconf->afc.location.orientation); + if (!str_obj) + goto error; + + json_object_object_add(ellipse_obj, "orientation", str_obj); + break; + } + + elevation_obj = json_object_new_object(); + if (!elevation_obj) + goto error; + + json_object_object_add(location_obj, "elevation", + elevation_obj); + str_obj = json_object_new_double(iconf->afc.location.height); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "height", str_obj); + if (iconf->afc.location.height_type) { + str_obj = json_object_new_string(iconf->afc.location.height_type); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "heightType", str_obj); + } + + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance); + if (!str_obj) + goto error; + + json_object_object_add(elevation_obj, "verticalUncertainty", + str_obj); + str_obj = json_object_new_int(is_ap_indoor); + if (!str_obj) + goto error; + + json_object_object_add(location_obj, "indoorDeployment", str_obj); + + return location_obj; + +error: + json_object_put(location_obj); + return NULL; +} + + +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class) +{ + struct json_object *chan_list_obj, *str_obj; + const struct oper_class_map *oper_class; + int chan_offset, chan; + + oper_class = get_oper_class(NULL, op_class); + if (!oper_class) + return NULL; + + chan_list_obj = json_object_new_array(); + if (!chan_list_obj) + return NULL; + + switch (op_class) { + case 132: /* 40MHz */ + chan_offset = 2; + break; + case 133: /* 80MHz */ + chan_offset = 6; + break; + case 134: /* 160MHz */ + chan_offset = 14; + break; + default: + chan_offset = 0; + break; + } + + for (chan = oper_class->min_chan; chan <= oper_class->max_chan; + chan += oper_class->inc) { + str_obj = json_object_new_int(chan + chan_offset); + if (!str_obj) { + json_object_put(chan_list_obj); + return NULL; + } + json_object_array_add(chan_list_obj, str_obj); + } + + return chan_list_obj; +} + + +static struct json_object * +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface) +{ + struct json_object *op_class_list_obj, *str_obj; + struct hostapd_config *iconf = iface->conf; + int i; + + op_class_list_obj = json_object_new_array(); + if (!op_class_list_obj) + return NULL; + + for (i = 0; i < iconf->afc.n_op_class; i++) { + struct json_object *op_class_obj, *chan_list_obj; + u8 op_class = iconf->afc.op_class[i]; + + if (!is_6ghz_op_class(op_class)) + continue; + + op_class_obj = json_object_new_object(); + if (!op_class_obj) + goto error; + + json_object_array_add(op_class_list_obj, op_class_obj); + str_obj = json_object_new_int(op_class); + if (!str_obj) + goto error; + + json_object_object_add(op_class_obj, "globalOperatingClass", + str_obj); + + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class); + if (!chan_list_obj) + goto error; + + json_object_object_add(op_class_obj, "channelCfi", + chan_list_obj); + } + + return op_class_list_obj; + +error: + json_object_put(op_class_list_obj); + return NULL; +} + + +static struct json_object * +hostapd_afc_build_request(struct hostapd_iface *iface) +{ + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj; + struct json_object *s2_obj, *str_obj, *location_obj; + struct hostapd_config *iconf = iface->conf; + struct json_object *op_class_list_obj; + int i; + + l1_obj = json_object_new_object(); + if (!l1_obj) + return NULL; + + if (iconf->afc.request.version) { + str_obj = json_object_new_string(iconf->afc.request.version); + if (!str_obj) + goto error; + + json_object_object_add(l1_obj, "version", str_obj); + } + + la1_obj = json_object_new_array(); + if (!la1_obj) + goto error; + + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests", + la1_obj); + l2_obj = json_object_new_object(); + if (!l2_obj) + goto error; + + json_object_array_add(la1_obj, l2_obj); + if (iconf->afc.request.id) { + str_obj = json_object_new_string(iconf->afc.request.id); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "requestId", str_obj); + } + + s2_obj = json_object_new_object(); + if (!s2_obj) + goto error; + + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj); + if (iconf->afc.request.sn) { + str_obj = json_object_new_string(iconf->afc.request.sn); + if (!str_obj) + goto error; + + json_object_object_add(s2_obj, "serialNumber", str_obj); + } + + la2_obj = json_object_new_array(); + if (!la2_obj) + goto error; + + json_object_object_add(s2_obj, "certificationId", la2_obj); + for (i = 0; i < iconf->afc.n_cert_ids; i++) { + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(la2_obj, obj); + str_obj = + json_object_new_string(iconf->afc.cert_ids[i].rulset); + if (!str_obj) + goto error; + + json_object_object_add(obj, "rulesetId", str_obj); + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id); + if (!str_obj) + goto error; + + json_object_object_add(obj, "id", str_obj); + } + + location_obj = hostapd_afc_build_location_request(iface); + if (!location_obj) + goto error; + + json_object_object_add(l2_obj, "location", location_obj); + str_obj = json_object_new_int(iconf->afc.min_power); + if (!str_obj) + goto error; + + json_object_object_add(l2_obj, "minDesiredPower", str_obj); + + if (iconf->afc.n_freq_range) { + struct json_object *freq_obj; + + freq_obj = json_object_new_array(); + if (!freq_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredFrequencyRange", + freq_obj); + for (i = 0; i < iconf->afc.n_freq_range; i++) { + struct afc_freq_range *fr = &iconf->afc.freq_range[i]; + struct json_object *obj; + + obj = json_object_new_object(); + if (!obj) + goto error; + + json_object_array_add(freq_obj, obj); + str_obj = json_object_new_int(fr->low_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "lowFrequency", str_obj); + str_obj = json_object_new_int(fr->high_freq); + if (!str_obj) + goto error; + + json_object_object_add(obj, "highFrequency", str_obj); + } + } + + op_class_list_obj = hostapd_afc_build_req_chan_list(iface); + if (!op_class_list_obj) + goto error; + + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj); + + wpa_printf(MSG_DEBUG, "Pending AFC request: %s", + json_object_get_string(l1_obj)); + + return l1_obj; + +error: + json_object_put(l1_obj); + + return NULL; +} + + +static int +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct afc_freq_range_elem *f = NULL; + struct json_object *obj; + int i, count = 0; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableFrequencyInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct json_object *range_elem_obj, *freq_range_obj; + struct json_object *high_freq_obj, *low_freq_obj; + struct json_object *max_psd_obj; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!range_elem_obj) + continue; + + if (!json_object_object_get_ex(range_elem_obj, + "frequencyRange", + &freq_range_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "lowFrequency", + &low_freq_obj)) + continue; + + if (!json_object_object_get_ex(freq_range_obj, + "highFrequency", + &high_freq_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxPsd", + &max_psd_obj) && + !json_object_object_get_ex(range_elem_obj, "maxPSD", + &max_psd_obj)) + continue; + + f = os_realloc_array(f, count + 1, sizeof(*f)); + if (!f) + return -ENOMEM; + + f[count].low_freq = json_object_get_int(low_freq_obj); + f[count].high_freq = json_object_get_int(high_freq_obj); + f[count++].max_psd = json_object_get_int(max_psd_obj); + } + iface->afc.freq_range = f; + iface->afc.num_freq_range = count; + + return 0; +} + + +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list, + int *chan_list_size, u8 op_class, + int center_chan, int power) +{ + int num_low_subchan, ch, count = *chan_list_size; + struct afc_chan_info_elem *c = *chan_list; + + switch (op_class) { + case 132: /* 40MHz */ + num_low_subchan = 2; + break; + case 133: /* 80MHz */ + num_low_subchan = 6; + break; + case 134: /* 160MHz */ + num_low_subchan = 14; + break; + default: + num_low_subchan = 0; + break; + } + + for (ch = center_chan - num_low_subchan; + ch <= center_chan + num_low_subchan; ch += 4) { + int i; + + for (i = 0; i < count; i++) { + if (c[i].chan == ch) + break; + } + + if (i == count) { + c = os_realloc_array(c, count + 1, sizeof(*c)); + if (!c) + return -ENOMEM; + + c[count].chan = ch; + c[count++].power = power; + } + } + + *chan_list_size = count; + *chan_list = c; + + return 0; +} + + +static int +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, + struct json_object *reply_elem_obj) +{ + struct afc_chan_info_elem *c = NULL; + struct json_object *obj; + int i, count = 0; + + if (!json_object_object_get_ex(reply_elem_obj, + "availableChannelInfo", &obj)) + return 0; + + for (i = 0; i < json_object_array_length(obj); i++) { + struct json_object *range_elem_obj, *op_class_obj; + struct json_object *chan_cfi_obj, *max_eirp_obj; + int ch, op_class; + + range_elem_obj = json_object_array_get_idx(obj, i); + if (!range_elem_obj) + continue; + + if (!json_object_object_get_ex(range_elem_obj, + "globalOperatingClass", + &op_class_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "maxEirp", + &max_eirp_obj)) + continue; + + if (!json_object_object_get_ex(range_elem_obj, "channelCfi", + &chan_cfi_obj)) + continue; + + op_class = json_object_get_int(op_class_obj); + for (ch = 0; + ch < json_object_array_length(chan_cfi_obj); ch++) { + struct json_object *pwr_obj; + struct json_object *ch_obj; + int channel, power; + + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch); + if (!ch_obj) + continue; + + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch); + if (!pwr_obj) + continue; + + channel = json_object_get_int(ch_obj); + power = json_object_get_int(pwr_obj); + + hostad_afc_update_chan_info(&c, &count, op_class, + channel, power); + } + iface->afc.chan_info_list = c; + iface->afc.num_chan_info = count; + } + + return 0; +} + + +static int hostad_afc_get_timeout(struct json_object *obj) +{ + time_t t, now; + struct tm tm; + + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec) <= 0) + return HOSTAPD_AFC_TIMEOUT; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_isdst = -1; + t = mktime(&tm); + time(&now); + + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100; +} + + +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) +{ + struct json_object *payload_obj, *reply_obj, *version_obj; + struct hostapd_config *iconf = iface->conf; + int i, request_timeout = -1, ret = -EINVAL; + + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply); + payload_obj = json_tokener_parse(reply); + if (!payload_obj) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) + return -EINVAL; + + if (iconf->afc.request.version && + os_strcmp(iconf->afc.request.version, + json_object_get_string(version_obj))) + return -EINVAL; + + if (!json_object_object_get_ex(payload_obj, + "availableSpectrumInquiryResponses", + &reply_obj)) + return -EINVAL; + + for (i = 0; i < json_object_array_length(reply_obj); i++) { + struct json_object *reply_elem_obj, *obj, *status_obj; + int j, status = -EINVAL; + + reply_elem_obj = json_object_array_get_idx(reply_obj, i); + if (!reply_elem_obj) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "requestId", + &obj)) + continue; + + if (iconf->afc.request.id && + os_strcmp(iconf->afc.request.id, + json_object_get_string(obj))) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId", + &obj)) + continue; + + for (j = 0; j < iconf->afc.n_cert_ids; j++) { + if (!os_strcmp(iconf->afc.cert_ids[j].rulset, + json_object_get_string(obj))) + break; + } + + if (j == iconf->afc.n_cert_ids) + continue; + + if (!json_object_object_get_ex(reply_elem_obj, "response", + &obj)) + continue; + + if (json_object_object_get_ex(obj, "shortDescription", + &status_obj)) + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s", + i, json_object_get_string(status_obj)); + + if (json_object_object_get_ex(obj, "responseCode", + &status_obj)) + status = json_object_get_int(status_obj); + + if (status < 0) + continue; + + if (hostad_afc_parse_available_freq_info(iface, + reply_elem_obj) || + hostad_afc_parse_available_chan_info(iface, + reply_elem_obj)) + continue; + + if (json_object_object_get_ex(reply_elem_obj, + "availabilityExpireTime", + &obj)) { + int timeout = hostad_afc_get_timeout(obj); + + if (request_timeout < 0 || timeout < request_timeout) + request_timeout = timeout; + } + + ret = status; + } + + iface->afc.data_valid = true; + iface->afc.timeout = request_timeout; + if (iface->afc.timeout < 0) + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + + return ret; +} + + +static int hostapd_afc_send_receive(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + json_object *request_obj = NULL; + struct timeval sock_timeout = { + .tv_sec = 5, + }; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + const char *request; + char *buf = NULL; + int sockfd, ret; + fd_set read_set; + + if (iface->afc.data_valid) { + /* AFC data already downloaded from the server */ + return 0; + } + + if (!iconf->afc.socket) { + wpa_printf(MSG_ERROR, "Missing AFC socket string"); + return -EINVAL; + } + + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", + iconf->afc.socket); + return -EINVAL; + } + + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "Failed creating AFC socket"); + return sockfd; + } + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "Failed connecting AFC socket"); + ret = -EIO; + goto close_sock; + } + + request_obj = hostapd_afc_build_request(iface); + if (!request_obj) { + ret = -ENOMEM; + goto close_sock; + } + + request = json_object_to_json_string(request_obj); + if (send(sockfd, request, strlen(request), 0) < 0) { + wpa_printf(MSG_ERROR, "Failed sending AFC request"); + ret = -EIO; + goto close_sock; + } + + FD_ZERO(&read_set); + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { + wpa_printf(MSG_ERROR, "Select failed on AFC socket"); + ret = -errno; + goto close_sock; + } + + if (!FD_ISSET(sockfd, &read_set)) { + ret = -EIO; + goto close_sock; + } + + buf = os_zalloc(HOSTAPD_AFC_BUFSIZE); + if (!buf) { + ret = -ENOMEM; + goto close_sock; + } + + ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0); + if (ret <= 0) + goto close_sock; + + ret = hostapd_afc_parse_reply(iface, buf); + if (ret) + wpa_printf(MSG_ERROR, "Failed parsing AFC reply: %d", ret); +close_sock: + os_free(buf); + json_object_put(request_obj); + close(sockfd); + + return ret; +} + + +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) +{ + const struct oper_class_map *oper_class; + int ch; + + oper_class = get_oper_class(NULL, iface->conf->op_class); + if (!oper_class) + return false; + + for (ch = oper_class->min_chan; ch <= oper_class->max_chan; + ch += oper_class->inc) { + struct hostapd_hw_modes *mode = iface->current_mode; + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if (chan->chan == ch && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + return true; + } + } + + return false; +} + + +int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + int ret; + + /* AFC is required just for standard power AP */ + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return 1; + + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) + return 1; + + if (iface->state == HAPD_IFACE_ACS) + return 1; + + ret = hostapd_afc_send_receive(iface); + if (ret < 0) { + /* + * If the connection to the AFCD failed, resched for a + * future attempt. + */ + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret); + if (ret == -EIO) + ret = 0; + goto resched; + } + + hostap_afc_disable_channels(iface); + if (!hostapd_afc_has_usable_chans(iface)) + goto resched; + + /* Trigger an ACS freq scan */ + iconf->channel = 0; + iface->freq = 0; + + if (acs_init(iface) != HOSTAPD_CHAN_ACS) { + wpa_printf(MSG_ERROR, "Could not start ACS"); + ret = -EINVAL; + } + +resched: + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); + + return ret; +} + + +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) +{ + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); + + iface->afc.num_freq_range = 0; + iface->afc.num_chan_info = 0; + + iface->afc.chan_info_list = NULL; + iface->afc.freq_range = NULL; + + iface->afc.data_valid = false; +} + + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + bool restart_iface = true; + + hostapd_afc_delete_data_from_server(iface); + if (iface->state != HAPD_IFACE_ENABLED) { + /* Hostapd is not fully enabled yet, toggle the interface */ + goto restart_interface; + } + + if (hostapd_afc_send_receive(iface) < 0 || + hostapd_get_hw_features(iface)) { + restart_iface = false; + goto restart_interface; + } + + if (hostapd_is_usable_chans(iface)) + goto resched; + + restart_iface = hostapd_afc_has_usable_chans(iface); + if (restart_iface) { + /* Trigger an ACS freq scan */ + iface->conf->channel = 0; + iface->freq = 0; + } + +restart_interface: + hostapd_disable_iface(iface); + if (restart_iface) + hostapd_enable_iface(iface); +resched: + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostapd_afc_stop(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode; + int i; + + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A) + return; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) + return; + + if (!iface->afc.data_valid) + return; + + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A]; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + int j; + + if (!is_6ghz_freq(chan->freq)) + continue; + + for (j = 0; j < iface->afc.num_freq_range; j++) { + if (chan->freq >= iface->afc.freq_range[j].low_freq && + chan->freq <= iface->afc.freq_range[j].high_freq) + break; + } + + if (j != iface->afc.num_freq_range) + continue; + + for (j = 0; j < iface->afc.num_chan_info; j++) { + if (chan->chan == iface->afc.chan_info_list[j].chan) + break; + } + + if (j != iface->afc.num_chan_info) + continue; + + chan->flag |= HOSTAPD_CHAN_DISABLED; + wpa_printf(MSG_MSGDUMP, + "Disabling freq=%d MHz (not allowed by AFC)", + chan->freq); + } +} diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index e1910d422..2282a574b 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1036,6 +1036,22 @@ void hostapd_config_free(struct hostapd_config *conf) #endif /* CONFIG_ACS */ wpabuf_free(conf->lci); wpabuf_free(conf->civic); +#ifdef CONFIG_AFC + os_free(conf->afc.socket); + os_free(conf->afc.request.version); + os_free(conf->afc.request.id); + os_free(conf->afc.request.sn); + for (i = 0; i < conf->afc.n_cert_ids; i++) { + os_free(conf->afc.cert_ids[i].rulset); + os_free(conf->afc.cert_ids[i].id); + } + os_free(conf->afc.cert_ids); + os_free(conf->afc.location.height_type); + os_free(conf->afc.location.linear_polygon_data); + os_free(conf->afc.location.radial_polygon_data); + os_free(conf->afc.freq_range); + os_free(conf->afc.op_class); +#endif /* CONFIG_AFC */ os_free(conf); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index aa513be4c..ca26959d5 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1226,6 +1226,53 @@ struct hostapd_config { MBSSID_ENABLED = 1, ENHANCED_MBSSID_ENABLED = 2, } mbssid; + +#ifdef CONFIG_AFC + struct { + char *socket; + struct { + char *version; + char *id; + char *sn; + } request; + unsigned int n_cert_ids; + struct cert_id { + char *rulset; + char *id; + } *cert_ids; + struct { + enum afc_location_type { + ELLIPSE, + LINEAR_POLYGON, + RADIAL_POLYGON, + } type; + unsigned int n_linear_polygon_data; + struct afc_linear_polygon { + double longitude; + double latitude; + } *linear_polygon_data; + unsigned int n_radial_polygon_data; + struct afc_radial_polygon { + double length; + double angle; + } *radial_polygon_data; + int major_axis; + int minor_axis; + int orientation; + double height; + char *height_type; + int vertical_tolerance; + } location; + unsigned int n_freq_range; + struct afc_freq_range { + int low_freq; + int high_freq; + } *freq_range; + unsigned int n_op_class; + unsigned int *op_class; + int min_power; + } afc; +#endif /* CONFIG_AFC */ }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 56bac4526..c36187586 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -715,6 +715,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + hostapd_afc_stop(iface); eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, NULL); @@ -2455,6 +2456,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } #endif /* CONFIG_MESH */ +#ifdef CONFIG_IEEE80211AX + /* check AFC for 6GHz channels. */ + res = hostapd_afc_handle_request(iface); + if (res <= 0) { + if (res < 0) + goto fail; + return res; + } +#endif /* CONFIG_IEEE80211AX */ + if (!delay_apply_cfg && hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, @@ -2853,6 +2864,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) hostapd_set_state(iface, HAPD_IFACE_DISABLED); + hostapd_afc_stop(iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; iface->is_no_ir = false; @@ -2926,6 +2938,10 @@ void hostapd_interface_free(struct hostapd_iface *iface) __func__, iface->bss[j]); os_free(iface->bss[j]); } +#ifdef CONFIG_AFC + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); +#endif hostapd_cleanup_iface(iface); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 1d1943ac5..88d111022 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -710,9 +710,54 @@ struct hostapd_iface { bool is_no_ir; bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */ + +#ifdef CONFIG_AFC + struct { + int timeout; + unsigned int num_freq_range; + struct afc_freq_range_elem { + int low_freq; + int high_freq; + /** + * max eirp power spectral density received from + * the AFC coordinator for this band + */ + int max_psd; + } *freq_range; + unsigned int num_chan_info; + struct afc_chan_info_elem { + int chan; + /** + * max eirp power received from the AFC coordinator + * for this channel + */ + int power; + } *chan_info_list; + bool data_valid; + } afc; +#endif /* CONFIG_AFC */ }; /* hostapd.c */ +#ifdef CONFIG_AFC +int hostapd_afc_handle_request(struct hostapd_iface *iface); +void hostapd_afc_stop(struct hostapd_iface *iface); +void hostap_afc_disable_channels(struct hostapd_iface *iface); +#else +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + return 1; +} + +static inline void hostapd_afc_stop(struct hostapd_iface *iface) +{ +} + +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ +} +#endif /* CONFIG_AFC */ + int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 85e67080d..8aa0b3ab5 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) iface->hw_features = modes; iface->num_hw_features = num_modes; + hostap_afc_disable_channels(iface); + for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; int dfs_enabled = hapd->iconf->ieee80211h && -- 2.44.0 From quic_adisi at quicinc.com Tue Apr 16 21:27:44 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Wed, 17 Apr 2024 09:57:44 +0530 Subject: [PATCH v2 05/22] hostapd: MLO: handle auth/assoc on link address In-Reply-To: <20240328181652.2956122-6-quic_adisi@quicinc.com> References: <20240328181652.2956122-6-quic_adisi@quicinc.com> Message-ID: <20240417042744.2004649-1-quic_adisi@quicinc.com> From: Sriram R Currently in function mlme_event_mgmt_tx_status(), link_id will be only filled if the frame was the last transmitted on the whole drv (driver) level. With co-hosted MLDs, there could be cases where multiple packets are sent out by various interfaces (BSS) under the same drv. Now while handling the Tx status, only one interface will get the proper link_id. Rest all will get -1, and event will be routed to first BSS always. If the packet was not sent from first BSS then this leads to possibility of packet getting dropped. Hence in order to make the underlying link identification easier, modify authentication and association frames to be always sent with link address as A1 and A3 for ease of Tx status handling. Signed-off-by: Sriram R Signed-off-by: Aditya Kumar Singh --- v2: Rephrased commit message a bit to describe why changes is required --- src/ap/ieee802_11.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 85a39d5ffb0e..b045dc6f7cb0 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -416,14 +416,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, struct wpabuf *ml_resp = NULL; #ifdef CONFIG_IEEE80211BE - /* - * Once a non-AP MLD is added to the driver, the addressing should use - * the MLD MAC address. Thus, use the MLD address instead of translating - * the addresses. - */ if (ap_sta_is_mld(hapd, sta)) { - sa = hapd->mld->mld_addr; - ml_resp = hostapd_ml_auth_resp(hapd); if (!ml_resp) return -1; @@ -444,7 +437,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, WLAN_FC_STYPE_AUTH); os_memcpy(reply->da, dst, ETH_ALEN); os_memcpy(reply->sa, sa, ETH_ALEN); - os_memcpy(reply->bssid, bssid, ETH_ALEN); + os_memcpy(reply->bssid, sa, ETH_ALEN); reply->u.auth.auth_alg = host_to_le16(auth_alg); reply->u.auth.auth_transaction = host_to_le16(auth_transaction); @@ -3265,14 +3258,9 @@ static void handle_auth(struct hostapd_data *hapd, bssid = mgmt->bssid; #ifdef CONFIG_IEEE80211BE - /* - * Once a non-AP MLD is added to the driver, the addressing should use - * the MLD MAC address. It is the responsibility of the driver to - * handle the translations. - */ if (ap_sta_is_mld(hapd, sta)) { dst = sta->addr; - bssid = hapd->mld->mld_addr; + bssid = hapd->own_addr; } #endif /* CONFIG_IEEE80211BE */ @@ -4823,15 +4811,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); -#ifdef CONFIG_IEEE80211BE - /* - * Once a non-AP MLD is added to the driver, the addressing should use - * MLD MAC address. - */ - if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans) - sa = hapd->mld->mld_addr; -#endif /* CONFIG_IEEE80211BE */ - os_memcpy(reply->da, addr, ETH_ALEN); os_memcpy(reply->sa, sa, ETH_ALEN); os_memcpy(reply->bssid, sa, ETH_ALEN); -- 2.25.1 From benjamin at sipsolutions.net Wed Apr 17 00:06:03 2024 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Wed, 17 Apr 2024 09:06:03 +0200 Subject: [PATCH] wpa_supplicant: MLD STA: find partner links by BSSID and SSID In-Reply-To: <20240409065023.30664-1-michael-cy.lee@mediatek.com> References: <20240409065023.30664-1-michael-cy.lee@mediatek.com> Message-ID: Hi, On Tue, 2024-04-09 at 14:50 +0800, Michael-CY Lee wrote: > MLD STA finds MLD AP's partner links by BSSID from the scan results. > However, if the scan results contain BSSs with same BSSID but different > BSS information, the MLD STA might assign a wrong BSS to one of the MLD > AP's partner links. As a clarification, why/how do we get into the situation that the same BSSIDs exists with the wrong SSID in the supplicant? Did the AP change its SSID and we still have the old one in the cache? If that is the case, then maybe what we should be doing is to verify that the BSS parameter change count matches. If not, it makes sense to reject the BSS for now, which should trigger an ML probe request to update the information and the next attempt will work fine. Benjamin > This patch avoids the problem by using both BSSID and SSID to find the > MLD AP's partner links. > > Signed-off-by: Michael-CY Lee > --- > ?wpa_supplicant/bss.c |? 9 +++++++-- > ?wpa_supplicant/sme.c | 14 ++++++++------ > ?2 files changed, 15 insertions(+), 8 deletions(-) > > diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c > index 289035310..4b8a98adf 100644 > --- a/wpa_supplicant/bss.c > +++ b/wpa_supplicant/bss.c > @@ -1529,8 +1529,13 @@ wpa_bss_parse_ml_rnr_ap_info(struct > wpa_supplicant *wpa_s, > ? wpa_printf(MSG_DEBUG, > ? ?? "MLD: Reported link not part of > MLD"); > ? } else if (!(BIT(link_id) & *seen)) { > - struct wpa_bss *neigh_bss = > - wpa_bss_get_bssid(wpa_s, pos + 1); > + struct wpa_bss *neigh_bss; > + > + if (ssid) > + neigh_bss = wpa_bss_get(wpa_s, pos + > 1, ssid->ssid, > + ssid- > >ssid_len); > + else > + neigh_bss = wpa_bss_get_bssid(wpa_s, > pos + 1); > ? > ? *seen |= BIT(link_id); > ? wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link > ID=%u", > diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c > index f08184f98..0643774a5 100644 > --- a/wpa_supplicant/sme.c > +++ b/wpa_supplicant/sme.c > @@ -390,7 +390,8 @@ static void wpas_ml_handle_removed_links(struct > wpa_supplicant *wpa_s, > ? > ?#ifdef CONFIG_TESTING_OPTIONS > ?static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant > *wpa_s, > - ???? struct wpa_bss *bss) > + ???? struct wpa_bss *bss, > + ???? struct wpa_ssid *ssid) > ?{ > ? unsigned int low, high, i; > ? > @@ -456,7 +457,7 @@ found: > ? ?? MAC2STR(wpa_s->links[i].bssid)); > ? > ? /* Get the BSS entry and do the switch */ > - bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid); > + bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid, > ssid->ssid_len); > ? wpa_s->mlo_assoc_link_id = i; > ? > ? return bss; > @@ -515,7 +516,7 @@ out: > ? > ? > ?static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s, > - ?? struct wpa_bss *bss) > + ?? struct wpa_bss *bss, struct > wpa_ssid *ssid) > ?{ > ? u8 i; > ? > @@ -533,7 +534,8 @@ static void wpas_sme_set_mlo_links(struct > wpa_supplicant *wpa_s, > ? if (bss->mld_link_id == i) > ? wpa_s->links[i].bss = bss; > ? else > - wpa_s->links[i].bss = > wpa_bss_get_bssid(wpa_s, bssid); > + wpa_s->links[i].bss = wpa_bss_get(wpa_s, > bssid, ssid->ssid, > + ? ssid- > >ssid_len); > ? } > ?} > ? > @@ -576,10 +578,10 @@ static void sme_send_authentication(struct > wpa_supplicant *wpa_s, > ? ??? NULL, ssid, NULL) && > ? ??? bss->valid_links) { > ? wpa_printf(MSG_DEBUG, "MLD: In authentication"); > - wpas_sme_set_mlo_links(wpa_s, bss); > + wpas_sme_set_mlo_links(wpa_s, bss, ssid); > ? > ?#ifdef CONFIG_TESTING_OPTIONS > - bss = wpas_ml_connect_pref(wpa_s, bss); > + bss = wpas_ml_connect_pref(wpa_s, bss, ssid); > ? > ? if (wpa_s->conf->mld_force_single_link) { > ? wpa_printf(MSG_DEBUG, "MLD: Force single > link"); From chaitanya.mgit at gmail.com Wed Apr 17 00:41:45 2024 From: chaitanya.mgit at gmail.com (Krishna Chaitanya) Date: Wed, 17 Apr 2024 13:11:45 +0530 Subject: [PATCH v4 1/4] hostapd: afcd: add AFC daemon support In-Reply-To: References: <636f5b4c075621220e9dd8785a58d233701b3eb9.1712755468.git.lorenzo@kernel.org> Message-ID: On Tue, Apr 16, 2024 at 8:52?PM Lorenzo Bianconi wrote: > > > On Wed, Apr 10, 2024 at 7:05?PM Lorenzo Bianconi wrote: > > > + "requestId":"11235814", > > 11235813 to match the default request ID > > > + > > > +static void usage(void) > > > +{ > > > + wpa_printf(MSG_ERROR, > > > + "%s:\n" > > > + "afcd -u [-p][-t][-D][-P][-dB]", > > > + __func__); > > > +} > > missing `-d` usage > > what do you mean? a nit, just missing "-d" in the usage text. From andrei.otcheretianski at intel.com Wed Apr 17 05:28:12 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Wed, 17 Apr 2024 15:28:12 +0300 Subject: [PATCH] DBUS: Use correct values for persistent group Message-ID: <20240417122812.2015822-1-andrei.otcheretianski@intel.com> DBUS lib expects "persistent" to be bool (0/1) and crashes otherwise. Since persistent may also be 2 convert it to boolean. Signed-off-by: Andrei Otcheretianski Signed-off-by: Benjamin Berg --- wpa_supplicant/dbus/dbus_new.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 8bd6a9a43b..02107dc475 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -1663,7 +1663,7 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, wpa_s->dbus_new_path) || !wpa_dbus_dict_append_string(&dict_iter, "role", client ? "client" : "GO") || - !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) || + !wpa_dbus_dict_append_bool(&dict_iter, "persistent", !!persistent) || !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || (ip && -- 2.43.0 From andrei.otcheretianski at intel.com Wed Apr 17 05:45:24 2024 From: andrei.otcheretianski at intel.com (Andrei Otcheretianski) Date: Wed, 17 Apr 2024 15:45:24 +0300 Subject: [PATCH] p2p: call normal SD query callback on RX/TX race Message-ID: <20240417124524.2027615-1-andrei.otcheretianski@intel.com> From: Benjamin Berg If the TX success response races with the RX frame then the state machine was simply move to P2P_SD_DURING_FIND to continue the operation. However, this does not take into account broadcast queries where the callback handler updates the peer's sd_pending_bcast_queries. Fix this by exporting the callback and calling it directly. This is fine, as the operation is cancelled immediately afterwards, ensuring that the callback is not called a second time. Signed-off-by: Benjamin Berg --- src/p2p/p2p.c | 4 ++-- src/p2p/p2p_i.h | 3 +++ src/p2p/p2p_sd.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index bb1938dc31..8e0fc35dfb 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -3332,7 +3332,7 @@ skip_sd: } -static void p2p_sd_cb(struct p2p_data *p2p, int success) +void p2p_sd_query_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); @@ -3835,7 +3835,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_neg_conf_cb(p2p, result); break; case P2P_PENDING_SD: - p2p_sd_cb(p2p, success); + p2p_sd_query_cb(p2p, success); break; case P2P_PENDING_PD: p2p_prov_disc_cb(p2p, success); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 2612be5ad3..0a487e0316 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -899,6 +899,9 @@ void p2p_pref_channel_filter(const struct p2p_channels *a, const struct weighted_pcl *freq_list, unsigned int num_channels, struct p2p_channels *res, bool go); + +void p2p_sd_query_cb(struct p2p_data *p2p, int success); + void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index d6882e4a32..5537a6eaaf 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -502,7 +502,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, * received. */ p2p_dbg(p2p, "GAS Initial Request had not yet received TX status - process the response anyway"); - p2p_set_state(p2p, P2P_SD_DURING_FIND); + p2p_sd_query_cb(p2p, 1); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); -- 2.43.0 From lorenzo at kernel.org Tue Apr 16 08:46:48 2024 From: lorenzo at kernel.org (Lorenzo Bianconi) Date: Tue, 16 Apr 2024 17:46:48 +0200 Subject: [PATCH v4 3/4] hostapd: ap: add AFC client support In-Reply-To: References: Message-ID: [...] > > + > > +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 > > +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ > > +#define HOSTAPD_AFC_BUFSIZE 4096 > With the afc-reply.json, the response is 4842 bytes, so, this needs to > be increased. ack, I will fix it. > > + > > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); > > + > > + > > +static struct json_object * > > +hostapd_afc_build_location_request(struct hostapd_iface *iface) > > +{ > > + struct json_object *location_obj, *center_obj, *ellipse_obj; > > + struct json_object *elevation_obj, *str_obj; > > + struct hostapd_config *iconf = iface->conf; > > + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); > > + > > + location_obj = json_object_new_object(); > > + if (!location_obj) > > + return NULL; > > + > > + if (iconf->afc.location.type != LINEAR_POLYGON) { > > + struct afc_linear_polygon *lp = > > + &iconf->afc.location.linear_polygon_data[0]; > > + > > + ellipse_obj = json_object_new_object(); > > + if (!ellipse_obj) > > + goto error; > > + > > + center_obj = json_object_new_object(); > > + if (!center_obj) > > + goto error; > > + > > + json_object_object_add(ellipse_obj, "center", center_obj); > > + > > + str_obj = json_object_new_double(lp->longitude); > If the config file doesn't have the entries, then these pointers NULL > and hostapd > crashes. ack, I will fix it. > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(center_obj, "longitude", str_obj); > > + str_obj = json_object_new_double(lp->latitude); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(center_obj, "latitude", str_obj); > > + } > > + > > + switch (iconf->afc.location.type) { > > + case LINEAR_POLYGON: { > > + struct json_object *outer_boundary_obj; > > + int i; > > + > > + outer_boundary_obj = json_object_new_object(); > > + if (!outer_boundary_obj) > > + goto error; > > + > > + json_object_object_add(location_obj, "linearPolygon", > > + outer_boundary_obj); > > + ellipse_obj = json_object_new_array(); > > + if (!ellipse_obj) > > + goto error; > > + > > + json_object_object_add(outer_boundary_obj, "outerBoundary", > > + ellipse_obj); > > + for (i = 0; > > + i < iconf->afc.location.n_linear_polygon_data; i++) { > > + struct afc_linear_polygon *lp = > > + &iconf->afc.location.linear_polygon_data[i]; > > + center_obj = json_object_new_object(); > > + if (!center_obj) > > + goto error; > > + > > + json_object_array_add(ellipse_obj, center_obj); > > + str_obj = json_object_new_double(lp->longitude); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(center_obj, "longitude", > > + str_obj); > > + str_obj = json_object_new_double(lp->latitude); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(center_obj, "latitude", > > + str_obj); > > + } > > + break; > > + } > > + case RADIAL_POLYGON: { > > + struct json_object *outer_boundary_obj; > > + int i; > > + > > + json_object_object_add(location_obj, "radialPolygon", > > + ellipse_obj); > > + > > + outer_boundary_obj = json_object_new_array(); > > + if (!outer_boundary_obj) > > + goto error; > > + > > + json_object_object_add(ellipse_obj, "outerBoundary", > > + outer_boundary_obj); > > + for (i = 0; > > + i < iconf->afc.location.n_radial_polygon_data; i++) { > > + struct afc_radial_polygon *rp = > > + &iconf->afc.location.radial_polygon_data[i]; > > + struct json_object *angle_obj; > > + > > + angle_obj = json_object_new_object(); > > + if (!angle_obj) > > + goto error; > > + > > + json_object_array_add(outer_boundary_obj, angle_obj); > > + > > + str_obj = json_object_new_double(rp->angle); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(angle_obj, "angle", str_obj); > > + str_obj = json_object_new_double(rp->length); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(angle_obj, "length", str_obj); > > + } > > + break; > > + } > > + case ELLIPSE: > > + default: > > + json_object_object_add(location_obj, "ellipse", ellipse_obj); > > + > > + str_obj = json_object_new_int(iconf->afc.location.major_axis); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(ellipse_obj, "majorAxis", str_obj); > > + str_obj = json_object_new_int(iconf->afc.location.minor_axis); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(ellipse_obj, "minorAxis", str_obj); > > + str_obj = json_object_new_int(iconf->afc.location.orientation); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(ellipse_obj, "orientation", str_obj); > > + break; > > + } > > + > > + elevation_obj = json_object_new_object(); > > + if (!elevation_obj) > > + goto error; > > + > > + json_object_object_add(location_obj, "elevation", > > + elevation_obj); > > + str_obj = json_object_new_double(iconf->afc.location.height); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(elevation_obj, "height", str_obj); > > + str_obj = json_object_new_string(iconf->afc.location.height_type); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(elevation_obj, "heightType", str_obj); > > + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(elevation_obj, "verticalUncertainty", > > + str_obj); > > + str_obj = json_object_new_int(is_ap_indoor); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(location_obj, "indoorDeployment", str_obj); > > + > > + return location_obj; > > + > > +error: > > + json_object_put(location_obj); > > + return NULL; > > +} > > + > > + > > +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class) > > +{ > > + struct json_object *chan_list_obj, *str_obj; > > + const struct oper_class_map *oper_class; > > + int chan_offset, chan; > > + > > + oper_class = get_oper_class(NULL, op_class); > > + if (!oper_class) > > + return NULL; > > + > > + chan_list_obj = json_object_new_array(); > > + if (!chan_list_obj) > > + return NULL; > > + > > + switch (op_class) { > > + case 132: /* 40MHz */ > > + chan_offset = 2; > > + break; > > + case 133: /* 80MHz */ > > + chan_offset = 6; > > + break; > > + case 134: /* 160MHz */ > > + chan_offset = 14; > > + break; > > + default: > > + chan_offset = 0; > > + break; > > + } > > + > > + for (chan = oper_class->min_chan; chan <= oper_class->max_chan; > > + chan += oper_class->inc) { > > + str_obj = json_object_new_int(chan + chan_offset); > > + if (!str_obj) { > > + json_object_put(chan_list_obj); > > + return NULL; > > + } > > + json_object_array_add(chan_list_obj, str_obj); > > + } > > + > > + return chan_list_obj; > > +} > > + > > + > > +static struct json_object * > > +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface) > > +{ > > + struct json_object *op_class_list_obj, *str_obj; > > + struct hostapd_config *iconf = iface->conf; > > + int i; > > + > > + op_class_list_obj = json_object_new_array(); > > + if (!op_class_list_obj) > > + return NULL; > > + > > + for (i = 0; i < iconf->afc.n_op_class; i++) { > > + struct json_object *op_class_obj, *chan_list_obj; > > + u8 op_class = iconf->afc.op_class[i]; > > + > > + if (!is_6ghz_op_class(op_class)) > > + continue; > > + > > + op_class_obj = json_object_new_object(); > > + if (!op_class_obj) > > + goto error; > > + > > + json_object_array_add(op_class_list_obj, op_class_obj); > > + str_obj = json_object_new_int(op_class); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(op_class_obj, "globalOperatingClass", > > + str_obj); > > + > > + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class); > > + if (!chan_list_obj) > > + goto error; > > + > > + json_object_object_add(op_class_obj, "channelCfi", > > + chan_list_obj); > > + } > > + > > + return op_class_list_obj; > > + > > +error: > > + json_object_put(op_class_list_obj); > > + return NULL; > > +} > > + > > + > > +static struct json_object * > > +hostapd_afc_build_request(struct hostapd_iface *iface) > > +{ > > + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj; > > + struct json_object *s2_obj, *str_obj, *location_obj; > > + struct hostapd_config *iconf = iface->conf; > > + struct json_object *op_class_list_obj; > > + int i; > > + > > + l1_obj = json_object_new_object(); > > + if (!l1_obj) > > + return NULL; > > + > > + if (iconf->afc.request.version) { > > + str_obj = json_object_new_string(iconf->afc.request.version); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(l1_obj, "version", str_obj); > > + } > > + > > + la1_obj = json_object_new_array(); > > + if (!la1_obj) > > + goto error; > > + > > + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests", > > + la1_obj); > > + l2_obj = json_object_new_object(); > > + if (!l2_obj) > > + goto error; > > + > > + json_object_array_add(la1_obj, l2_obj); > > + if (iconf->afc.request.id) { > > + str_obj = json_object_new_string(iconf->afc.request.id); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "requestId", str_obj); > > + } > > + > > + s2_obj = json_object_new_object(); > > + if (!s2_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj); > > + if (iconf->afc.request.sn) { > > + str_obj = json_object_new_string(iconf->afc.request.sn); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(s2_obj, "serialNumber", str_obj); > > + } > > + > > + la2_obj = json_object_new_array(); > > + if (!la2_obj) > > + goto error; > > + > > + json_object_object_add(s2_obj, "certificationId", la2_obj); > > + for (i = 0; i < iconf->afc.n_cert_ids; i++) { > > + struct json_object *obj; > > + > > + obj = json_object_new_object(); > > + if (!obj) > > + goto error; > > + > > + json_object_array_add(la2_obj, obj); > > + str_obj = > > + json_object_new_string(iconf->afc.cert_ids[i].rulset); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(obj, "rulesetId", str_obj); > > + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(obj, "id", str_obj); > > + } > > + > > + location_obj = hostapd_afc_build_location_request(iface); > > + if (!location_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "location", location_obj); > > + str_obj = json_object_new_int(iconf->afc.min_power); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "minDesiredPower", str_obj); > > + > > + if (iconf->afc.n_freq_range) { > > + struct json_object *freq_obj; > > + > > + freq_obj = json_object_new_array(); > > + if (!freq_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "inquiredFrequencyRange", > > + freq_obj); > > + for (i = 0; i < iconf->afc.n_freq_range; i++) { > > + struct afc_freq_range *fr = &iconf->afc.freq_range[i]; > > + struct json_object *obj; > > + > > + obj = json_object_new_object(); > > + if (!obj) > > + goto error; > > + > > + json_object_array_add(freq_obj, obj); > > + str_obj = json_object_new_int(fr->low_freq); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(obj, "lowFrequency", str_obj); > > + str_obj = json_object_new_int(fr->high_freq); > > + if (!str_obj) > > + goto error; > > + > > + json_object_object_add(obj, "highFrequency", str_obj); > > + } > > + } > > + > > + op_class_list_obj = hostapd_afc_build_req_chan_list(iface); > > + if (!op_class_list_obj) > > + goto error; > > + > > + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj); > > + > > + wpa_printf(MSG_DEBUG, "Pending AFC request: %s", > > + json_object_get_string(l1_obj)); > > + > > + return l1_obj; > > + > > +error: > > + json_object_put(l1_obj); > > + > > + return NULL; > > +} > > + > > + > > +static int > > +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, > > + struct json_object *reply_elem_obj) > > +{ > > + struct afc_freq_range_elem *f = NULL; > > + struct json_object *obj; > > + int i, count = 0; > > + > > + if (!json_object_object_get_ex(reply_elem_obj, > > + "availableFrequencyInfo", &obj)) > > + return 0; > > + > > + for (i = 0; i < json_object_array_length(obj); i++) { > > + struct json_object *range_elem_obj, *freq_range_obj; > > + struct json_object *high_freq_obj, *low_freq_obj; > > + struct json_object *max_psd_obj; > > + > > + range_elem_obj = json_object_array_get_idx(obj, i); > > + if (!range_elem_obj) > > + continue; > > + > > + if (!json_object_object_get_ex(range_elem_obj, > > + "frequencyRange", > > + &freq_range_obj)) > > + continue; > > + > > + if (!json_object_object_get_ex(freq_range_obj, > > + "lowFrequency", > > + &low_freq_obj)) > > + continue; > > + > > + if (!json_object_object_get_ex(freq_range_obj, > > + "highFrequency", > > + &high_freq_obj)) > > + continue; > > + > > + if (!json_object_object_get_ex(range_elem_obj, "maxPsd", > > + &max_psd_obj) && > > + !json_object_object_get_ex(range_elem_obj, "maxPSD", > > + &max_psd_obj)) > > + continue; > > + > > + f = os_realloc_array(f, count + 1, sizeof(*f)); > > + if (!f) > > + return -ENOMEM; > > + > > + f[count].low_freq = json_object_get_int(low_freq_obj); > > + f[count].high_freq = json_object_get_int(high_freq_obj); > > + f[count++].max_psd = json_object_get_int(max_psd_obj); > > + } > > + iface->afc.freq_range = f; > > + iface->afc.num_freq_range = count; > > + > > + return 0; > > +} > > + > > + > > +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list, > > + int *chan_list_size, u8 op_class, > > + int center_chan, int power) > > +{ > > + int num_low_subchan, ch, count = *chan_list_size; > > + struct afc_chan_info_elem *c = *chan_list; > > + > > + switch (op_class) { > > + case 132: /* 40MHz */ > > + num_low_subchan = 2; > > + break; > > + case 133: /* 80MHz */ > > + num_low_subchan = 6; > > + break; > > + case 134: /* 160MHz */ > > + num_low_subchan = 14; > > + break; > > + default: > > + num_low_subchan = 0; > > + break; > > + } > > + > > + for (ch = center_chan - num_low_subchan; > > + ch <= center_chan + num_low_subchan; ch += 4) { > > + int i; > > + > > + for (i = 0; i < count; i++) { > > + if (c[i].chan == ch) > > + break; > > + } > > + > > + if (i == count) { > > + c = os_realloc_array(c, count + 1, sizeof(*c)); > > + if (!c) > > + return -ENOMEM; > > + > > + c[count].chan = ch; > > + c[count++].power = power; > > + } > > + } > > + > > + *chan_list_size = count; > > + *chan_list = c; > > + > > + return 0; > > +} > > + > > + > > +static int > > +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, > > + struct json_object *reply_elem_obj) > > +{ > > + struct afc_chan_info_elem *c = NULL; > > + struct json_object *obj; > > + int i, count = 0; > > + > > + if (!json_object_object_get_ex(reply_elem_obj, > > + "availableChannelInfo", &obj)) > > + return 0; > > + > > + for (i = 0; i < json_object_array_length(obj); i++) { > > + struct json_object *range_elem_obj, *op_class_obj; > > + struct json_object *chan_cfi_obj, *max_eirp_obj; > > + int ch, op_class; > > + > > + range_elem_obj = json_object_array_get_idx(obj, i); > > + if (!range_elem_obj) > > + continue; > > + > > + if (!json_object_object_get_ex(range_elem_obj, > > + "globalOperatingClass", > > + &op_class_obj)) > > + continue; > > + > > + if (!json_object_object_get_ex(range_elem_obj, "maxEirp", > > + &max_eirp_obj)) > > + continue; > > + > > + if (!json_object_object_get_ex(range_elem_obj, "channelCfi", > > + &chan_cfi_obj)) > > + continue; > > + > > + op_class = json_object_get_int(op_class_obj); > > + for (ch = 0; > > + ch < json_object_array_length(chan_cfi_obj); ch++) { > > + struct json_object *pwr_obj; > > + struct json_object *ch_obj; > > + int channel, power; > > + > > + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch); > > + if (!ch_obj) > > + continue; > > + > > + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch); > > + if (!pwr_obj) > > + continue; > > + > > + channel = json_object_get_int(ch_obj); > > + power = json_object_get_int(pwr_obj); > > + > > + hostad_afc_update_chan_info(&c, &count, op_class, > > + channel, power); > > + } > > + iface->afc.chan_info_list = c; > > + iface->afc.num_chan_info = count; > > + } > > + > > + return 0; > > +} > > + > > + > > +static int hostad_afc_get_timeout(struct json_object *obj) > > +{ > > + time_t t, now; > > + struct tm tm; > > + > > + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ", > > + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, > > + &tm.tm_min, &tm.tm_sec) <= 0) > > + return HOSTAPD_AFC_TIMEOUT; > > + > > + tm.tm_year -= 1900; > > + tm.tm_mon -= 1; > > + tm.tm_isdst = -1; > > + t = mktime(&tm); > > + time(&now); > > + > > + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100; > > +} > > + > > + > > +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) > > +{ > > + struct json_object *payload_obj, *reply_obj, *version_obj; > > + struct hostapd_config *iconf = iface->conf; > > + int i, request_timeout = -1, ret = -EINVAL; > > + > > + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply); > > + payload_obj = json_tokener_parse(reply); > > + if (!payload_obj) > > + return -EINVAL; > This function could use some error logs, esp. as its related to parsing. ack, I will fix it. > > + > > + if (!json_object_object_get_ex(payload_obj, "version", &version_obj)) > > + return -EINVAL; > > + > > + if (iconf->afc.request.version && > > + os_strcmp(iconf->afc.request.version, > > + json_object_get_string(version_obj))) > > + return -EINVAL; > > + > > + if (!json_object_object_get_ex(payload_obj, > > + "availableSpectrumInquiryResponses", > > + &reply_obj)) > > + return -EINVAL; > > + > > + for (i = 0; i < json_object_array_length(reply_obj); i++) { > > + struct json_object *reply_elem_obj, *obj, *status_obj; > > + int j, status = -EINVAL; > > + > > + reply_elem_obj = json_object_array_get_idx(reply_obj, i); > > + if (!reply_elem_obj) > > + continue; > > + > > + if (!json_object_object_get_ex(reply_elem_obj, "requestId", > > + &obj)) > > + continue; > > + > > + if (iconf->afc.request.id && > > + os_strcmp(iconf->afc.request.id, > > + json_object_get_string(obj))) > > + continue; > > + > > + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId", > > + &obj)) > > + continue; > > + > > + for (j = 0; j < iconf->afc.n_cert_ids; j++) { > > + if (!os_strcmp(iconf->afc.cert_ids[j].rulset, > > + json_object_get_string(obj))) > > + break; > > + } > > + > > + if (j == iconf->afc.n_cert_ids) > > + continue; > > + > > + if (!json_object_object_get_ex(reply_elem_obj, "response", > > + &obj)) > > + continue; > > + > > + if (json_object_object_get_ex(obj, "shortDescription", > > + &status_obj)) > > + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s", > > + i, json_object_get_string(status_obj)); > > + > > + if (json_object_object_get_ex(obj, "responseCode", > > + &status_obj)) > > + status = json_object_get_int(status_obj); > > + > > + if (status < 0) > > + continue; > > + > > + if (hostad_afc_parse_available_freq_info(iface, > > + reply_elem_obj) || > > + hostad_afc_parse_available_chan_info(iface, > > + reply_elem_obj)) > > + continue; > > + > > + if (json_object_object_get_ex(reply_elem_obj, > > + "availabilityExpireTime", > > + &obj)) { > > + int timeout = hostad_afc_get_timeout(obj); > > + > > + if (request_timeout < 0 || timeout < request_timeout) > > + request_timeout = timeout; > > + } > > + > > + ret = status; > > + } > > + > > + iface->afc.data_valid = true; > > + iface->afc.timeout = request_timeout; > > + if (iface->afc.timeout < 0) > > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; > > + > > + return ret; > > +} > > + > > + > > +static int hostapd_afc_send_receive(struct hostapd_iface *iface) > > +{ > > + struct hostapd_config *iconf = iface->conf; > > + json_object *request_obj = NULL; > > + struct timeval sock_timeout = { > > + .tv_sec = 5, > > + }; > > + struct sockaddr_un addr = { > > + .sun_family = AF_UNIX, > > +#ifdef __FreeBSD__ > > + .sun_len = sizeof(addr), > > +#endif /* __FreeBSD__ */ > > + }; > > + char buf[HOSTAPD_AFC_BUFSIZE] = {}; > > + const char *request; > > + int sockfd, ret; > > + fd_set read_set; > > + > > + if (iface->afc.data_valid) { > > + /* AFC data already downloaded from the server */ > > + return 0; > > + } > > + > > + if (!iconf->afc.socket) { > > + wpa_printf(MSG_ERROR, "Missing AFC socket string"); > > + return -EINVAL; > > + } > > + > > + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; > > + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { > > + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", > > + iconf->afc.socket); > > + return -EINVAL; > > + } > > + > > + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); > > + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); > > + if (sockfd < 0) { > > + wpa_printf(MSG_ERROR, "Failed creating AFC socket"); > > + return sockfd; > > + } > > + > > + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { > > + wpa_printf(MSG_ERROR, "Failed connecting AFC socket"); > > + ret = -EIO; > > + goto close_sock; > > + } > > + > > + request_obj = hostapd_afc_build_request(iface); > > + if (!request_obj) { > > + ret = -ENOMEM; > > + goto close_sock; > > + } > > + > > + request = json_object_to_json_string(request_obj); > > + if (send(sockfd, request, strlen(request), 0) < 0) { > > + wpa_printf(MSG_ERROR, "Failed sending AFC request"); > > + ret = -EIO; > > + goto close_sock; > > + } > > + > > + FD_ZERO(&read_set); > > + FD_SET(sockfd, &read_set); > > + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { > > + wpa_printf(MSG_ERROR, "Select failed on AFC socket"); > > + ret = -errno; > > + goto close_sock; > > + } > > + > > + if (!FD_ISSET(sockfd, &read_set)) { > > + ret = -EIO; > > + goto close_sock; > > + } > > + > > + ret = recv(sockfd, buf, sizeof(buf) - 1, 0); > > + if (ret <= 0) > > + goto close_sock; > > + > > + ret = hostapd_afc_parse_reply(iface, buf); > > +close_sock: > > + json_object_put(request_obj); > > + close(sockfd); > > + > > + return ret; > > +} > > + > > + > > +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) > > +{ > > + const struct oper_class_map *oper_class; > > + int ch; > > + > > + oper_class = get_oper_class(NULL, iface->conf->op_class); > > + if (!oper_class) > > + return false; > > + > > + for (ch = oper_class->min_chan; ch <= oper_class->max_chan; > > + ch += oper_class->inc) { > > + struct hostapd_hw_modes *mode = iface->current_mode; > > + int i; > > + > > + for (i = 0; i < mode->num_channels; i++) { > > + struct hostapd_channel_data *chan = &mode->channels[i]; > > + > > + if (chan->chan == ch && > > + !(chan->flag & HOSTAPD_CHAN_DISABLED)) > > + return true; > > + } > > + } > > + > > + return false; > > +} > > + > > + > > +int hostapd_afc_handle_request(struct hostapd_iface *iface) > > +{ > > + struct hostapd_config *iconf = iface->conf; > > + int ret; > > + > > + /* AFC is required just for standard power AP */ > > + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) > > + return 1; > > + > > + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) > > + return 1; > > + > > + if (iface->state == HAPD_IFACE_ACS) > > + return 1; > > + > > + ret = hostapd_afc_send_receive(iface); > > + if (ret < 0) { > > + /* > > + * If the connection to the AFCD failed, resched for a > > + * future attempt. > > + */ > > + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret); > > + if (ret == -EIO) > > + ret = 0; > > + goto resched; > > + } > > + > > + hostap_afc_disable_channels(iface); > > + if (!hostapd_afc_has_usable_chans(iface)) > > + goto resched; > > + > > + /* Trigger an ACS freq scan */ > > + iconf->channel = 0; > > + iface->freq = 0; > > + > > + if (acs_init(iface) != HOSTAPD_CHAN_ACS) { > > + wpa_printf(MSG_ERROR, "Could not start ACS"); > > + ret = -EINVAL; > > + } > > + > > +resched: > > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); > > + eloop_register_timeout(iface->afc.timeout, 0, > > + hostapd_afc_timeout_handler, iface, NULL); > > + > > + return ret; > > +} > > + > > + > > +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) > > +{ > > + os_free(iface->afc.chan_info_list); > > + os_free(iface->afc.freq_range); > > + > > + iface->afc.num_freq_range = 0; > > + iface->afc.num_chan_info = 0; > > + > > + iface->afc.chan_info_list = NULL; > > + iface->afc.freq_range = NULL; > > + > > + iface->afc.data_valid = false; > > +} > > + > > + > > +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) > > +{ > > + struct hostapd_iface *iface = eloop_ctx; > > + bool restart_iface = true; > > + > > + hostapd_afc_delete_data_from_server(iface); > > + if (iface->state != HAPD_IFACE_ENABLED) { > > + /* Hostapd is not fully enabled yet, toggle the interface */ > > + goto restart_interface; > > + } > > + > > + if (hostapd_afc_send_receive(iface) < 0 || > > + hostapd_get_hw_features(iface)) { > > + restart_iface = false; > > + goto restart_interface; > > + } > > + > > + if (hostapd_is_usable_chans(iface)) > > + goto resched; > > + > > + restart_iface = hostapd_afc_has_usable_chans(iface); > > + if (restart_iface) { > > + /* Trigger an ACS freq scan */ > > + iface->conf->channel = 0; > > + iface->freq = 0; > > + } > > + > > +restart_interface: > > + hostapd_disable_iface(iface); > > + if (restart_iface) > > + hostapd_enable_iface(iface); > > +resched: > > + eloop_register_timeout(iface->afc.timeout, 0, > > + hostapd_afc_timeout_handler, iface, NULL); > > +} > > + > > + > > +void hostapd_afc_stop(struct hostapd_iface *iface) > > +{ > > + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); > > +} > > + > > + > > +void hostap_afc_disable_channels(struct hostapd_iface *iface) > > +{ > > + struct hostapd_hw_modes *mode; > > + int i; > > + > > + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A) > > + return; > > + > > + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type)) > > + return; > > + > > + if (!iface->afc.data_valid) > > + return; > > + > > + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A]; > > + for (i = 0; i < mode->num_channels; i++) { > > + struct hostapd_channel_data *chan = &mode->channels[i]; > > + int j; > > + > > + if (!is_6ghz_freq(chan->freq)) > > + continue; > > + > > + for (j = 0; j < iface->afc.num_freq_range; j++) { > > + if (chan->freq >= iface->afc.freq_range[j].low_freq && > > + chan->freq <= iface->afc.freq_range[j].high_freq) > > + break; > > + } > > + > > + if (j != iface->afc.num_freq_range) > > + continue; > > + > > + for (j = 0; j < iface->afc.num_chan_info; j++) { > > + if (chan->chan == iface->afc.chan_info_list[j].chan) > > + break; > > + } > > + > > + if (j != iface->afc.num_chan_info) > > + continue; > > + > > + chan->flag |= HOSTAPD_CHAN_DISABLED; > Please add a debug print with channel info. ack, I will fix it. Regards, Lorenzo > > + } > > +} > > diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c > > index 1a18df617..ca67aeb41 100644 > > --- a/src/ap/ap_config.c > > +++ b/src/ap/ap_config.c > > @@ -1035,6 +1035,22 @@ void hostapd_config_free(struct hostapd_config *conf) > > #endif /* CONFIG_ACS */ > > wpabuf_free(conf->lci); > > wpabuf_free(conf->civic); > > +#ifdef CONFIG_AFC > > + os_free(conf->afc.socket); > > + os_free(conf->afc.request.version); > > + os_free(conf->afc.request.id); > > + os_free(conf->afc.request.sn); > > + for (i = 0; i < conf->afc.n_cert_ids; i++) { > > + os_free(conf->afc.cert_ids[i].rulset); > > + os_free(conf->afc.cert_ids[i].id); > > + } > > + os_free(conf->afc.cert_ids); > > + os_free(conf->afc.location.height_type); > > + os_free(conf->afc.location.linear_polygon_data); > > + os_free(conf->afc.location.radial_polygon_data); > > + os_free(conf->afc.freq_range); > > + os_free(conf->afc.op_class); > > +#endif /* CONFIG_AFC */ > > > > os_free(conf); > > } > > diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h > > index 754d55331..2330163c4 100644 > > --- a/src/ap/ap_config.h > > +++ b/src/ap/ap_config.h > > @@ -1225,6 +1225,53 @@ struct hostapd_config { > > MBSSID_ENABLED = 1, > > ENHANCED_MBSSID_ENABLED = 2, > > } mbssid; > > + > > +#ifdef CONFIG_AFC > > + struct { > > + char *socket; > > + struct { > > + char *version; > > + char *id; > > + char *sn; > > + } request; > > + unsigned int n_cert_ids; > > + struct cert_id { > > + char *rulset; > > + char *id; > > + } *cert_ids; > > + struct { > > + enum afc_location_type { > > + ELLIPSE, > > + LINEAR_POLYGON, > > + RADIAL_POLYGON, > > + } type; > > + unsigned int n_linear_polygon_data; > > + struct afc_linear_polygon { > > + double longitude; > > + double latitude; > > + } *linear_polygon_data; > > + unsigned int n_radial_polygon_data; > > + struct afc_radial_polygon { > > + double length; > > + double angle; > > + } *radial_polygon_data; > > + int major_axis; > > + int minor_axis; > > + int orientation; > > + double height; > > + char *height_type; > > + int vertical_tolerance; > > + } location; > > + unsigned int n_freq_range; > > + struct afc_freq_range { > > + int low_freq; > > + int high_freq; > > + } *freq_range; > > + unsigned int n_op_class; > > + unsigned int *op_class; > > + int min_power; > > + } afc; > > +#endif /* CONFIG_AFC */ > > }; > > > > > > diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c > > index f8cb6432d..940a2a0af 100644 > > --- a/src/ap/hostapd.c > > +++ b/src/ap/hostapd.c > > @@ -714,6 +714,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) > > static void hostapd_cleanup_iface(struct hostapd_iface *iface) > > { > > wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); > > + hostapd_afc_stop(iface); > > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); > > eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, > > NULL); > > @@ -2454,6 +2455,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, > > } > > #endif /* CONFIG_MESH */ > > > > +#ifdef CONFIG_IEEE80211AX > > + /* check AFC for 6GHz channels. */ > > + res = hostapd_afc_handle_request(iface); > > + if (res <= 0) { > > + if (res < 0) > > + goto fail; > > + return res; > > + } > > +#endif /* CONFIG_IEEE80211AX */ > > + > > if (!delay_apply_cfg && > > hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, > > hapd->iconf->channel, > > @@ -2852,6 +2863,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) > > > > hostapd_set_state(iface, HAPD_IFACE_DISABLED); > > > > + hostapd_afc_stop(iface); > > eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); > > iface->wait_channel_update = 0; > > iface->is_no_ir = false; > > @@ -2925,6 +2937,10 @@ void hostapd_interface_free(struct hostapd_iface *iface) > > __func__, iface->bss[j]); > > os_free(iface->bss[j]); > > } > > +#ifdef CONFIG_AFC > > + os_free(iface->afc.chan_info_list); > > + os_free(iface->afc.freq_range); > > +#endif > > hostapd_cleanup_iface(iface); > > } > > > > diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h > > index affe4f604..b74dc75f6 100644 > > --- a/src/ap/hostapd.h > > +++ b/src/ap/hostapd.h > > @@ -700,9 +700,54 @@ struct hostapd_iface { > > > > /* Configured freq of interface is NO_IR */ > > bool is_no_ir; > > + > > +#ifdef CONFIG_AFC > > + struct { > > + int timeout; > > + unsigned int num_freq_range; > > + struct afc_freq_range_elem { > > + int low_freq; > > + int high_freq; > > + /** > > + * max eirp power spectral density received from > > + * the AFC coordinator for this band > > + */ > > + int max_psd; > > + } *freq_range; > > + unsigned int num_chan_info; > > + struct afc_chan_info_elem { > > + int chan; > > + /** > > + * max eirp power received from the AFC coordinator > > + * for this channel > > + */ > > + int power; > > + } *chan_info_list; > > + bool data_valid; > > + } afc; > > +#endif /* CONFIG_AFC */ > > }; > > > > /* hostapd.c */ > > +#ifdef CONFIG_AFC > > +int hostapd_afc_handle_request(struct hostapd_iface *iface); > > +void hostapd_afc_stop(struct hostapd_iface *iface); > > +void hostap_afc_disable_channels(struct hostapd_iface *iface); > > +#else > > +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) > > +{ > > + return 1; > > +} > > + > > +static inline void hostapd_afc_stop(struct hostapd_iface *iface) > > +{ > > +} > > + > > +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) > > +{ > > +} > > +#endif /* CONFIG_AFC */ > > + > > int hostapd_for_each_interface(struct hapd_interfaces *interfaces, > > int (*cb)(struct hostapd_iface *iface, > > void *ctx), void *ctx); > > diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c > > index e652d7504..222f3dc05 100644 > > --- a/src/ap/hw_features.c > > +++ b/src/ap/hw_features.c > > @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) > > iface->hw_features = modes; > > iface->num_hw_features = num_modes; > > > > + hostap_afc_disable_channels(iface); > > + > > for (i = 0; i < num_modes; i++) { > > struct hostapd_hw_modes *feature = &modes[i]; > > int dfs_enabled = hapd->iconf->ieee80211h && > > -- -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 228 bytes Desc: not available URL: From zseilnacht at plume.com Wed Apr 17 00:40:33 2024 From: zseilnacht at plume.com (Ziga Seilnacht) Date: Wed, 17 Apr 2024 09:40:33 +0200 Subject: [PATCH 4/7] driver_nl80211: rewrite neigh code to not depend on libnl3-route Message-ID: Felix Fietkau wrote: > [PATCH 4/7] driver_nl80211: rewrite neigh code to not depend on libnl3-route > > Removes an unnecessary dependency and also makes the code smaller > > Signed-off-by: Felix Fietkau > --- Hello, Commit a210fdb1c717b23b0d691b54d9b7be6f7bcf7799 breaks hostapd build configuration with CONFIG_FULL_DYNAMIC_VLAN=y CONFIG_VLAN_NETLINK=y for me. Those two options are also enabled in Debian's hostapd config. The build fails with: /usr/bin/ld: /build-cache/hostap/build/hostapd/src/ap/vlan_util.o: in function `vlan_add': /build-cache/hostap/hostapd/../src/ap/vlan_util.c:59: undefined reference to `rtnl_link_get_kernel' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:66: undefined reference to `rtnl_link_get_ifindex' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:67: undefined reference to `rtnl_link_put' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:70: undefined reference to `rtnl_link_get_kernel' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:81: undefined reference to `rtnl_link_alloc' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:87: undefined reference to `rtnl_link_set_type' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:94: undefined reference to `rtnl_link_set_link' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:95: undefined reference to `rtnl_link_set_name' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:97: undefined reference to `rtnl_link_vlan_set_id' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:104: undefined reference to `rtnl_link_add' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:117: undefined reference to `rtnl_link_put' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:73: undefined reference to `rtnl_link_put' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:117: undefined reference to `rtnl_link_put' /usr/bin/ld: /build-cache/hostap/build/hostapd/src/ap/vlan_util.o: in function `vlan_rem': /build-cache/hostap/hostapd/../src/ap/vlan_util.c:145: undefined reference to `rtnl_link_get_kernel' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:153: undefined reference to `rtnl_link_delete' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:164: undefined reference to `rtnl_link_put' /usr/bin/ld: /build-cache/hostap/hostapd/../src/ap/vlan_util.c:164: undefined reference to `rtnl_link_put' collect2: error: ld returned 1 exit status make: *** [Makefile:1310: hostapd] Error 1 Looks like the makefile changes from that commit might need to be changed? Best regards, Ziga Seilnacht -- This email and any attachments thereto may contain private, confidential, and privileged material for the sole use of the intended recipient. Any review, copying, or distribution of this email (or any attachments thereto) by others is strictly prohibited. If you are not the intended recipient, please contact the sender immediately and permanently delete the original and any copies of this email and any attachments thereto. From j at w1.fi Fri Apr 19 09:13:24 2024 From: j at w1.fi (Jouni Malinen) Date: Fri, 19 Apr 2024 19:13:24 +0300 Subject: [PATCH 4/7] driver_nl80211: rewrite neigh code to not depend on libnl3-route In-Reply-To: References: Message-ID: On Wed, Apr 17, 2024 at 09:40:33AM +0200, Ziga Seilnacht wrote: > Commit a210fdb1c717b23b0d691b54d9b7be6f7bcf7799 breaks hostapd build > configuration with > CONFIG_FULL_DYNAMIC_VLAN=y CONFIG_VLAN_NETLINK=y for me. Those two options are > also enabled in Debian's hostapd config. > > The build fails with: > > /usr/bin/ld: /build-cache/hostap/build/hostapd/src/ap/vlan_util.o: in > function `vlan_add': > /build-cache/hostap/hostapd/../src/ap/vlan_util.c:59: undefined > reference to `rtnl_link_get_kernel' It looks like CONFIG_DRIVER_MACSEC_LINUX=y masked this in my tests with hostapd builds since it is also pulling in libnl3-route. > Looks like the makefile changes from that commit might need to be changed? I reverted the makefile related changes for that commit now to avoid this issue with vlan_util.c. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sat Apr 20 09:06:09 2024 From: j at w1.fi (Jouni Malinen) Date: Sat, 20 Apr 2024 19:06:09 +0300 Subject: [PATCH 00/22] hostapd: add cohosted MLO support In-Reply-To: <20240328181652.2956122-1-quic_adisi@quicinc.com> References: <20240328181652.2956122-1-quic_adisi@quicinc.com> Message-ID: On Thu, Mar 28, 2024 at 11:46:30PM +0530, Aditya Kumar Singh wrote: > Currently MLO is being supported with an assumption of only single BSS per > link in the hostapd conf file. This needs to be extended when cohosted ML > BSS exist in the same config file. > > Extend the support for cohosted BSSes along with certain improvements in > wpa state machine, rekeying areas. I applied patches 5-20 with cleanup. > tests: MLO: add basic cohosted MLDs functionality testing > tests: MLO: add cohosted MLDs connectivity testing Those are still waiting for kernel changes: > Only the two newly added test cases in [21/22] and [22/22] depends on > the following - > [1] wifi: mac80211: handle link ID during management Tx > https://lore.kernel.org/all/20240326045242.3190611-1-quic_adisi at quicinc.com/ -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:01:31 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:01:31 +0300 Subject: [PATCH 4/7] SME: Explicitly deauthenticate on authentication/association timeout In-Reply-To: <20240408130701.531194-4-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> <20240408130701.531194-4-andrei.otcheretianski@intel.com> Message-ID: On Mon, Apr 08, 2024 at 04:06:58PM +0300, Andrei Otcheretianski wrote: > This would clear the local driver state and also the AP state, and > would allow clean connection establishment next time. > > This is needed in cases where the AP sends the association response > frame and sets the station state to associated, but the underlying > driver, e.g., mac80211, fails to parse the association response and > drops it, eventually clearing the association flow and sending a > timeout event to user space. This seems to break the hwsim test case eht_mld_sae_two_links_disable_enable in my test setup. The RECONNECT command after having restarted the AP MLD seems to result in the Authentication frames not being received (no ACK) by the AP MLD. Those frames seem to be being sent to the old link addressed of the AP MLD and not the ones that were assigned when restarting it. I'm not completely sure what is causing the issue and why exactly this change triggers this. In any case, I cannot apply this as-is without getting whatever else might be behind this addressed. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:06:40 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:06:40 +0300 Subject: [PATCH 1/7] tests: Wait after removing a BSS In-Reply-To: <20240408130701.531194-1-andrei.otcheretianski@intel.com> References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> Message-ID: Thanks, patches 1-3 and 5-7 applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:07:46 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:07:46 +0300 Subject: [PATCH] Allow Session-Timeout with PSK RADIUS during 4 Way Handshake In-Reply-To: References: Message-ID: On Tue, Apr 09, 2024 at 03:06:38PM -0700, Lee Harding wrote: > I've been reading the code and testing the PSK via RADIUS > functionality in hostap and discovered what I believe to be a bug. > When the RADIUS response includes a Session-Timeout and is otherwise > valid (an Access-Accept with a valid Tunnel-Password) the association > still fails due to the strict comparison of the accepted value with > HOSTAPD_ACL_ACCEPT. Apparently this wasn't previously tested. > > The patch below allows a packet containing a valid Session-Timeout > attribute to be accepted by extending the "success" comparison to > include HOSTAPD_ACL_ACCEPT_TIMEOUT. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:08:13 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:08:13 +0300 Subject: [PATCH v1 1/1] wpa_cli: Modified to make the event WPA_EVENT_CHANNEL_SWITCH accessible to action scripts In-Reply-To: <20240415084822.611328-2-arun.jose.wg@bp.renesas.com> References: <20240415084822.611328-1-arun.jose.wg@bp.renesas.com> <20240415084822.611328-2-arun.jose.wg@bp.renesas.com> Message-ID: On Mon, Apr 15, 2024 at 02:18:22PM +0530, arun.jose.wg at bp.renesas.com wrote: > Modified the code in wpa_cli.c to make the channel switch complete > event, WPA_EVENT_CHANNEL_SWITCH accessible to the action script. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:08:26 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:08:26 +0300 Subject: [PATCH] Set correct center segment indexes in channel switch fallback In-Reply-To: References: Message-ID: On Tue, Apr 16, 2024 at 11:47:36AM +0000, Jurijs Soloveckis wrote: > Set correct center segment indexes in channel switch fallback for non-5GHz band Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:08:39 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:08:39 +0300 Subject: [PATCH] DBUS: Use correct values for persistent group In-Reply-To: <20240417122812.2015822-1-andrei.otcheretianski@intel.com> References: <20240417122812.2015822-1-andrei.otcheretianski@intel.com> Message-ID: On Wed, Apr 17, 2024 at 03:28:12PM +0300, Andrei Otcheretianski wrote: > DBUS lib expects "persistent" to be bool (0/1) and crashes otherwise. > Since persistent may also be 2 convert it to boolean. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Sun Apr 21 02:08:56 2024 From: j at w1.fi (Jouni Malinen) Date: Sun, 21 Apr 2024 12:08:56 +0300 Subject: [PATCH] p2p: call normal SD query callback on RX/TX race In-Reply-To: <20240417124524.2027615-1-andrei.otcheretianski@intel.com> References: <20240417124524.2027615-1-andrei.otcheretianski@intel.com> Message-ID: On Wed, Apr 17, 2024 at 03:45:24PM +0300, Andrei Otcheretianski wrote: > If the TX success response races with the RX frame then the state > machine was simply move to P2P_SD_DURING_FIND to continue the operation. > However, this does not take into account broadcast queries where the > callback handler updates the peer's sd_pending_bcast_queries. > > Fix this by exporting the callback and calling it directly. This is > fine, as the operation is cancelled immediately afterwards, ensuring > that the callback is not called a second time. Thanks, applied. -- Jouni Malinen PGP id EFC895FA From quic_adisi at quicinc.com Sun Apr 21 20:42:05 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 09:12:05 +0530 Subject: [PATCH 00/22] hostapd: add cohosted MLO support In-Reply-To: References: <20240328181652.2956122-1-quic_adisi@quicinc.com> Message-ID: On 4/20/24 21:36, Jouni Malinen wrote: > On Thu, Mar 28, 2024 at 11:46:30PM +0530, Aditya Kumar Singh wrote: >> Currently MLO is being supported with an assumption of only single BSS per >> link in the hostapd conf file. This needs to be extended when cohosted ML >> BSS exist in the same config file. >> >> Extend the support for cohosted BSSes along with certain improvements in >> wpa state machine, rekeying areas. > > I applied patches 5-20 with cleanup. Thanks. > >> tests: MLO: add basic cohosted MLDs functionality testing >> tests: MLO: add cohosted MLDs connectivity testing > > Those are still waiting for kernel changes: > Yeah but looks like now the dependency is merged. So a with fresh snapshot of wireless tree, it should be good. >> Only the two newly added test cases in [21/22] and [22/22] depends on >> the following - >> [1] wifi: mac80211: handle link ID during management Tx >> https://lore.kernel.org/all/20240326045242.3190611-1-quic_adisi at quicinc.com/ > Having said that, however, I have found one small issue with the second test case. I will fix that and send v2 for those two separate test cases soon. From quic_adisi at quicinc.com Sun Apr 21 21:12:37 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 09:42:37 +0530 Subject: [PATCH v2 0/2] add co-hosted MLD test cases Message-ID: <20240422041239.1823537-1-quic_adisi@quicinc.com> Add test cases to test basic co-hosted MLDs functionality. These were previously part of the series - * [PATCH 00/22] hostapd: add cohosted MLO support at [21/22] and [22/22] respectively. However, the code changes are merged and only test case was pending due to wireless tree dependency. Now that is also resolved, have re-spin a v2 with only test cases changes in it. Aditya Kumar Singh (2): tests: MLO: add basic cohosted MLDs functionality testing tests: MLO: add cohosted MLDs connectivity testing --- v2: * No changes in 1/2 * Properly stop the test case so that next test case can be run without any issue when added --- tests/hwsim/test_eht.py | 274 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) base-commit: 8f83b7d7d1a824af1facff3ee895d611331507ca -- 2.34.1 From quic_adisi at quicinc.com Sun Apr 21 21:12:38 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 09:42:38 +0530 Subject: [PATCH v2 1/2] tests: MLO: add basic cohosted MLDs functionality testing In-Reply-To: <20240422041239.1823537-1-quic_adisi@quicinc.com> References: <20240422041239.1823537-1-quic_adisi@quicinc.com> Message-ID: <20240422041239.1823537-2-quic_adisi@quicinc.com> Add test case to test basic cohosted MLDs functionality. Add helper functions to create the configuration file, start hostapd instance. Client connectivty test case will be added via a subsequent change. eht_mld_cohosted_discovery: 2 co-hosted MLDs without non-MLD RNR. Basic bring up and beacon, MLD RNR, scan validation. eht_mld_cohosted_discovery_with_rnr: Same like eht_mld_cohosted_discovery but additionally non-MLD RNR (rnr=1) is also enabled. Validate the non-MLD RNR as well. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/test_eht.py | 230 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index a012fe4e799d..b53f843c7ba8 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -15,6 +15,7 @@ from tshark import run_tshark from test_gas import hs20_ap_params from test_dpp import check_dpp_capab, wait_auth_success from test_rrm import build_beacon_request, run_req_beacon, BeaconReport +import tempfile def eht_verify_wifi_version(dev): status = dev.get_status() @@ -1823,3 +1824,232 @@ def test_eht_mlo_csa(dev, apdev): traffic_test(wpas, hapd0) #TODO: CSA on non-first link + +def create_base_conf_file(iface, channel, prefix='hostapd-', hw_mode='g', + op_class=None): + # Create configuration file and add phy characteristics + fd, fname = tempfile.mkstemp(dir='/tmp', + prefix=prefix + iface + "-chan-" + str(channel) + "-") + f = os.fdopen(fd, 'w') + + f.write("driver=nl80211\n") + f.write("hw_mode=" + str(hw_mode) + "\n") + f.write("ieee80211n=1\n") + if hw_mode == 'a' and \ + (op_class is None or \ + op_class not in [131, 132, 133, 134, 135, 136, 137]): + f.write("ieee80211ac=1\n") + f.write("ieee80211ax=1\n") + f.write("ieee80211be=1\n") + f.write("channel=" + str(channel) + "\n") + + return f, fname + +def append_bss_conf_to_file(f, ifname, params, first=False): + # Add BSS specific characteristics + config = "bss" + + if first: + config = "interface" + + f.write("\n" + config + "=%s\n" % ifname) + + for k, v in list(params.items()): + f.write("{}={}\n".format(k,v)) + + f.write("mld_ap=1\n") + +def dump_config(fname): + with open(fname, 'r') as f: + cfg = f.read() + logger.debug("hostapd config: " + str(fname) + "\n" + cfg) + +def get_config(iface, count, ssid, passphrase, channel, bssid_regex, + rnr=False, debug=False): + f, fname = create_base_conf_file(iface, channel=channel) + hapds = [] + + for i in range(count): + if i == 0: + ifname = iface + else: + ifname = iface + "-" + str(i) + + set_ssid = ssid + str(i) + set_passphrase = passphrase + str(i) + params = hostapd.wpa2_params(ssid=set_ssid, passphrase=set_passphrase, + wpa_key_mgmt="SAE", ieee80211w="2") + params['sae_pwe'] = "2" + params['group_mgmt_cipher'] = "AES-128-CMAC" + params['beacon_prot'] = "1" + params["ctrl_interface"] = "/var/run/hostapd/chan_" + str(channel) + params["bssid"] = bssid_regex % (i + 1) + + if rnr: + params["rnr"]="1" + + append_bss_conf_to_file(f, ifname, params, first=(i == 0)) + + hapds.append([ifname, params["ctrl_interface"], i]) + + f.close() + + if debug: + dump_config(fname) + + return fname, hapds + +def start_ap(prefix, configs): + pid = prefix + ".hostapd.pid" + configs = configs.split() + + cmd = ['../../hostapd/hostapd', '-ddKtB', '-P', pid, '-f', + prefix + ".hostapd-log"] + + cmd = cmd + configs + + logger.info("Starting APs") + res = subprocess.check_call(cmd) + if res != 0: + raise Exception("Could not start hostapd: %s" % str(res)) + + # Wait for hostapd to complete initialization and daemonize. + time.sleep(2) + + if not os.path.exists(pid): + raise Exception("hostapd did not create PID file.") + +def get_mld_devs(hapd_iface, count, prefix, rnr=False): + fname1, hapds1 = get_config(hapd_iface, count=count, ssid="mld-", + passphrase="qwertyuiop-", channel=1, + bssid_regex="02:00:00:00:07:%02x", + rnr=rnr, debug=True) + fname2, hapds2 = get_config(hapd_iface, count=count, ssid="mld-", + passphrase="qwertyuiop-", channel=6, + bssid_regex="02:00:00:00:08:%02x", + rnr=rnr, debug=True) + + start_ap(prefix, fname1 + " " + fname2) + + hapd_mld1_link0 = hostapd.Hostapd(ifname=hapds1[0][0], ctrl=hapds1[0][1], + bssidx=hapds1[0][2]) + hapd_mld1_link1 = hostapd.Hostapd(ifname=hapds2[0][0], ctrl=hapds2[0][1], + bssidx=hapds2[0][2]) + + hapd_mld2_link0 = hostapd.Hostapd(ifname=hapds1[1][0], ctrl=hapds1[1][1], + bssidx=hapds1[1][2]) + hapd_mld2_link1 = hostapd.Hostapd(ifname=hapds2[1][0], ctrl=hapds2[1][1], + bssidx=hapds2[1][2]) + + if not hapd_mld1_link0.ping(): + raise Exception("Could not ping hostapd") + + if not hapd_mld1_link1.ping(): + raise Exception("Could not ping hostapd") + + if not hapd_mld2_link0.ping(): + raise Exception("Could not ping hostapd") + + if not hapd_mld2_link1.ping(): + raise Exception("Could not ping hostapd") + + os.remove(fname1) + os.remove(fname2) + + return [hapd_mld1_link0, hapd_mld1_link1, hapd_mld2_link0, hapd_mld2_link1] + +def stop_mld_devs(hapds, pid): + pid = pid + ".hostapd.pid" + + if "OK" not in hapds[0].request("TERMINATE"): + raise Exception("Failed to terminate hostapd process") + + ev = hapds[0].wait_event(["CTRL-EVENT-TERMINATING"], timeout=15) + if ev is None: + raise Exception("CTRL-EVENT-TERMINATING not seen") + + time.sleep(0.5) + + if os.path.exists(pid): + raise Exception("PID file exits after process termination") + +def eht_parse_rnr(bss, rnr=False, exp_bssid=None): + partner_rnr_pattern = re.compile(".*ap_info.*, mld ID=0, link ID=", + re.MULTILINE) + ml_pattern = re.compile(".*multi-link:.*, MLD addr=.*", re.MULTILINE) + + if partner_rnr_pattern.search(bss) is None: + raise Exception("RNR element not found for first link of first MLD") + + if ml_pattern.search(bss) is None: + raise Exception("ML element not found for first link of first MLD") + + if not rnr: + return + + coloc_rnr_pattern = re.compile(".*ap_info.*, mld ID=255, link ID=..", + re.MULTILINE) + + if coloc_rnr_pattern.search(bss) is None: + raise Exception("RNR element not found for co-located BSS") + + line = coloc_rnr_pattern.search(bss).group() + if line.count('bssid') > 1: + raise Exception("More than one BSS found for co-located RNR") + + # Get the BSSID carried in the RNR + index = line.rindex('bssid') + bssid = line[index+len('bssid')+1:].split(',')[0] + + # Get the MLD ID carried in the RNR + index = line.rindex('link ID') + link_id = line[index+len('link ID')+1:].split(',')[0] + + if link_id != "15": + raise Exception("Unexpected link ID for co-located BSS which is not own partner") + + if bssid != exp_bssid: + raise Exception("Unexpected BSSID for co-located BSS") + +def eht_mld_cohosted_discovery(dev, apdev, params, rnr=False): + with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'], + rnr=rnr) + + # Only scan link 0 + res = wpas.request("SCAN freq=2412") + if "FAIL" in res: + raise Exception("Failed to start scan") + + ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"]) + if ev is None: + raise Exception("Scan did not start") + + ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"]) + if ev is None: + raise Exception("Scan did not complete") + + logger.info("Scan done") + + bss = wpas.request("BSS " + hapds[0].own_addr()) + logger.info("BSS 0_0: " + str(bss)) + eht_parse_rnr(bss, rnr, hapds[2].own_addr()) + + bss = wpas.request("BSS " + hapds[2].own_addr()) + logger.info("BSS 1_0: " + str(bss)) + eht_parse_rnr(bss, rnr, hapds[0].own_addr()) + + stop_mld_devs(hapds, params['prefix']) + +def test_eht_mld_cohosted_discovery(dev, apdev, params): + """EHT 2 AP MLDs discovery""" + eht_mld_cohosted_discovery(dev, apdev, params) + +def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params): + """EHT 2 AP MLDs discovery (with co-location RNR)""" + eht_mld_cohosted_discovery(dev, apdev, params, rnr=True) -- 2.34.1 From quic_adisi at quicinc.com Sun Apr 21 21:12:39 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 09:42:39 +0530 Subject: [PATCH v2 2/2] tests: MLO: add cohosted MLDs connectivity testing In-Reply-To: <20240422041239.1823537-1-quic_adisi@quicinc.com> References: <20240422041239.1823537-1-quic_adisi@quicinc.com> Message-ID: <20240422041239.1823537-3-quic_adisi@quicinc.com> Add test case 'eht_mld_cohosted_connectivity' which creates two 2 link AP MLDs and connect 2 links MLD client to each one of them and test data traffic. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/test_eht.py | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index b53f843c7ba8..a58f73d759c5 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -2053,3 +2053,47 @@ def test_eht_mld_cohosted_discovery(dev, apdev, params): def test_eht_mld_cohosted_discovery_with_rnr(dev, apdev, params): """EHT 2 AP MLDs discovery (with co-location RNR)""" eht_mld_cohosted_discovery(dev, apdev, params, rnr=True) + +def test_eht_mld_cohosted_connectivity(dev, apdev, params): + """EHT 2 AP MLDs with 2 MLD clients connection""" + with HWSimRadio(use_mlo=True, n_channels=2) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio1, wpas_iface1): + + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(wpas_iface) + + wpas1 = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas1.interface_add(wpas_iface1) + + hapds = get_mld_devs(hapd_iface=hapd_iface, count=2, prefix=params['prefix'], + rnr=False) + + passphrase = "qwertyuiop-" + ssid = "mld-" + + # Connect one client to first AP MLD and verify traffic on both links + wpas.set("sae_pwe", "1") + wpas.connect(ssid+"0", sae_password=passphrase+"0", scan_freq="2412", + key_mgmt="SAE", ieee80211w="2") + + eht_verify_status(wpas, hapds[0], 2412, 20, is_ht=True, mld=True, + valid_links=3, active_links=3) + eht_verify_wifi_version(wpas) + + traffic_test(wpas, hapds[0]) + traffic_test(wpas, hapds[1]) + + # Connect another client to second AP MLD and verify traffic on both links + wpas1.set("sae_pwe", "1") + wpas1.connect(ssid+"1", sae_password=passphrase+"1", scan_freq="2437", + key_mgmt="SAE", ieee80211w="2") + + eht_verify_status(wpas1, hapds[3], 2437, 20, is_ht=True, mld=True, + valid_links=3, active_links=3) + eht_verify_wifi_version(wpas1) + + traffic_test(wpas1, hapds[3]) + traffic_test(wpas1, hapds[2]) + + stop_mld_devs(hapds, params['prefix']) -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 04:19:02 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 16:49:02 +0530 Subject: [PATCH 0/4] MLO related fixes for scan/ACS Message-ID: <20240422111906.2928263-1-quic_adisi@quicinc.com> Do some fixes in scan handling with MLO and also fixes deferred first link BSS radius/auth server/802.1X initialization. Aditya Kumar Singh (3): AP MLD: fix deferred first link BSS's radius, auth server and 802.1X initialization nl80211: fix scan request and its related events handling with MLO nl80211: fix simultaneous scanning failure in case of MLO Harshitha Prem (1): ACS: handle ACS scan start request failure with error code -EBUSY --- src/ap/acs.c | 62 ++++++++++--- src/ap/acs.h | 3 + src/ap/ap_drv_ops.c | 6 ++ src/ap/authsrv.c | 16 +++- src/ap/hostapd.c | 96 ++++++++++++------- src/ap/hostapd.h | 1 + src/ap/ieee802_1x.c | 15 ++- src/drivers/driver.h | 8 ++ src/drivers/driver_nl80211.c | 20 +++- src/drivers/driver_nl80211.h | 3 +- src/drivers/driver_nl80211_event.c | 144 +++++++++++++++++++---------- src/drivers/driver_nl80211_scan.c | 61 +++++++++++- wpa_supplicant/scan.c | 1 + 13 files changed, 327 insertions(+), 109 deletions(-) base-commit: ed78f56dcecb46a36fe0bfffeeb90b78f8b31b9d -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 04:19:03 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 16:49:03 +0530 Subject: [PATCH 1/4] AP MLD: fix deferred first link BSS's radius, auth server and 802.1X initialization In-Reply-To: <20240422111906.2928263-1-quic_adisi@quicinc.com> References: <20240422111906.2928263-1-quic_adisi@quicinc.com> Message-ID: <20240422111906.2928263-2-quic_adisi@quicinc.com> Currently, radius client, auth server and 802.1X are copied from first link's BSS into the non-first link during its setup. However, there could be a case where the first link is not initialized fully because of ACS/HT40 SCAN/DFS. Hence, in such cases, NULL is getting copied and later it leads to segmentation fault. Add changes to initialize those on behalf of the first link if such be the case and update it so that next time other non-first link can use it. Signed-off-by: Aditya Kumar Singh --- src/ap/authsrv.c | 16 ++++++-- src/ap/hostapd.c | 96 +++++++++++++++++++++++++++++---------------- src/ap/ieee802_1x.c | 15 +++++-- 3 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 6ed4d063349b..bfb89cc9b6a1 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -258,13 +258,21 @@ int authsrv_init(struct hostapd_data *hapd) { #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"); + struct hostapd_data *first = hostapd_mld_get_first_bss(hapd); - first = hostapd_mld_get_first_bss(hapd); if (!first) return -1; + + if (!first->eap_cfg) { + wpa_printf(MSG_DEBUG, + "MLD: First BSS auth_serv does not exist. Init on its behalf"); + + if (authsrv_init(first)) + return -1; + } + + wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS"); + #ifdef EAP_TLS_FUNCS hapd->ssl_ctx = first->ssl_ctx; #endif /* EAP_TLS_FUNCS */ diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 130b6ebc6b21..eb5bf3371456 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1307,6 +1307,58 @@ static int hostapd_start_beacon(struct hostapd_data *hapd, } +#ifndef CONFIG_NO_RADIUS +static int hostapd_bss_radius_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf; + + if (!hapd) + return -1; + + conf = hapd->conf; + + if (hapd->radius) { + wpa_printf(MSG_DEBUG, "Skipping RADIUS client init (already done)"); + return 0; + } + + hapd->radius = radius_client_init(hapd, conf->radius); + if (!hapd->radius) { + wpa_printf(MSG_ERROR, + "RADIUS client initialization failed."); + return -1; + } + + if (conf->radius_das_port) { + struct radius_das_conf das_conf; + + os_memset(&das_conf, 0, sizeof(das_conf)); + das_conf.port = conf->radius_das_port; + das_conf.shared_secret = conf->radius_das_shared_secret; + das_conf.shared_secret_len = + conf->radius_das_shared_secret_len; + das_conf.client_addr = &conf->radius_das_client_addr; + das_conf.time_window = conf->radius_das_time_window; + das_conf.require_event_timestamp = + conf->radius_das_require_event_timestamp; + das_conf.require_message_authenticator = + conf->radius_das_require_message_authenticator; + das_conf.ctx = hapd; + das_conf.disconnect = hostapd_das_disconnect; + das_conf.coa = hostapd_das_coa; + hapd->radius_das = radius_das_init(&das_conf); + if (!hapd->radius_das) { + wpa_printf(MSG_ERROR, + "RADIUS DAS initialization failed."); + return -1; + } + } + + return 0; +} +#endif /* CONFIG_NO_RADIUS */ + + /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data @@ -1540,46 +1592,24 @@ setup_mld: #endif /* CONFIG_SQLITE */ if (hostapd_mld_is_first_bss(hapd)) { - hapd->radius = radius_client_init(hapd, conf->radius); - if (!hapd->radius) { - wpa_printf(MSG_ERROR, - "RADIUS client initialization failed."); + if (hostapd_bss_radius_init(hapd)) + return -1; + } else { +#ifdef CONFIG_IEEE80211BE + struct hostapd_data *f_bss = hostapd_mld_get_first_bss(hapd); + if (!f_bss) return -1; - } - if (conf->radius_das_port) { - struct radius_das_conf das_conf; - - os_memset(&das_conf, 0, sizeof(das_conf)); - das_conf.port = conf->radius_das_port; - das_conf.shared_secret = conf->radius_das_shared_secret; - das_conf.shared_secret_len = - conf->radius_das_shared_secret_len; - das_conf.client_addr = &conf->radius_das_client_addr; - das_conf.time_window = conf->radius_das_time_window; - das_conf.require_event_timestamp = - conf->radius_das_require_event_timestamp; - das_conf.require_message_authenticator = - conf->radius_das_require_message_authenticator; - das_conf.ctx = hapd; - das_conf.disconnect = hostapd_das_disconnect; - das_conf.coa = hostapd_das_coa; - hapd->radius_das = radius_das_init(&das_conf); - if (!hapd->radius_das) { - wpa_printf(MSG_ERROR, - "RADIUS DAS initialization failed."); + if (!f_bss->radius) { + wpa_printf(MSG_DEBUG, + "MLD: First BSS RADIUS client does not exist. Init on its behalf"); + + if (hostapd_bss_radius_init(f_bss)) return -1; - } } - } else { -#ifdef CONFIG_IEEE80211BE - struct hostapd_data *f_bss; wpa_printf(MSG_DEBUG, "MLD: Using RADIUS client of the first BSS"); - 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 */ diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 8e98b6521695..857f0d184774 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -2541,14 +2541,21 @@ int ieee802_1x_init(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211BE if (!hostapd_mld_is_first_bss(hapd)) { - struct hostapd_data *first; + struct hostapd_data *first = hostapd_mld_get_first_bss(hapd); + if (!first) + return -1; + + if (!first->eapol_auth) { + wpa_printf(MSG_DEBUG, + "MLD: First BSS IEEE 802.1X state machine does not exist. Init on its behalf"); + + if (ieee802_1x_init(first)) + return -1; + } wpa_printf(MSG_DEBUG, "MLD: Using IEEE 802.1X state machine of the first BSS"); - first = hostapd_mld_get_first_bss(hapd); - if (!first) - return -1; hapd->eapol_auth = first->eapol_auth; return 0; } -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 04:19:04 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 16:49:04 +0530 Subject: [PATCH 2/4] nl80211: fix scan request and its related events handling with MLO In-Reply-To: <20240422111906.2928263-1-quic_adisi@quicinc.com> References: <20240422111906.2928263-1-quic_adisi@quicinc.com> Message-ID: <20240422111906.2928263-3-quic_adisi@quicinc.com> Currently, whenever a scan is started, it uses drv's first BSS only whether it is AP or STA interface. However with MLO AP related changes, same drv could be used by other BSSes as well which needs scanning. Hence, the current logic will not work since scan needs to be handled on non-first BSS as well. Add changes to move the logic of always using drv's first BSS during scan events to using BSS on which the event arrived. Also, for ML AP operation, even though BSS is same, link BSS also needs to be identified. Hence add a back pointer in BSS struct which would be used to point to the link BSS which requested scan on that BSS. This will help in routing the scan events to appropriate BSS ctx. Signed-off-by: Aditya Kumar Singh --- src/ap/ap_drv_ops.c | 6 ++ src/drivers/driver.h | 8 ++ src/drivers/driver_nl80211.c | 20 +++- src/drivers/driver_nl80211.h | 3 +- src/drivers/driver_nl80211_event.c | 144 +++++++++++++++++++---------- src/drivers/driver_nl80211_scan.c | 27 +++++- wpa_supplicant/scan.c | 1 + 7 files changed, 152 insertions(+), 57 deletions(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index c47349110e5f..a875422f0d45 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -781,6 +781,12 @@ bool hostapd_drv_nl80211(struct hostapd_data *hapd) int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params) { + params->mlo_link_id = -1; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + params->mlo_link_id = hapd->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ + if (hapd->driver && hapd->driver->scan2) return hapd->driver->scan2(hapd->drv_priv, params); return -1; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 52632154ee4b..9873acb524fc 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -709,6 +709,14 @@ struct wpa_driver_scan_params { */ s8 link_id; + /** + * mlo_link_id - Link ID (in case of MLO) + * + * If this is set to value >= 0, after scan completion, this would be + * used to route the event to proper driver private data. + */ + u8 mlo_link_id; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c6af0f02f619..b2efcddf058f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4195,6 +4195,22 @@ struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id) } +u8 nl80211_get_link_id_from_link(struct i802_bss *bss, struct i802_link *link) +{ + u8 link_id; + + if (link == bss->flink) + return 0; + + for_each_link(bss->valid_links, link_id) { + if (&bss->links[link_id] == link) + return link_id; + } + + return 0; +} + + static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq) { struct i802_link *link = nl80211_get_link(bss, link_id); @@ -10259,7 +10275,9 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) if (err) wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); else - wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + wpa_supplicant_event((bss->scan_link && bss->scan_link->ctx) ? + bss->scan_link->ctx : bss->ctx, + EVENT_SURVEY, &data); clean_survey_results(survey_results); return err; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 618746e67722..8e1426465586 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -67,7 +67,7 @@ struct i802_bss { u16 valid_links; struct i802_link links[MAX_NUM_MLD_LINKS]; - struct i802_link *flink; + struct i802_link *flink, *scan_link; int ifindex; int br_ifindex; @@ -352,6 +352,7 @@ const char * nl80211_iftype_str(enum nl80211_iftype mode); void nl80211_restore_ap_mode(struct i802_bss *bss); struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id); +u8 nl80211_get_link_id_from_link(struct i802_bss *bss, struct i802_link *link); static inline bool nl80211_link_valid(u16 links, s8 link_id) { diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 6e75748792bf..6432a3274d09 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1959,9 +1959,10 @@ static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv, } -static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, +static void send_scan_event(struct i802_bss *bss, int aborted, struct nlattr *tb[], int external_scan) { + struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data event; struct nlattr *nl; int rem; @@ -1969,6 +1970,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, #define MAX_REPORT_FREQS 110 int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; + struct i802_link *mld_link; + void *ctx = bss->ctx; if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; @@ -2032,7 +2035,25 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, ETH_ALEN); } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); + /* need to pass to correct link ctx during AP operation */ + if (is_ap_interface(drv->nlmode)) { + mld_link = bss->scan_link; + if (!mld_link) { + wpa_printf(MSG_DEBUG, "nl80211: Scan event on unknown link"); + goto process_scan_event; + } + + if (mld_link->ctx) { + u8 link_id = nl80211_get_link_id_from_link(bss, mld_link); + + wpa_printf(MSG_DEBUG, "nl80211: Scan event for link_id %d", + link_id); + ctx = mld_link->ctx; + } + } + +process_scan_event: + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, &event); } @@ -3833,48 +3854,16 @@ static void nl80211_color_change_announcement_completed(struct i802_bss *bss) #endif /* CONFIG_IEEE80211AX */ -static void do_process_drv_event(struct i802_bss *bss, int cmd, - struct nlattr **tb) +static void nl80211_scan_event(struct i802_bss *bss, enum nl80211_commands cmd, + struct nlattr *tb[]) { struct wpa_driver_nl80211_data *drv = bss->drv; int external_scan_event = 0; - struct nlattr *frame = tb[NL80211_ATTR_FRAME]; - - wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", - cmd, nl80211_command_to_string(cmd), bss->ifname); - -#ifdef CONFIG_DRIVER_NL80211_QCA - if (cmd == NL80211_CMD_ROAM && - (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { - if (drv->pending_roam_data) { - wpa_printf(MSG_DEBUG, - "nl80211: Process pending roam+auth vendor event"); - qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data, - drv->pending_roam_data_len); - os_free(drv->pending_roam_data); - drv->pending_roam_data = NULL; - return; - } - /* - * Device will use roam+auth vendor event to indicate - * roaming, so ignore the regular roam event. - */ - drv->roam_indication_done = true; - wpa_printf(MSG_DEBUG, - "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", - cmd); - return; - } -#endif /* CONFIG_DRIVER_NL80211_QCA */ - - if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && - (cmd == NL80211_CMD_NEW_SCAN_RESULTS || - cmd == NL80211_CMD_SCAN_ABORTED)) - nl80211_restore_ap_mode(bss); + void *ctx = bss->ctx; switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + wpa_dbg(ctx, MSG_DEBUG, "nl80211: Scan trigger"); drv->scan_state = SCAN_STARTED; if (drv->scan_for_auth) { /* @@ -3886,40 +3875,40 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); break; } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + wpa_supplicant_event(ctx, EVENT_SCAN_STARTED, NULL); break; case NL80211_CMD_START_SCHED_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + wpa_dbg(ctx, MSG_DEBUG, "nl80211: Sched scan started"); drv->scan_state = SCHED_SCAN_STARTED; break; case NL80211_CMD_SCHED_SCAN_STOPPED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + wpa_dbg(ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); drv->scan_state = SCHED_SCAN_STOPPED; - wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + wpa_supplicant_event(ctx, EVENT_SCHED_SCAN_STOPPED, NULL); break; case NL80211_CMD_NEW_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, + wpa_dbg(ctx, MSG_DEBUG, "nl80211: New scan results available"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } - send_scan_event(drv, 0, tb, external_scan_event); + send_scan_event(bss, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, + wpa_dbg(ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb, 0); + send_scan_event(bss, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + wpa_dbg(ctx, MSG_DEBUG, "nl80211: Scan aborted"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_ABORTED; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { @@ -3928,12 +3917,67 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, * order not to make wpa_supplicant stop its scanning. */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } - send_scan_event(drv, 1, tb, external_scan_event); + send_scan_event(bss, 1, tb, external_scan_event); + break; + default: + wpa_dbg(ctx, MSG_DEBUG, "nl80211: Unknown scan cmd %d (%s)", + cmd, nl80211_command_to_string(cmd)); + break; + } +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *frame = tb[NL80211_ATTR_FRAME]; + + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); + +#ifdef CONFIG_DRIVER_NL80211_QCA + if (cmd == NL80211_CMD_ROAM && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + if (drv->pending_roam_data) { + wpa_printf(MSG_DEBUG, + "nl80211: Process pending roam+auth vendor event"); + qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data, + drv->pending_roam_data_len); + os_free(drv->pending_roam_data); + drv->pending_roam_data = NULL; + return; + } + /* + * Device will use roam+auth vendor event to indicate + * roaming, so ignore the regular roam event. + */ + drv->roam_indication_done = true; + wpa_printf(MSG_DEBUG, + "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", + cmd); + return; + } +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) + nl80211_restore_ap_mode(bss); + + switch (cmd) { + case NL80211_CMD_TRIGGER_SCAN: + case NL80211_CMD_START_SCHED_SCAN: + case NL80211_CMD_SCHED_SCAN_STOPPED: + case NL80211_CMD_NEW_SCAN_RESULTS: + case NL80211_CMD_SCHED_SCAN_RESULTS: + case NL80211_CMD_SCAN_ABORTED: + nl80211_scan_event(bss, cmd, tb); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 1eb4374052e5..68deb2d9e296 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -153,6 +153,7 @@ fail: void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct i802_bss *bss; wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); #ifdef CONFIG_DRIVER_NL80211_QCA @@ -160,14 +161,22 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0) return; #endif /* CONFIG_DRIVER_NL80211_QCA */ + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->scan_link) + break; + } + if (!drv->vendor_scan_cookie && - nl80211_abort_scan(drv->first_bss) == 0) + nl80211_abort_scan(bss) == 0) { + bss->scan_link = NULL; return; + } wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) - nl80211_restore_ap_mode(drv->first_bss); + nl80211_restore_ap_mode(bss); wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); @@ -347,7 +356,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, int ret = -1, timeout; struct nl_msg *msg = NULL; - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; if (TEST_FAIL()) @@ -440,11 +449,19 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, } wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " "seconds", ret, timeout); - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; + bss->scan_link = bss->flink; + if (is_ap_interface(drv->nlmode) && + nl80211_link_valid(bss->valid_links, params->mlo_link_id)) { + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: scan requested for link %d", + params->mlo_link_id); + bss->scan_link = nl80211_get_link(bss, params->mlo_link_id); + } + fail: nlmsg_free(msg); return ret; diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 8b59e409b81d..f97af36f9c8c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -3390,6 +3390,7 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->p2p_include_6ghz = src->p2p_include_6ghz; params->non_coloc_6ghz = src->non_coloc_6ghz; params->min_probe_req_content = src->min_probe_req_content; + params->mlo_link_id = src->mlo_link_id; return params; failed: -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 04:19:05 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 16:49:05 +0530 Subject: [PATCH 3/4] nl80211: fix simultaneous scanning failure in case of MLO In-Reply-To: <20240422111906.2928263-1-quic_adisi@quicinc.com> References: <20240422111906.2928263-1-quic_adisi@quicinc.com> Message-ID: <20240422111906.2928263-4-quic_adisi@quicinc.com> Currently only 1 scan can be performed on per phy level at a time in driver. Due to this, if via other underlying phy, a scan request is sent, kernel returns -EBUSY. And then hostapd would try to set the interface in station mode if it was originally in AP mode and retry sending a scan request. However, this behaviour is expected in case of multi link operation and hence there is no need to switch the mode as such. Hence, add logic to not change the NL mode if kernel returns -EBUSY during multi link AP operation. The caller can accordingly decide and if needed, it can re-schedule a scan request after some time. Signed-off-by: Aditya Kumar Singh --- src/drivers/driver_nl80211_scan.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 68deb2d9e296..95c7ba80e81a 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -413,6 +413,40 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, if (drv->hostapd && is_ap_interface(drv->nlmode)) { enum nl80211_iftype old_mode = drv->nlmode; +#ifdef CONFIG_IEEE80211BE + /* For Multi Link BSS, retry scan if any other links + * are busy scanning + */ + if (ret == -EBUSY && + nl80211_link_valid(bss->valid_links, params->mlo_link_id)) { + struct i802_bss *link_bss; + u8 link_id; + + wpa_printf(MSG_DEBUG, + "nl80211: Scan trigger on Multi Link BSS failed (requested link=%d on interface %s)", + params->mlo_link_id, bss->ifname); + + for (link_bss = drv->first_bss; link_bss; + link_bss = link_bss->next) + if (link_bss->scan_link) + break; + + if (!link_bss) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface information already running scan not available"); + goto fail; + } + + link_id = + nl80211_get_link_id_from_link(link_bss, + link_bss->scan_link); + wpa_printf(MSG_DEBUG, + "nl80211: Scan already running on interface %s link %d", + link_bss->ifname, link_id); + goto fail; + } +#endif /* CONFIG_IEEE80211BE */ + /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 04:19:06 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 16:49:06 +0530 Subject: [PATCH 4/4] ACS: handle ACS scan start request failure with error code -EBUSY In-Reply-To: <20240422111906.2928263-1-quic_adisi@quicinc.com> References: <20240422111906.2928263-1-quic_adisi@quicinc.com> Message-ID: <20240422111906.2928263-5-quic_adisi@quicinc.com> From: Harshitha Prem Currently, if ACS scan request fails, states are cleared and returned. However, in case of multi link opertion, there is a possibilty of getting return value of -EBUSY. In this case, ACS can retry the scan request after some time same like HT-40 scan. Hence, add changes to retry the scan after 5 seconds if -EBUSY is returned. Max 15 re-attempts would be made post which no further attempts would be made. Signed-off-by: Harshitha Prem Co-developed-by: Aditya Kumar Singh Signed-off-by: Aditya Kumar Singh --- src/ap/acs.c | 62 +++++++++++++++++++++++++++++++++++++++--------- src/ap/acs.h | 3 +++ src/ap/hostapd.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/ap/acs.c b/src/ap/acs.c index 28b0ba71cc7b..24804e8ec8b2 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -12,6 +12,7 @@ #include "utils/common.h" #include "utils/list.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/wpa_ctrl.h" @@ -307,6 +308,7 @@ static const struct bw_item *bw_desc[] = { static int acs_request_scan(struct hostapd_iface *iface); static int acs_survey_is_sufficient(struct freq_survey *survey); +static void acs_scan_retry(void *eloop_data, void *user_data); static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) @@ -352,6 +354,8 @@ void acs_cleanup(struct hostapd_iface *iface) iface->chans_surveyed = 0; iface->acs_num_completed_scans = 0; + iface->acs_num_retries = 0; + eloop_cancel_timeout(acs_scan_retry, iface, NULL); } @@ -1317,6 +1321,7 @@ static void acs_scan_complete(struct hostapd_iface *iface) int err; iface->scan_cb = NULL; + iface->acs_num_retries = 0; wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", iface->conf->acs_num_scans); @@ -1329,7 +1334,7 @@ static void acs_scan_complete(struct hostapd_iface *iface) if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { err = acs_request_scan(iface); - if (err) { + if (err && err != -EBUSY) { wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); goto fail; } @@ -1378,11 +1383,10 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface, return freq; } - static int acs_request_scan(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; - int i, *freq; + int i, *freq, ret; int num_channels; struct hostapd_hw_modes *mode; @@ -1415,24 +1419,60 @@ static int acs_request_scan(struct hostapd_iface *iface) return -1; } - iface->scan_cb = acs_scan_complete; - - wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", - iface->acs_num_completed_scans + 1, - iface->conf->acs_num_scans); + if (!iface->acs_num_retries) + wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", + iface->acs_num_completed_scans + 1, + iface->conf->acs_num_scans); + else + wpa_printf(MSG_DEBUG, "ACS: Re-try Scanning attempt %d (%d / %d)", + iface->acs_num_retries, + iface->acs_num_completed_scans + 1, + iface->conf->acs_num_scans); + + ret = hostapd_driver_scan(iface->bss[0], ¶ms); + os_free(params.freqs); - if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + if (ret == -EBUSY) { + iface->acs_num_retries++; + wpa_printf(MSG_ERROR, "Failed to request acs scan ret=%d (%s) - try to scan after %d seconds", + ret, strerror(-ret), ACS_SCAN_RETRY_INTERVAL); + eloop_cancel_timeout(acs_scan_retry, iface, NULL); + eloop_register_timeout(ACS_SCAN_RETRY_INTERVAL, 0, + acs_scan_retry, iface, NULL); + return 0; + } else if (ret < 0) { wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); acs_cleanup(iface); - os_free(params.freqs); return -1; } - os_free(params.freqs); + iface->scan_cb = acs_scan_complete; + return 0; } +static void acs_scan_retry(void *eloop_data, void *user_data) +{ + struct hostapd_iface *iface = eloop_data; + int ret; + + if (iface->acs_num_retries >= ACS_SCAN_RETRY_MAX_COUNT) { + wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan (all re-attempts failed)"); + goto acs_fail; + } + + ret = acs_request_scan(iface); + if (!ret) + return; + + wpa_printf(MSG_ERROR, "ACS: Failed to request re-try of initial scan"); + +acs_fail: + acs_fail(iface); +} + + enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { int err; diff --git a/src/ap/acs.h b/src/ap/acs.h index ec84f0ee97f3..8be3de5b5f78 100644 --- a/src/ap/acs.h +++ b/src/ap/acs.h @@ -15,6 +15,9 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface); void acs_cleanup(struct hostapd_iface *iface); +#define ACS_SCAN_RETRY_MAX_COUNT 15 +#define ACS_SCAN_RETRY_INTERVAL 5 + #else /* CONFIG_ACS */ static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 1d1943ac52ec..7454cdab179d 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -682,6 +682,7 @@ struct hostapd_iface { #ifdef CONFIG_ACS unsigned int acs_num_completed_scans; + unsigned int acs_num_retries; #endif /* CONFIG_ACS */ void (*scan_cb)(struct hostapd_iface *iface); -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:00 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:00 +0530 Subject: [PATCH 1/6] ctrl_iface: add support to change BSS color from cli In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-2-quic_adisi@quicinc.com> Add hostapd_cli command "color_change " to change BSS color in run time. hostapd_cli status can be used to check the updated color. Usage: hostapd_cli -i color_change If 0 value is given, HE BSS color would be disabled. Same or a non-zero value between [1-63] can be given to enable color again. Signed-off-by: Aditya Kumar Singh --- hostapd/ctrl_iface.c | 97 +++++++++++++++++++++++++++++++++++++++++++ hostapd/hostapd_cli.c | 30 +++++++++++++ src/ap/hostapd.c | 6 +-- src/ap/hostapd.h | 3 ++ 4 files changed, 133 insertions(+), 3 deletions(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9fb6010c7362..f7fc4643a3ab 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2630,6 +2630,98 @@ static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params, #endif /* NEED_AP_MLME */ +#ifdef CONFIG_IEEE80211AX +static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface, + char *pos) +{ +#ifdef NEED_AP_MLME + struct cca_settings settings; + struct hostapd_data *hapd = iface->bss[0]; + int ret, color; + unsigned int i; + char *end; + + os_memset(&settings, 0, sizeof(settings)); + + color = strtol(pos, &end, 10); + if (pos == end || color < 0 || color > 63) { + wpa_printf(MSG_ERROR, "color_change: invalid color provided"); + return -1; + } + + /* color value is expected to be [1-63]. If 0 comes, assumption is + * user wants to disable the color. In this case no need to do CCA, + * just changing beacon is sufficient + */ + if (color == 0) { + if (iface->conf->he_op.he_bss_color_disabled) { + wpa_printf(MSG_ERROR, "color_change: color is already disabled"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 1; + + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); + + return 0; + } + + if (color == iface->conf->he_op.he_bss_color) { + if (!iface->conf->he_op.he_bss_color_disabled) { + wpa_printf(MSG_ERROR, "color_change: provided color is already set"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 0; + + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); + + return 0; + } + + if (hapd->cca_in_progress) { + wpa_printf(MSG_ERROR, "color_change: CCA is already in progress"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 0; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + + hostapd_cleanup_cca_params(bss); + + bss->cca_color = color; + bss->cca_count = 10; + + if (hostapd_fill_cca_settings(bss, &settings)) { + wpa_printf(MSG_DEBUG, + "hostapd fill cca settings failed for color: %d\n", + color); + hostapd_cleanup_cca_params(bss); + continue; + } + + wpa_printf(MSG_DEBUG, "Setting user selected color: %d", color); + ret = hostapd_drv_switch_color(bss, &settings); + + if (ret) + hostapd_cleanup_cca_params(bss); + + free_beacon_data(&settings.beacon_cca); + free_beacon_data(&settings.beacon_after); + } + + return 0; +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} +#endif /* CONFIG_IEEE80211AX */ + + static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, char *pos) { @@ -4154,6 +4246,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) reply_len = -1; +#ifdef CONFIG_IEEE80211AX + } else if (os_strncmp(buf, "COLOR_CHANGE ", 12) == 0) { + if (hostapd_ctrl_iface_color_change(hapd->iface, buf + 12)) + reply_len = -1; +#endif /* CONFIG_IEEE80211AX */ } else if (os_strncmp(buf, "NOTIFY_CW_CHANGE ", 17) == 0) { if (hostapd_ctrl_iface_notify_cw_change(hapd, buf + 17)) reply_len = -1; diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index e1fe2860b3de..8002f365bcd3 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1168,6 +1168,31 @@ static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) #endif /* CONFIG_FST */ +#ifdef CONFIG_IEEE80211AX +static int hostapd_cli_cmd_color_change(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid color_change command: no argument given \n" + "usage: \n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "COLOR_CHANGE %s", + argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long CHAN_CHANGE command\n"); + return -1; + } + + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_IEEE80211AX */ + + static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1746,6 +1771,11 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { " [sec_channel_offset=] [center_freq1=]\n" " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" " = initiate channel switch announcement" }, +#ifdef CONFIG_IEEE80211AX + { "color_change", hostapd_cli_cmd_color_change, NULL, + " = initiate BSS color change to set user color\n" + "Value 0 will disable the color\n"}, +#endif /* CONFIG_IEEE80211AX */ { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL, " = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" }, { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 130b6ebc6b21..fc965cc14d7f 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4088,7 +4088,7 @@ int hostapd_csa_in_progress(struct hostapd_iface *iface) #ifdef NEED_AP_MLME -static void free_beacon_data(struct beacon_data *beacon) +void free_beacon_data(struct beacon_data *beacon) { os_free(beacon->head); beacon->head = NULL; @@ -4535,8 +4535,8 @@ void hostapd_cleanup_cca_params(struct hostapd_data *hapd) } -static int hostapd_fill_cca_settings(struct hostapd_data *hapd, - struct cca_settings *settings) +int hostapd_fill_cca_settings(struct hostapd_data *hapd, + struct cca_settings *settings) { struct hostapd_iface *iface = hapd->iface; u8 old_color; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 1d1943ac52ec..9c0714a34b20 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -823,6 +823,9 @@ 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); +void free_beacon_data(struct beacon_data *beacon); +int hostapd_fill_cca_settings(struct hostapd_data *hapd, + struct cca_settings *settings); #ifdef CONFIG_IEEE80211BE bool hostapd_mld_is_first_bss(struct hostapd_data *hapd); -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:01 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:01 +0530 Subject: [PATCH 2/6] hostapd: remove double "on" debug print from CCA event callbacks In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-3-quic_adisi@quicinc.com> Signed-off-by: Aditya Kumar Singh --- src/ap/drv_callbacks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index e026d9a6e05b..038408fd1bc2 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -2714,16 +2714,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->bss_color_collision.bitmap); break; case EVENT_CCA_STARTED_NOTIFY: - wpa_printf(MSG_DEBUG, "CCA started on on %s", + wpa_printf(MSG_DEBUG, "CCA started on %s", hapd->conf->iface); break; case EVENT_CCA_ABORTED_NOTIFY: - wpa_printf(MSG_DEBUG, "CCA aborted on on %s", + wpa_printf(MSG_DEBUG, "CCA aborted on %s", hapd->conf->iface); hostapd_cleanup_cca_params(hapd); break; case EVENT_CCA_NOTIFY: - wpa_printf(MSG_DEBUG, "CCA finished on on %s", + wpa_printf(MSG_DEBUG, "CCA finished on %s", hapd->conf->iface); if (hapd->cca_color) hapd->iface->conf->he_op.he_bss_color = hapd->cca_color; -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:04 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:04 +0530 Subject: [PATCH 5/6] tests: add HE BSS color change test In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-6-quic_adisi@quicinc.com> Add he_bss_color_change test case which brings up a HE AP and performs color change operation and validates the same via status cli. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/test_he.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/hwsim/test_he.py b/tests/hwsim/test_he.py index 544fa2e7b3d6..ce572f111e7c 100644 --- a/tests/hwsim/test_he.py +++ b/tests/hwsim/test_he.py @@ -1797,3 +1797,45 @@ def run_he_downgrade_to_20_mhz(dev, apdev, params): finally: dev[0].request("DISCONNECT") clear_regdom(hapd, dev) + +def test_he_bss_color_change(dev, apdev): + """HE AP with Color change""" + params = {"ssid": "test_he", + "ieee80211ax": "1", + "he_bss_color": "42", + "he_mu_edca_ac_be_ecwmin": "7", + "he_mu_edca_ac_be_ecwmax": "15"} + hapd = hostapd.add_ap(apdev[0], params) + if hapd.get_status_field("ieee80211ax") != "1": + raise Exception("STATUS did not indicate ieee80211ax=1") + + color = hapd.get_status_field("he_bss_color") + if color != "42": + raise Exception("Expected current he_bss_color to be 42") + + # Small sleep to capture beacons before the change + time.sleep(0.5) + + # Change color by doing CCA + hapd.request("COLOR_CHANGE 20") + time.sleep(1.5) + + color = hapd.get_status_field("he_bss_color") + if color != "20": + raise Exception("Expected current he_bss_color to be 20") + + # Disable color by setting value to 0 + hapd.request("COLOR_CHANGE 0") + time.sleep(1.5) + + color = hapd.get_status_field("he_bss_color") + if color is not None: + raise Exception("Expected he_bss_color to get disabled but found %d" % color) + + # Enable color back by setting same previous color value + hapd.request("COLOR_CHANGE 20") + time.sleep(1.5) + + color = hapd.get_status_field("he_bss_color") + if color != "20": + raise Exception("Expected current he_bss_color to be 20") -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:39:59 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:09:59 +0530 Subject: [PATCH 0/6] Add HE BSS color change support via cli Message-ID: <20240422124005.3075001-1-quic_adisi@quicinc.com> Add support to change HE BSS color via cli. Also, add support to make this work for a AP MLD. However, like CSA, color change can only be done on first link for now. To test this, add two new test cases as well. Aditya Kumar Singh (6): ctrl_iface: add support to change BSS color from cli hostapd: remove double "on" debug print from CCA event callbacks hostapd: update beacons after color change hostapd: MLO: send link id during color change tests: add HE BSS color change test tests: MLO: add color change test --- hostapd/ctrl_iface.c | 97 ++++++++++++++++++++++++++++++++++++ hostapd/hostapd_cli.c | 30 +++++++++++ src/ap/drv_callbacks.c | 42 +++++++++++++--- src/ap/hostapd.c | 12 +++-- src/ap/hostapd.h | 3 ++ src/drivers/driver.h | 5 ++ src/drivers/driver_nl80211.c | 14 ++++++ tests/hwsim/test_eht.py | 39 +++++++++++++++ tests/hwsim/test_he.py | 42 ++++++++++++++++ 9 files changed, 274 insertions(+), 10 deletions(-) base-commit: ed78f56dcecb46a36fe0bfffeeb90b78f8b31b9d -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:02 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:02 +0530 Subject: [PATCH 3/6] hostapd: update beacons after color change In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-4-quic_adisi@quicinc.com> Once CCA is finished, beacon needs to be updated. BCCA IE needs to be removed and new color value shall be advertised in the BSS Color Information field of HE Operation IE. Add changes to update the beacons accordingly. Signed-off-by: Aditya Kumar Singh --- src/ap/drv_callbacks.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 038408fd1bc2..c269dc9be3cd 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -2383,6 +2383,36 @@ static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, #endif /* NEED_AP_MLME */ +#ifdef CONFIG_IEEE80211AX +static void hostapd_event_color_change(struct hostapd_data *hapd, bool success) +{ + struct hostapd_data *bss; + size_t i; + int err; + + for (i = 0; i < hapd->iface->num_bss; i++) { + bss = hapd->iface->bss[i]; + + if (bss->cca_color == 0) + continue; + + if (success) + hapd->iface->conf->he_op.he_bss_color = + bss->cca_color; + + bss->cca_in_progress = 0; + err = ieee802_11_set_beacon(bss); + if (err) { + wpa_printf(MSG_ERROR, "Failed to remove BCCA IE"); + bss->cca_in_progress = 1; + } else { + hostapd_cleanup_cca_params(bss); + } + } +} +#endif /* CONFIG_IEEE80211AX */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -2720,14 +2750,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_CCA_ABORTED_NOTIFY: wpa_printf(MSG_DEBUG, "CCA aborted on %s", hapd->conf->iface); - hostapd_cleanup_cca_params(hapd); + hostapd_event_color_change(hapd, false); break; case EVENT_CCA_NOTIFY: wpa_printf(MSG_DEBUG, "CCA finished on %s", hapd->conf->iface); - if (hapd->cca_color) - hapd->iface->conf->he_op.he_bss_color = hapd->cca_color; - hostapd_cleanup_cca_params(hapd); + hostapd_event_color_change(hapd, true); break; #endif /* CONFIG_IEEE80211AX */ default: -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:03 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:03 +0530 Subject: [PATCH 4/6] hostapd: MLO: send link id during color change In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-5-quic_adisi@quicinc.com> Add changes to send link id in the NL command if operating in MLO during color change. Signed-off-by: Aditya Kumar Singh --- src/ap/hostapd.c | 6 ++++++ src/drivers/driver.h | 5 +++++ src/drivers/driver_nl80211.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index fc965cc14d7f..c211f64e6c27 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4545,6 +4545,12 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd, if (!iface || iface->conf->he_op.he_bss_color_disabled) return -1; +#ifdef CONFIG_IEEE80211BE + settings->link_id = -1; + if (hapd->conf->mld_ap) + settings->link_id = hapd->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ + old_color = iface->conf->he_op.he_bss_color; iface->conf->he_op.he_bss_color = hapd->cca_color; ret = hostapd_build_beacon_data(hapd, &settings->beacon_after); diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 52632154ee4b..4cf4eeee2975 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2752,6 +2752,7 @@ struct csa_settings { * @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info * @counter_offset_beacon: Offset to the count field in Beacon frame tail * @counter_offset_presp: Offset to the count field in Probe Response frame + * @link_id: If >=0 indicates the link of the MLD to configure */ struct cca_settings { u8 cca_count; @@ -2762,6 +2763,10 @@ struct cca_settings { u16 counter_offset_beacon; u16 counter_offset_presp; + +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ }; /* TDLS peer capabilities for send_tdls_mgmt() */ diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c6af0f02f619..255becc1e73e 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -11389,6 +11389,20 @@ static int nl80211_switch_color(void *priv, struct cca_settings *settings) } nla_nest_end(msg, beacon_cca); + +#ifdef CONFIG_IEEE80211BE + if (nl80211_link_valid(bss->valid_links, settings->link_id)) { + wpa_printf(MSG_DEBUG, "nl80211: Color change request on link_id=%d", + settings->link_id); + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + settings->link_id)) { + nlmsg_free(msg); + return -1; + } + } +#endif /* CONFIG_IEEE80211BE */ + ret = send_and_recv_cmd(drv, msg); if (ret) { wpa_printf(MSG_DEBUG, -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:40:05 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:10:05 +0530 Subject: [PATCH 6/6] tests: MLO: add color change test In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <20240422124005.3075001-7-quic_adisi@quicinc.com> Add eht_mlo_color_change test case to perform color change on first link of a AP MLD. Performing on non-first link will be done later. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/test_eht.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index a012fe4e799d..30ce7a3dc45b 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -1823,3 +1823,42 @@ def test_eht_mlo_csa(dev, apdev): traffic_test(wpas, hapd0) #TODO: CSA on non-first link + +def test_eht_mlo_color_change(dev, apdev): + """EHT MLD AP. Color Change Announcement""" + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + ssid = "mld_ap" + passphrase = 'qwertyuiop' + + params = eht_mld_ap_wpa2_params(ssid, passphrase, + key_mgmt="SAE", mfp="2", pwe='1') + params['link_id'] = '0' + params['he_bss_color'] = '42' + + hapd0 = eht_mld_enable_ap(hapd_iface, params) + + params['channel'] = '6' + params['link_id'] = '1' + params['he_bss_color'] = '24' + + hapd1 = eht_mld_enable_ap(hapd_iface, params) + + logger.info("Perform CCA on 1st link") + hapd0.request("COLOR_CHANGE 10") + time.sleep(1.5) + + color = hapd0.get_status_field("he_bss_color") + if color != "10": + raise Exception("Expected current he_bss_color to be 10") + + logger.info("Perform CCA on 1st link again") + hapd0.request("COLOR_CHANGE 60") + time.sleep(1.5) + + color = hapd0.get_status_field("he_bss_color") + if color != "60": + raise Exception("Expected current he_bss_color to be 60") + + #TODO: CCA on non-first link -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 05:50:23 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Mon, 22 Apr 2024 18:20:23 +0530 Subject: [PATCH 0/6] Add HE BSS color change support via cli In-Reply-To: <20240422124005.3075001-1-quic_adisi@quicinc.com> References: <20240422124005.3075001-1-quic_adisi@quicinc.com> Message-ID: <24ca398c-d23f-4072-97d6-8c929ff916e1@quicinc.com> On 4/22/24 18:09, Aditya Kumar Singh wrote: > Add support to change HE BSS color via cli. Also, add support to make this > work for a AP MLD. However, like CSA, color change can only be done on > first link for now. > > To test this, add two new test cases as well. > > Aditya Kumar Singh (6): > ctrl_iface: add support to change BSS color from cli > hostapd: remove double "on" debug print from CCA event callbacks > hostapd: update beacons after color change > hostapd: MLO: send link id during color change > tests: add HE BSS color change test > tests: MLO: add color change test > --- Depends-on: * Patch 4, 5 and 6 only depends on [PATCH v2 0/7] wifi: cfg80211/mac80211: add support for HE BSS color handling with Multi-Link Operation (https://patchwork.kernel.org/project/linux-wireless/list/?series=846477&state=%2A&archive=both) --- From quic_adisi at quicinc.com Mon Apr 22 22:31:23 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:23 +0530 Subject: [PATCH 1/6] ctrl_iface: create link based hapd control sockets In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-2-quic_adisi@quicinc.com> From: Karthikeyan Kathirvel Create link based control sockets to access the link based commands through hostapd_cli. This will create the link interfaces in the name of wlan_link Example: To fetch link 0 status from wlan0, below command can be used - $ hostapd_cli -i wlan0 -l 0 status On failure of link/interface selection, below error will be observed $ hostapd_cli -i wlan0 -l 2 status Failed to connect to hostapd - wpa_ctrl_open: No such file or directory Signed-off-by: Karthikeyan Kathirvel Co-developed-by: Aditya Kumar Singh Signed-off-by: Aditya Kumar Singh --- hostapd/ctrl_iface.c | 16 ++++++++++++++-- hostapd/hostapd_cli.c | 30 ++++++++++++++++++++++++++++-- src/ap/hostapd.c | 28 ++++++++++++++++++++++++++++ src/ap/hostapd.h | 1 + src/common/wpa_ctrl.h | 4 ++++ 5 files changed, 75 insertions(+), 4 deletions(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9fb6010c7362..d4bb8a055ef2 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -4574,18 +4574,26 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { char *buf; size_t len; + char *ctrl_sock_iface; + +#ifdef CONFIG_IEEE80211BE + ctrl_sock_iface = hapd->ctrl_sock_iface; +#else + ctrl_sock_iface = hapd->conf->iface; +#endif /* CONFIG_IEEE80211BE */ if (hapd->conf->ctrl_interface == NULL) return NULL; len = os_strlen(hapd->conf->ctrl_interface) + - os_strlen(hapd->conf->iface) + 2; + os_strlen(ctrl_sock_iface) + 2; + buf = os_malloc(len); if (buf == NULL) return NULL; os_snprintf(buf, len, "%s/%s", - hapd->conf->ctrl_interface, hapd->conf->iface); + hapd->conf->ctrl_interface, ctrl_sock_iface); buf[len - 1] = '\0'; return buf; } @@ -4756,7 +4764,11 @@ fail: #endif /* ANDROID */ if (os_strlen(hapd->conf->ctrl_interface) + 1 + +#ifdef CONFIG_IEEE80211BE + os_strlen(hapd->ctrl_sock_iface) >= sizeof(addr.sun_path)) +#else os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) +#endif /* CONFIG_IEEE80211BE */ goto fail; s = socket(PF_UNIX, SOCK_DGRAM, 0); diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index e1fe2860b3de..e6ad7ae26524 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -54,7 +54,11 @@ static void usage(void) fprintf(stderr, "%s\n", hostapd_cli_version); fprintf(stderr, "\n" +#ifdef CONFIG_IEEE80211BE + "usage: hostapd_cli [-p] [-i] [-l] [-hvBr] " +#else "usage: hostapd_cli [-p] [-i] [-hvBr] " +#endif /* CONFIG_IEEE80211BE */ "[-a] \\\n" " [-P] [-G] [command..]\n" "\n" @@ -74,7 +78,12 @@ static void usage(void) " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" - " socket path)\n\n"); + " socket path)\n" +#ifdef CONFIG_IEEE80211BE + " -l Link ID of the interface in case of Multi-Link\n" + " Operation\n" +#endif /* CONFIG_IEEE80211BE */ + "\n"); print_help(stderr, NULL); } @@ -2191,19 +2200,26 @@ static void hostapd_cli_action(struct wpa_ctrl *ctrl) eloop_unregister_read_sock(fd); } - int main(int argc, char *argv[]) { int warning_displayed = 0; int c; int daemonize = 0; int reconnect = 0; +#ifdef CONFIG_IEEE80211BE + int link_id = -1; + char buf[300]; +#endif /* CONFIG_IEEE80211BE */ if (os_program_init()) return -1; for (;;) { +#ifdef CONFIG_IEEE80211BE + c = getopt(argc, argv, "a:BhG:i:l:p:P:rs:v"); +#else c = getopt(argc, argv, "a:BhG:i:p:P:rs:v"); +#endif /* CONFIG_IEEE80211BE */ if (c < 0) break; switch (c) { @@ -2238,6 +2254,16 @@ int main(int argc, char *argv[]) case 's': client_socket_dir = optarg; break; +#ifdef CONFIG_IEEE80211BE + case 'l': + link_id = atoi(optarg); + os_memset(buf, '\0', sizeof(buf)); + os_snprintf(buf, sizeof(buf), "%s_%s%d", + ctrl_ifname, WPA_CTRL_IFACE_LINK_NAME, link_id); + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(buf); + break; +#endif /* CONFIG_IEEE80211BE */ default: usage(); return -1; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 130b6ebc6b21..ebdf445831ab 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1775,12 +1775,37 @@ int hostapd_set_acl(struct hostapd_data *hapd) } +static void hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd) +{ +#ifdef CONFIG_IEEE80211BE + os_memset(hapd->ctrl_sock_iface, '\0', + sizeof(hapd->ctrl_sock_iface)); + os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface, + sizeof(hapd->ctrl_sock_iface)); + + if (hapd->conf->mld_ap) { + char buf[128]; + + os_memset(buf, '\0', sizeof(buf)); + os_snprintf(buf, sizeof(buf), "%s_%s%d", + hapd->conf->iface, WPA_CTRL_IFACE_LINK_NAME, + hapd->mld_link_id); + os_memset(hapd->ctrl_sock_iface, '\0', + sizeof(hapd->ctrl_sock_iface)); + os_strlcpy(hapd->ctrl_sock_iface, buf, sizeof(buf)); + } +#endif /* CONFIG_IEEE80211BE */ +} + + static int start_ctrl_iface_bss(struct hostapd_data *hapd) { if (!hapd->iface->interfaces || !hapd->iface->interfaces->ctrl_iface_init) return 0; + hostapd_set_ctrl_sock_iface(hapd); + if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", @@ -1801,6 +1826,9 @@ static int start_ctrl_iface(struct hostapd_iface *iface) for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *hapd = iface->bss[i]; + + hostapd_set_ctrl_sock_iface(hapd); + if (iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 1d1943ac52ec..4b87a8b76262 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -476,6 +476,7 @@ struct hostapd_data { struct hostapd_mld *mld; struct dl_list link; u8 mld_link_id; + char ctrl_sock_iface[IFNAMSIZ + 1]; #ifdef CONFIG_TESTING_OPTIONS u8 eht_mld_link_removal_count; #endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index f6142501e440..865ac6d91052 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -674,4 +674,8 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); } #endif +#ifdef CONFIG_IEEE80211BE +#define WPA_CTRL_IFACE_LINK_NAME "link" +#endif /* CONFIG_IEEE80211BE */ + #endif /* WPA_CTRL_H */ -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:22 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:22 +0530 Subject: [PATCH 0/6] MLO control socket changes Message-ID: <20240423053128.129322-1-quic_adisi@quicinc.com> Control socket for each link BSS of MLD currently needs to be in a separate directory since interface name is same for all links in an AP MLD. Hence once the first link comes up, rest of links will not come up if using the same control interface directory. Hence, introduce link level sockets. Each link will use socket with name "_link" under given control interface directory. Also, introduce a MLD level socket with name "" in the same directory. This will help to route the commands to underlying links if required as well as it will keep backwards compatibility with other applications trying to find "" file in the control interface directory. Aditya Kumar Singh (5): ctrl_iface: MLO: introduce MLD level socket hostapd_cli: MLO: pass 'LINKID' in the command hostapd_cli: MLO: add status command for MLD socket tests: MLO: use link ID to access control sockets tests: MLO: add MLD socket connectivity test case Karthikeyan Kathirvel (1): ctrl_iface: create link based hapd control sockets --- hostapd/ctrl_iface.c | 397 +++++++++++++++++++++++++++++++++++++++- hostapd/ctrl_iface.h | 4 + hostapd/hostapd_cli.c | 69 ++++++- hostapd/main.c | 5 + src/ap/hostapd.c | 39 ++++ src/ap/hostapd.h | 7 + src/common/wpa_ctrl.c | 54 ++++++ src/common/wpa_ctrl.h | 7 + tests/hwsim/hostapd.py | 27 ++- tests/hwsim/mld.py | 36 ++++ tests/hwsim/test_eht.py | 104 ++++++++++- 11 files changed, 735 insertions(+), 14 deletions(-) create mode 100644 tests/hwsim/mld.py base-commit: 5308029f8684b789b433c05e79c0a7ec37971ef8 -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:25 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:25 +0530 Subject: [PATCH 3/6] hostapd_cli: MLO: pass 'LINKID' in the command In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-4-quic_adisi@quicinc.com> MLD level socket can take 'LINKID '. Add changes to pass this via hostapd_cli. User needs to give "link_id=" in post fix fashion in the command in order to pass this link_id from cli. For example - $ hostapd_cli -i wlan0 status link_id=0 | grep freq= freq=2437 $ hostapd_cli -i wlan0 ... Interactive mode > ping PONG > > status link_id=0 Command for 'LINKID 0' state=ENABLED phy=phy0 freq=2437 Signed-off-by: Aditya Kumar Singh --- hostapd/hostapd_cli.c | 39 +++++++++++++++++++++++++++++++ src/common/wpa_ctrl.c | 54 +++++++++++++++++++++++++++++++++++++++++++ src/common/wpa_ctrl.h | 3 +++ 3 files changed, 96 insertions(+) diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index e6ad7ae26524..9f56dbd81cd3 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1948,6 +1948,45 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } else if (count == 0) { printf("Unknown command '%s'\n", argv[0]); } else { +#ifdef CONFIG_IEEE80211BE + char *pos, *end; + int i, j, link_id; + bool link_found = false; + + wpa_ctrl_reset_mld_link(ctrl); + i = 0; + + while (i < argc) { + pos = os_strstr(argv[i], "link_id="); + if (!pos) { + i++; + continue; + } + + pos = pos + 8; + link_id = strtol(pos, &end, 10); + + if (link_id < 0 || link_id >= 15) { + printf("Invalid link ID '%d'\n", link_id); + return; + } + + link_found = true; + + /* remove this link_id= from the arguements */ + for (j = i + 1; j < argc; j++) + argv[j - 1] = argv[j]; + + argc--; + i = 0; + } + + if (link_found) { + wpa_ctrl_set_mld_link(ctrl, link_id); + printf("Command for '%s'\n", + wpa_ctrl_get_mld_link(ctrl)); + } +#endif /* CONFIG_IEEE80211BE */ match->handler(ctrl, argc - 1, &argv[1]); } } diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 7e197f094fd1..d0c174c05d48 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -72,6 +72,13 @@ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE HANDLE pipe; #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +#ifdef CONFIG_IEEE80211BE + /* 'LINKID ' - 7 chars including space + * 'XX' - Two chars max for link id + * Total required 10 chars at least + */ + char link_id_str[10]; +#endif /* CONFIG_IEEE80211BE */ }; @@ -488,6 +495,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, fd_set rfds; const char *_cmd; char *cmd_buf = NULL; + char *link_cmd_buf = NULL; size_t _cmd_len; #ifdef CONFIG_CTRL_IFACE_UDP @@ -510,6 +518,28 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, _cmd_len = cmd_len; } +#ifdef CONFIG_IEEE80211BE + if (os_strlen(ctrl->link_id_str)) { + char *pos; + + _cmd_len = _cmd_len + 1 + os_strlen(ctrl->link_id_str); + link_cmd_buf = os_malloc(_cmd_len); + if (link_cmd_buf == NULL) { + if (cmd_buf) + os_free(cmd_buf); + return -1; + } + + pos = link_cmd_buf; + os_strlcpy(pos, _cmd, _cmd_len); + pos += os_strlen(_cmd); + *pos++ = ' '; + os_memcpy(pos, ctrl->link_id_str, os_strlen(ctrl->link_id_str)); + _cmd = link_cmd_buf; + wpa_ctrl_reset_mld_link(ctrl); + } +#endif /* CONFIG_IEEE80211BE */ + errno = 0; started_at.sec = 0; started_at.usec = 0; @@ -535,9 +565,11 @@ retry_send: } send_err: os_free(cmd_buf); + os_free(link_cmd_buf); return -1; } os_free(cmd_buf); + os_free(link_cmd_buf); os_get_reltime(&ending_at); ending_at.sec += 10; @@ -773,4 +805,26 @@ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#ifdef CONFIG_IEEE80211BE +void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl) +{ + os_memset(ctrl->link_id_str, '\0', sizeof(ctrl->link_id_str)); +} + + +void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id) +{ + os_snprintf(ctrl->link_id_str, sizeof(ctrl->link_id_str), + "LINKID %d", link_id); +} + + +char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl) +{ + return ctrl->link_id_str; +} +#endif /* CONFIG_IEEE80211BE */ + + #endif /* CONFIG_CTRL_IFACE */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 865ac6d91052..d1ce1dd299f4 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -676,6 +676,9 @@ char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); #ifdef CONFIG_IEEE80211BE #define WPA_CTRL_IFACE_LINK_NAME "link" +void wpa_ctrl_reset_mld_link(struct wpa_ctrl *ctrl); +void wpa_ctrl_set_mld_link(struct wpa_ctrl *ctrl, int link_id); +char *wpa_ctrl_get_mld_link(struct wpa_ctrl *ctrl); #endif /* CONFIG_IEEE80211BE */ #endif /* WPA_CTRL_H */ -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:26 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:26 +0530 Subject: [PATCH 4/6] hostapd_cli: MLO: add status command for MLD socket In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-5-quic_adisi@quicinc.com> Add MLD level 'status' command. Currently each link level socket has got 'status' command. When the same is passed on MLD level socket without any link id, it routes it to first BSS of the MLD if available. Handle this now properly. If link id is not passed then it will be treated as MLD level status command. $ hostapd_cli -i wlan0 .... Interactive mode > status name=wlan0 mld_address=AA:BB:CC:DD:EE:FF num_links=2 LINK INFORMATION link_id=0 link_addr=AA:BB:CC:DD:EE:EE link_id=1 link_addr=AA:BB:CC:DD:FF:FF Signed-off-by: Aditya Kumar Singh --- hostapd/ctrl_iface.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 4654766b4014..a825bfb6711c 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -4587,6 +4587,49 @@ static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld, } +int hostapd_ctrl_mld_iface_status(struct hostapd_mld *mld, char *buf, + size_t buflen) +{ + struct hostapd_data *link_hapd; + int len = 0, ret; + + ret = os_snprintf(buf + len, buflen - len, + "name=%s\n" + "mld_address=" MACSTR "\n" + "num_links=%d\n", + mld->name, MAC2STR(mld->mld_addr), mld->num_links); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + if (!mld->fbss) { + ret = os_snprintf(buf + len, buflen - len, + "\n No Link information present\n"); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + ret = os_snprintf(buf + len, buflen - len, + "LINK INFORMATION\n"); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + dl_list_for_each(link_hapd, &mld->links, struct hostapd_data, link) { + ret = os_snprintf(buf + len, buflen - len, + "link_id=%d\n" + "link_addr=" MACSTR "\n", + link_hapd->mld_link_id, MAC2STR(link_hapd->own_addr)); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + return len; +} + + static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld, char *buf, char *reply, int reply_size, @@ -4653,6 +4696,9 @@ static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld, } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen)) reply_len = -1; + } else if (os_strcmp(buf, "STATUS") == 0 && link_id == -1){ + reply_len = hostapd_ctrl_mld_iface_status(mld, reply, + reply_size); } else { if (link_id == -1) wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)"); -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:27 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:27 +0530 Subject: [PATCH 5/6] tests: MLO: use link ID to access control sockets In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-6-quic_adisi@quicinc.com> With MLO, each BSS will create sockets under the given ctrl_iface directory with the socket name being '_link'. Make necessary changes in MLO related test cases so that it can access the new socket and proceed further as expected. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/hostapd.py | 27 +++++++++++----- tests/hwsim/test_eht.py | 70 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/tests/hwsim/hostapd.py b/tests/hwsim/hostapd.py index ebb2e328af50..4fa19294a99e 100644 --- a/tests/hwsim/hostapd.py +++ b/tests/hwsim/hostapd.py @@ -147,14 +147,20 @@ class HostapdGlobal: class Hostapd: def __init__(self, ifname, bssidx=0, hostname=None, ctrl=hapd_ctrl, - port=8877): + port=8877, link=None): self.hostname = hostname self.host = remotehost.Host(hostname, ifname) self.ifname = ifname if hostname is None: - self.ctrl = wpaspy.Ctrl(os.path.join(ctrl, ifname)) - self.mon = wpaspy.Ctrl(os.path.join(ctrl, ifname)) - self.dbg = ifname + if link is None: + self.ctrl = wpaspy.Ctrl(os.path.join(ctrl, ifname)) + self.mon = wpaspy.Ctrl(os.path.join(ctrl, ifname)) + self.dbg = ifname + else: + ifname = ifname + "_link" + link + self.ctrl = wpaspy.Ctrl(os.path.join(ctrl, ifname)) + self.mon = wpaspy.Ctrl(os.path.join(ctrl, ifname)) + self.dbg = ifname else: self.ctrl = wpaspy.Ctrl(hostname, port) self.mon = wpaspy.Ctrl(hostname, port) @@ -740,6 +746,13 @@ def add_mld_link(apdev, params): hostname = None port = 8878 + if "link_id" not in params: + raise Exception("Link ID not passed in param") + + link_id = params["link_id"] + # Delete the 'link_id' key from params or else it will be added in config + del params["link_id"] + hapd_global = HostapdGlobal(apdev) confname, ctrl_iface = cfg_mld_link_file(ifname, params) hapd_global.send_file(confname, confname) @@ -749,7 +762,8 @@ def add_mld_link(apdev, params): if str(e) == "Could not add hostapd link": raise utils.HwsimSkip("No MLO support in hostapd") port = hapd_global.get_ctrl_iface_port(ifname) - hapd = Hostapd(ifname, hostname=hostname, ctrl=ctrl_iface, port=port) + hapd = Hostapd(ifname, hostname=hostname, ctrl=ctrl_iface, port=port, + link=link_id) if not hapd.ping(): raise Exception("Could not ping hostapd") return hapd @@ -1023,9 +1037,6 @@ def cfg_mld_link_file(ifname, params): fd, fname = tempfile.mkstemp(dir='/tmp', prefix=conf + '-') f = os.fdopen(fd, 'w') - if idx != 0: - ctrl_iface="/var/run/hostapd_%d" % idx - f.write("ctrl_interface=%s\n" % ctrl_iface) f.write("driver=nl80211\n") f.write("ieee80211n=1\n") diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index 296445e27198..8acb6a57bc7b 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -300,10 +300,12 @@ def test_eht_mld_discovery(dev, apdev): ssid = "mld_ap" link0_params = {"ssid": ssid, "hw_mode": "g", - "channel": "1"} + "channel": "1", + "link_id": "0"} link1_params = {"ssid": ssid, "hw_mode": "g", - "channel": "2"} + "channel": "2", + "link_id": "1"} hapd0 = eht_mld_enable_ap(hapd_iface, link0_params) hapd1 = eht_mld_enable_ap(hapd_iface, link1_params) @@ -385,12 +387,14 @@ def _eht_mld_owe_two_links(dev, apdev, second_link_disabled=False, ssid = "mld_ap_owe_two_link" params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' if second_link_disabled: params['mld_indicate_disabled'] = '1' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd0_iface, params) # Check legacy client connection @@ -443,6 +447,7 @@ def test_eht_mld_sae_single_link(dev, apdev): ssid = "mld_ap_sae_single_link" params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE", mfp="2", pwe='2') + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) @@ -468,10 +473,12 @@ def run_eht_mld_sae_two_links(dev, apdev, beacon_prot="1", params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE", mfp="2", pwe='1', beacon_prot=beacon_prot) + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -554,6 +561,7 @@ def test_eht_mld_sae_ext_one_link(dev, apdev): passphrase = 'qwertyuiop' ssid = "mld_ap_sae_ext_single_link" params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE-EXT-KEY") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) @@ -577,10 +585,12 @@ def test_eht_mld_sae_ext_two_links(dev, apdev): ssid = "mld_ap_sae_two_link" params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE-EXT-KEY") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -600,10 +610,12 @@ def test_eht_mld_sae_legacy_client(dev, apdev): ssid = "mld_ap_sae_two_link" params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE", mfp="2", pwe='1') + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -640,10 +652,12 @@ def test_eht_mld_sae_transition(dev, apdev): params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE-EXT-KEY SAE WPA-PSK WPA-PSK-SHA256", mfp="1") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -676,10 +690,12 @@ def test_eht_mld_ptk_rekey(dev, apdev): key_mgmt="SAE-EXT-KEY SAE WPA-PSK WPA-PSK-SHA256", mfp="1") params['wpa_ptk_rekey'] = '5' + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -716,10 +732,12 @@ def test_eht_mld_gtk_rekey(dev, apdev): key_mgmt="SAE-EXT-KEY SAE WPA-PSK WPA-PSK-SHA256", mfp="1") params['wpa_group_rekey'] = '5' + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -756,10 +774,12 @@ def test_eht_ml_probe_req(dev, apdev): ssid = "mld_ap_sae_two_link" params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE-EXT-KEY") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, params) @@ -798,10 +818,14 @@ def test_eht_mld_connect_probes(dev, apdev, params): key_mgmt="SAE", pwe='2') link_params['channel'] = '1' link_params['bssid'] = '00:11:22:33:44:01' + link_params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, link_params) link_params['channel'] = '6' link_params['bssid'] = '00:11:22:33:44:02' + link_params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd_iface, link_params) wpas.set("sae_pwe", "1") @@ -843,10 +867,14 @@ def test_eht_tx_link_rejected_connect_other(dev, apdev, params): key_mgmt="SAE", pwe='2') link_params['channel'] = '1' link_params['bssid'] = '00:11:22:33:44:01' + link_params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, link_params) link_params['channel'] = '6' link_params['bssid'] = '00:11:22:33:44:02' + link_params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd_iface, link_params) wpas.set("sae_pwe", "1") @@ -873,10 +901,14 @@ def test_eht_all_links_rejected(dev, apdev, params): key_mgmt="SAE", pwe='2') link_params['channel'] = '1' link_params['bssid'] = '00:11:22:33:44:01' + link_params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, link_params) link_params['channel'] = '6' link_params['bssid'] = '00:11:22:33:44:02' + link_params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd_iface, link_params) wpas.set("mld_connect_bssid_pref", "00:11:22:33:44:01") wpas.set("sae_pwe", "1") @@ -916,10 +948,13 @@ def test_eht_connect_invalid_link(dev, apdev, params): key_mgmt="SAE", pwe='2') link_params['channel'] = '1' link_params['bssid'] = '00:11:22:33:44:01' + link_params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, link_params) link_params['channel'] = '6' link_params['bssid'] = '00:11:22:33:44:02' + link_params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd_iface, link_params) # We scan for both APs, then try to connect to link 0, but only the @@ -951,9 +986,12 @@ def test_eht_mld_link_removal(dev, apdev): ssid = "mld_ap_owe_two_link" params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd0_iface, params) wpas.connect(ssid, scan_freq="2412 2437", key_mgmt="OWE", @@ -1003,10 +1041,12 @@ def test_eht_mld_bss_trans_mgmt_link_removal_imminent(dev, apdev): params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") params["bss_transition"] = "1" params["mbo"] = "1" + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd0_iface, params) @@ -1055,10 +1095,12 @@ def test_eht_ap_mld_proto(dev, apdev): ssid = "mld_ap_owe_two_link" params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' + params['link_id'] = '1' hapd1 = eht_mld_enable_ap(hapd0_iface, params) @@ -1443,10 +1485,14 @@ def test_eht_mld_gas(dev, apdev): params['venue_group'] = "7" params['venue_type'] = "1" params['venue_name'] = "eng:Example venue" + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd0_iface, params) bssid0 = hapd0.own_addr() params['channel'] = '6' + params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd0_iface, params) bssid1 = hapd1.own_addr() @@ -1479,9 +1525,13 @@ def test_eht_mld_dpp_responder_while_assoc(dev, apdev): ssid = "owe_two_link" params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' + params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd0_iface, params) wpas.connect(ssid, scan_freq="2412 2437", key_mgmt="OWE", @@ -1507,9 +1557,13 @@ def _eht_mld_disconnect(dev, apdev, disassoc=True): ssid = "mld_ap_owe_two_link" params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd0_iface, params) params['channel'] = '6' + params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd0_iface, params) wpas.connect(ssid, scan_freq="2412 2437", key_mgmt="OWE", @@ -1564,6 +1618,7 @@ def test_eht_mld_non_pref_chan(dev, apdev): params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2") params["bss_transition"] = "1" params["mbo"] = "1" + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd0_iface, params) @@ -1652,6 +1707,7 @@ def test_eht_mld_rrm_beacon_req(dev, apdev): params["bss_transition"] = "1" params["mbo"] = "1" params["rrm_beacon_report"] = "1" + params['link_id'] = '0' hapd0 = eht_mld_enable_ap(hapd0_iface, params) @@ -1667,6 +1723,8 @@ def test_eht_mld_rrm_beacon_req(dev, apdev): other_ssid = "other" params = eht_mld_ap_wpa2_params(other_ssid, key_mgmt="OWE", mfp="2") params["channel"] = '6' + params['link_id'] = '0' + hapd1 = eht_mld_enable_ap(hapd1_iface, params) # Issue a beacon request for the second AP @@ -1706,6 +1764,8 @@ def test_eht_mld_legacy_stas(dev, apdev): mfp="2", pwe='2') params['rsn_pairwise'] = "CCMP GCMP-256" params['sae_groups'] = "19 20" + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, params) for i in range(3): @@ -1745,6 +1805,8 @@ def test_eht_mld_and_mlds(dev, apdev): mfp="2", pwe='2') params['rsn_pairwise'] = "CCMP GCMP-256" params['sae_groups'] = "19 20" + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, params) wpas.set("sae_pwe", "1") @@ -1810,9 +1872,13 @@ def test_eht_mlo_csa(dev, apdev): params = eht_mld_ap_wpa2_params(ssid, passphrase, key_mgmt="SAE", mfp="2", pwe='1') + params['link_id'] = '0' + hapd0 = eht_mld_enable_ap(hapd_iface, params) params['channel'] = '6' + params['link_id'] = '1' + hapd1 = eht_mld_enable_ap(hapd_iface, params) wpas.set("sae_pwe", "1") -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:28 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:28 +0530 Subject: [PATCH 6/6] tests: MLO: add MLD socket connectivity test case In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-7-quic_adisi@quicinc.com> Add simple test case to bring up a 2 link MLD and get the status of each link via the MLD level socket. Signed-off-by: Aditya Kumar Singh --- tests/hwsim/mld.py | 36 ++++++++++++++++++++++++++++++++++++ tests/hwsim/test_eht.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/hwsim/mld.py diff --git a/tests/hwsim/mld.py b/tests/hwsim/mld.py new file mode 100644 index 000000000000..af6695e601df --- /dev/null +++ b/tests/hwsim/mld.py @@ -0,0 +1,36 @@ +# Python class for controlling Multi Link Device +# Copyright (c) 2024, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import logging +import wpaspy + +logger = logging.getLogger() +hapd_ctrl = '/var/run/hostapd' + +class Multi_Link_Device: + def __init__(self, ifname, ctrl=hapd_ctrl, port=8877): + self.ifname = ifname + self.ctrl = wpaspy.Ctrl(os.path.join(ctrl, ifname)) + self.dbg = ifname + + def close_ctrl(self): + self.ctrl.close() + self.ctrl = None + + def request(self, cmd): + logger.debug(self.dbg + ": MLD CTRL: " + cmd) + return self.ctrl.request(cmd) + + def ping(self): + return "PONG" in self.request("PING") + +def get_mld_obj(ifname, ctrl=hapd_ctrl, port=8877): + mld = Multi_Link_Device(ifname, ctrl, port) + if not mld.ping(): + raise Exception("Could not ping MLD %s" % ifname) + + return mld diff --git a/tests/hwsim/test_eht.py b/tests/hwsim/test_eht.py index 8acb6a57bc7b..6b44dad3c09f 100644 --- a/tests/hwsim/test_eht.py +++ b/tests/hwsim/test_eht.py @@ -15,6 +15,7 @@ from tshark import run_tshark from test_gas import hs20_ap_params from test_dpp import check_dpp_capab, wait_auth_success from test_rrm import build_beacon_request, run_req_beacon, BeaconReport +import mld def eht_verify_wifi_version(dev): status = dev.get_status() @@ -1905,3 +1906,36 @@ def test_eht_mlo_csa(dev, apdev): traffic_test(wpas, hapd0) #TODO: CSA on non-first link + +def test_eht_mld_socket_connectivity(dev, apdev): + """EHT MLD Socket Connectivity""" + with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \ + HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface): + + ssid = "mld_ap" + link0_params = {"ssid": ssid, + "hw_mode": "g", + "channel": "1", + "link_id": "0"} + link1_params = {"ssid": ssid, + "hw_mode": "g", + "channel": "2", + "link_id": "1"} + + hapd0 = eht_mld_enable_ap(hapd_iface, link0_params) + hapd1 = eht_mld_enable_ap(hapd_iface, link1_params) + + mld_dev = mld.get_mld_obj(hapd_iface) + + # Check status of each link + res = str(mld_dev.request("STATUS LINKID 0")) + if "state" not in res: + raise Exception("Failed to get link 0 status via MLD socket") + + logger.info("LINK 0 STATUS: \n" + res) + + res = mld_dev.request("STATUS LINKID 1") + if "state" not in res: + raise Exception("Failed to get link 1 status via MLD socket") + + logger.info("LINK 1 STATUS: \n" + res) -- 2.34.1 From quic_adisi at quicinc.com Mon Apr 22 22:31:24 2024 From: quic_adisi at quicinc.com (Aditya Kumar Singh) Date: Tue, 23 Apr 2024 11:01:24 +0530 Subject: [PATCH 2/6] ctrl_iface: MLO: introduce MLD level socket In-Reply-To: <20240423053128.129322-1-quic_adisi@quicinc.com> References: <20240423053128.129322-1-quic_adisi@quicinc.com> Message-ID: <20240423053128.129322-3-quic_adisi@quicinc.com> With MLO, each link have socket created with "_link" under the control interface directory. Introduce a MLD level socket - "" as well under the same control interface directory. This socket can be used to pass the command to its partner links directly instead of using the link level socket. Link ID needs to be passed with the command. The structure of the command is - " LINKID " Directory looks something like this - $ ls /var/run/hostapd/ wlan0 wlan0_link0 wlan0_link1 wlan0 here is the MLD level socket. Rest are each link level. This would also help to maintain backwards compatibility with applications which looks for under the control interface directory.` Signed-off-by: Aditya Kumar Singh --- hostapd/ctrl_iface.c | 335 +++++++++++++++++++++++++++++++++++++++++++ hostapd/ctrl_iface.h | 4 + hostapd/main.c | 5 + src/ap/hostapd.c | 11 ++ src/ap/hostapd.h | 6 + 5 files changed, 361 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index d4bb8a055ef2..4654766b4014 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -4569,6 +4569,341 @@ done: } +#ifdef CONFIG_IEEE80211BE +#ifndef CONFIG_CTRL_IFACE_UDP +static int hostapd_mld_ctrl_iface_attach(struct hostapd_mld *mld, + struct sockaddr_storage *from, + socklen_t fromlen, const char *input) +{ + return ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, input); +} + + +static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + return ctrl_iface_detach(&mld->ctrl_dst, from, fromlen); +} + + +static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld, + char *buf, char *reply, + int reply_size, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct hostapd_data *link_hapd, *link_itr; + int reply_len, link_id = -1; + char *link_cmd; + bool found = false; + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + /* Check if link id is provided in the command or not */ + link_cmd = os_strstr(buf, "LINKID"); + if (link_cmd) { + /* Trim the link id part now */ + *(link_cmd - 1) = '\0'; + + link_cmd += 7; + link_id = atoi(link_cmd); + + if (link_id < 0 || link_id >= 15) { + os_memcpy(reply, "INVALID LINK ID\n", 16); + reply_len = 16; + return reply_len; + } + + link_hapd = mld->fbss; + if (!link_hapd) { + os_memcpy(reply, "NO LINKS ACTIVE\n", 16); + reply_len = 16; + return reply_len; + } + + for_each_mld_link(link_itr, link_hapd) { + if (link_itr->mld_link_id == link_id) { + found = true; + break; + } + } + + if (!found) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + return reply_len; + } + + link_hapd = link_itr; + } else { + link_hapd = mld->fbss; + } + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, buf + 7)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen)) + reply_len = -1; + } else { + if (link_id == -1) + wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)"); + + if (!link_hapd) + reply_len = -1; + else + reply_len = + hostapd_ctrl_iface_receive_process(link_hapd, buf, + reply, reply_size, + from, fromlen); + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + return reply_len; +} + + +static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_mld *mld = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply, *pos = buf; + const int reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + + wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res); + + reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos, + reply, reply_size, + &from, fromlen); + + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + os_free(reply); +} + + +static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld) +{ + char *buf; + size_t len; + + if (!mld->ctrl_interface) + return NULL; + + len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2; + + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name); + buf[len - 1] = '\0'; + return buf; +} +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (!mld) + return -1; + + if (mld->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!", + mld->name); + return 0; + } + + dl_list_init(&mld->ctrl_dst); + + if (mld->ctrl_interface == NULL) + return 0; + + if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + } + + if (os_strlen(mld->ctrl_interface) + 1 + + os_strlen(mld->name) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (fname == NULL) + goto fail; + + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name); + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, + "hostapd-ctrl-iface: bind(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + os_free(fname); + + mld->ctrl_sock = s; + + if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld, + NULL) < 0) + return -1; + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + return 0; +} + + +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct wpa_ctrl_dst *dst, *prev; + + if (mld->ctrl_sock > -1) { + char *fname; + eloop_unregister_read_sock(mld->ctrl_sock); + close(mld->ctrl_sock); + mld->ctrl_sock = -1; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (fname) + unlink(fname); + os_free(fname); + + if (mld->ctrl_interface && + rmdir(mld->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "MLD Control interface " + "directory not empty - leaving it " + "behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + mld->ctrl_interface, + strerror(errno)); + } + } + } + + dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + os_free(mld->ctrl_interface); +} +#endif /* CONFIG_IEEE80211BE */ + + #ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h index 3341a66bdc6c..ec5a95be785c 100644 --- a/hostapd/ctrl_iface.h +++ b/hostapd/ctrl_iface.h @@ -14,6 +14,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); +#ifdef CONFIG_IEEE80211BE +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld); +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld); +#endif /* CONFIG_IEEE80211BE */ #else /* CONFIG_NO_CTRL_IFACE */ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { diff --git a/hostapd/main.c b/hostapd/main.c index 58cc86a08c1f..a0bca3dd5dd0 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -748,6 +748,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces) if (!interfaces->mld[i]) continue; + interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]); os_free(interfaces->mld[i]); interfaces->mld[i] = NULL; } @@ -793,6 +794,10 @@ int main(int argc, char *argv[]) interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; +#ifdef CONFIG_IEEE80211BE + interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init; + interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit; +#endif /* CONFIG_IEEE80211BE */ dl_list_init(&interfaces.global_ctrl_dst); #ifdef CONFIG_ETH_P_OUI dl_list_init(&interfaces.eth_p_oui); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index ebdf445831ab..9cd492752e99 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -3060,9 +3060,18 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, os_strlcpy(mld->name, conf->iface, sizeof(conf->iface)); dl_list_init(&mld->links); + mld->ctrl_sock = -1; + mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface); wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name); + /* + * Initialize MLD control interfaces early to allow external monitoring of + * link setup operations. + */ + if (interfaces->mld_ctrl_iface_init(mld)) + goto fail; + hapd->mld = mld; hostapd_mld_ref_inc(mld); hostapd_bss_alloc_link_id(hapd); @@ -3122,6 +3131,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces) if (!remove && !forced_remove) continue; + interfaces->mld_ctrl_iface_deinit(mld); + wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name, forced_remove ? " (forced)" : ""); os_free(mld); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 4b87a8b76262..052e8f072255 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -97,6 +97,8 @@ struct hapd_interfaces { #ifdef CONFIG_IEEE80211BE struct hostapd_mld **mld; size_t mld_count; + int (*mld_ctrl_iface_init)(struct hostapd_mld *mld); + void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld); #endif /* CONFIG_IEEE80211BE */ }; @@ -519,6 +521,10 @@ struct hostapd_mld { struct hostapd_data *fbss; struct dl_list links; /* List head of all affiliated links */ + + int ctrl_sock; + struct dl_list ctrl_dst; + char *ctrl_interface; /* directory for UNIX domain sockets */ }; #define HOSTAPD_MLD_MAX_REF_COUNT 0xFF -- 2.34.1 From marek at mazekey.pl Tue Apr 23 02:08:08 2024 From: marek at mazekey.pl (marek at mazekey.pl) Date: Tue, 23 Apr 2024 11:08:08 +0200 Subject: [PATCH] hostapd: fix opclass during CSA with DFS channels Message-ID: During CSA with DFS channels, disable, enable interface is a part of the algorithm. When interface was enabled old operating class before switch and new channel were used causing mismatch in configured_fixed_chan_to_freq function. Example of log when switch from channel 157 to 108 was triggered: "Could not convert op_class 124 channel 108 to operating frequency" Fixes: bb781c763 ("AP: Populate iface->freq before starting AP") Signed-off-by: Marek Kwaczynski --- src/ap/hostapd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 130b6ebc6..8d0601e6a 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4466,6 +4466,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, { u8 seg0_idx = 0, seg1_idx = 0; enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT; + u8 op_class; + u8 chan; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); @@ -4505,6 +4507,11 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, iface->freq = freq_params->freq; iface->conf->channel = freq_params->channel; iface->conf->secondary_channel = freq_params->sec_channel_offset; + ieee80211_freq_to_channel_ext(freq_params->freq, freq_params->sec_channel_offset, bw, &op_class, &chan); + if (chan != freq_params->channel) + wpa_printf(MSG_ERROR, "Channel mismatch: %d -> %d", freq_params->channel, chan); + + iface->conf->op_class = op_class; hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx); hostapd_set_oper_chwidth(iface->conf, bw); -- 2.34.1 From j at w1.fi Wed Apr 24 08:15:07 2024 From: j at w1.fi (Jouni Malinen) Date: Wed, 24 Apr 2024 18:15:07 +0300 Subject: [PATCH] hostapd: fix opclass during CSA with DFS channels In-Reply-To: References: Message-ID: On Tue, Apr 23, 2024 at 11:08:08AM +0200, marek at mazekey.pl wrote: > During CSA with DFS channels, disable, enable interface > is a part of the algorithm. > When interface was enabled old operating class before switch > and new channel were used causing mismatch in > configured_fixed_chan_to_freq function. > > Example of log when switch from channel 157 to 108 was triggered: > "Could not convert op_class 124 channel 108 to operating frequency" Could you please provide full hostapd configuration that can trigger this and a debug log showing this? In particular, I'd like to understand which country code was used. This did not seem to show any issues in a hwsim test setup with country code US, and I'd like to be able to add a regression test case with this type of fixes. -- Jouni Malinen PGP id EFC895FA From j at w1.fi Wed Apr 24 12:32:33 2024 From: j at w1.fi (Jouni Malinen) Date: Wed, 24 Apr 2024 22:32:33 +0300 Subject: [PATCH v2 0/2] add co-hosted MLD test cases In-Reply-To: <20240422041239.1823537-1-quic_adisi@quicinc.com> References: <20240422041239.1823537-1-quic_adisi@quicinc.com> Message-ID: On Mon, Apr 22, 2024 at 09:42:37AM +0530, Aditya Kumar Singh wrote: > Add test cases to test basic co-hosted MLDs functionality. > > These were previously part of the series - > * [PATCH 00/22] hostapd: add cohosted MLO support > at [21/22] and [22/22] respectively. > > However, the code changes are merged and only test case was pending due to > wireless tree dependency. Now that is also resolved, have re-spin a v2 > with only test cases changes in it. > > Aditya Kumar Singh (2): > tests: MLO: add basic cohosted MLDs functionality testing > tests: MLO: add cohosted MLDs connectivity testing Thanks, applied. -- Jouni Malinen PGP id EFC895FA From nico.escande at gmail.com Fri Apr 26 05:35:53 2024 From: nico.escande at gmail.com (Nicolas Escande) Date: Fri, 26 Apr 2024 14:35:53 +0200 Subject: [PATCH] nl80211: fix AP scan with STA fallback error path Message-ID: <20240426123603.2838736-1-nico.escande@gmail.com> I've run into a case where I can bring a 2.4GHz AP up whithout it being in a bridge while I used the 'bridge=' config option. This happens when the HT scan needed for 40MHz operation failed to start from the get go because another dev on the same phy was already in a scan. At the end the AP is up and running but not enslaved to any bridge. Upon looking at the code, It seems that some hardware fails to issue a scan while in AP and thus we have a fallback that switches the interface from AP to STA before retrying another scan (it will change it back to AP later on when finished). As we cannot have a (non wds) STA in a bridge, during that procedure, we also remove/add the AP/STA from/to the bridge as needed. However, in wpa_driver_nl80211_scan() we do not set drv->ap_scan_as_station until the end of the switch-to-sta & retry-scan-start block. This means that when the recursive call to wpa_driver_nl80211_scan() fails (the hardware is busy in my case) we restore the interface to AP with wpa_driver_nl80211_set_mode() but that will not add it back to the bridge. Problem. To Fix this lets always set drv->ap_scan_as_station before calling wpa_driver_nl80211_set_mode(). In case wpa_driver_nl80211_set_mode() or wpa_driver_nl80211_scan() fails lets call nl80211_restore_ap_mode() that will set the mode back to AP but also handle the bridge thing as needed. Signed-off-by: Nicolas Escande --- src/drivers/driver_nl80211_scan.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 1eb437405..621667c4f 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -402,23 +402,18 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); if (drv->hostapd && is_ap_interface(drv->nlmode)) { - enum nl80211_iftype old_mode = drv->nlmode; - /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ + drv->ap_scan_as_station = drv->nlmode; if (wpa_driver_nl80211_set_mode( - bss, NL80211_IFTYPE_STATION)) - goto fail; - - if (wpa_driver_nl80211_scan(bss, params)) { - wpa_driver_nl80211_set_mode(bss, old_mode); + bss, NL80211_IFTYPE_STATION) || + wpa_driver_nl80211_scan(bss, params)) { + nl80211_restore_ap_mode(bss); goto fail; } - /* Restore AP mode when processing scan results */ - drv->ap_scan_as_station = old_mode; ret = 0; } else goto fail; -- 2.44.0 From marek at mazekey.pl Fri Apr 26 15:11:18 2024 From: marek at mazekey.pl (marek at mazekey.pl) Date: Sat, 27 Apr 2024 00:11:18 +0200 Subject: [PATCH] hostapd: fix opclass during CSA with DFS channels In-Reply-To: References: Message-ID: <3cab177340659fdc6e30cc6a693d3d4b@mazekey.pl> We can easy reproduce it when switch to non DFS channel, then to DFS channel with different opclass, country code US. Examples: a) - CSA to channel 36 - DFS CSA to channel 100 Could not convert op_class 115 channel 100 to operating frequency b) - CSA to channel 157 - DFS CSA to channel 56 Could not convert op_class 124 channel 56 to operating frequency You can reproduce it with hwsim with small modification: From 5edc4a0a21ca677fc142857a349ba2a94b57ca2a Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Sat, 27 Apr 2024 00:01:06 +0200 Subject: [PATCH] tests: hwsim: check CSA between non DFS and DFS chan Add test to validate CSA between non DFS channel and DFS channel with different operating class. Signed-off-by: Marek Kwaczynski --- tests/hwsim/test_dfs.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/hwsim/test_dfs.py b/tests/hwsim/test_dfs.py index d0bae5a66..3be64b147 100644 --- a/tests/hwsim/test_dfs.py +++ b/tests/hwsim/test_dfs.py @@ -640,7 +640,7 @@ def test_dfs_chan_switch(dev, apdev): if freq != "5260": raise Exception("Unexpected frequency") - dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5260 5280") + dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5260 5280 5180 5500") dev[0].wait_regdom(country_ie=True) hwsim_utils.test_connectivity(dev[0], hapd) @@ -666,6 +666,39 @@ def test_dfs_chan_switch(dev, apdev): dev[0].wait_connected(timeout=30) hwsim_utils.test_connectivity(dev[0], hapd) + + # Move to non DFS channel: 36, opclass 115 + if "OK" not in hapd.request("CHAN_SWITCH 5 5180"): + raise Exception("CHAN_SWITCH failed") + ev = wait_dfs_event(hapd, "AP-CSA-FINISHED", 5) + + if "freq=5180" not in ev: + raise Exception("Unexpected channel: " + ev) + hwsim_utils.test_connectivity(dev[0], hapd) + + # Move to channel 100, opclass 121 + if "OK" not in hapd.request("CHAN_SWITCH 5 5500 ht"): + raise Exception("CHAN_SWITCH failed") + # This results in BSS going down before restart, so the STA is expected + # to report disconnection. + dev[0].wait_disconnected() + ev = wait_dfs_event(hapd, "DFS-CAC-START", 5) + if "freq=5500" not in ev: + raise Exception("Unexpected channel: " + ev) + ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70) + if "success=1" not in ev: + raise Exception("CAC failed") + if "freq=5500" not in ev: + raise Exception("Unexpected DFS freq result") + ev = hapd.wait_event(["AP-ENABLED"], timeout=5) + if not ev: + raise Exception("AP setup timed out") + freq = hapd.get_status_field("freq") + if freq != "5500": + raise Exception("Unexpected frequency") + + dev[0].wait_connected(timeout=30) + hwsim_utils.test_connectivity(dev[0], hapd) finally: clear_regdom(hapd, dev) -- 2.34.1 From m at xv97.com Sat Apr 27 01:19:45 2024 From: m at xv97.com (Chien Wong) Date: Sat, 27 Apr 2024 16:19:45 +0800 Subject: [PATCH 3/4] tests: NAN: Check USD publish type In-Reply-To: <20240427081946.147826-1-m@xv97.com> References: <20240427081946.147826-1-m@xv97.com> Message-ID: <20240427081946.147826-3-m@xv97.com> Signed-off-by: Chien Wong --- tests/hwsim/test_nan_usd.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/hwsim/test_nan_usd.py b/tests/hwsim/test_nan_usd.py index 05cf1392c..356d33419 100644 --- a/tests/hwsim/test_nan_usd.py +++ b/tests/hwsim/test_nan_usd.py @@ -17,6 +17,16 @@ def check_nan_usd_capab(dev): if "USD" not in capa: raise HwsimSkip("NAN USD not supported") +def test_nan_usd_publish_invalid_param(dev): + """NAN USD Publish with invalid parameters""" + check_nan_usd_capab(dev[0]) + + # Both solicited and unsolicited disabled is invalid + cmd = "NAN_PUBLISH service_name=_test solicited=0 unsolicited=0" + id0 = dev[0].request(cmd) + if "FAIL" not in id0: + raise Exception("NAN_PUBLISH accepts both solicited=0 and unsolicited=0") + def test_nan_usd_publish(dev, apdev): """NAN USD Publish""" check_nan_usd_capab(dev[0]) -- 2.44.0 From m at xv97.com Sat Apr 27 01:19:43 2024 From: m at xv97.com (Chien Wong) Date: Sat, 27 Apr 2024 16:19:43 +0800 Subject: [PATCH 1/4] NAN: Fix a typo in USD doc Message-ID: <20240427081946.147826-1-m@xv97.com> The 'req_instance' parameter in transmit command should be 'req_instance_id'. Fixes: e3f9ab3c3a01 ("NAN: USD in wpa_supplicant") Signed-off-by: Chien Wong --- wpa_supplicant/README-NAN-USD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wpa_supplicant/README-NAN-USD b/wpa_supplicant/README-NAN-USD index 5dfe6eeed..72c379fc9 100644 --- a/wpa_supplicant/README-NAN-USD +++ b/wpa_supplicant/README-NAN-USD @@ -75,7 +75,7 @@ NAN_CANCEL_SUBSCRIBE subscribe_id= This command maps to the CancelSubscribe() method in the NAN Discovery Engine. -NAN_TRANSMIT handle= req_instance= address= [ssi=] +NAN_TRANSMIT handle= req_instance_id= address= [ssi=] This command maps to the Transmit() method in the NAN Discovery Engine. -- 2.44.0 From m at xv97.com Sat Apr 27 01:19:46 2024 From: m at xv97.com (Chien Wong) Date: Sat, 27 Apr 2024 16:19:46 +0800 Subject: [PATCH 4/4] NAN: Register multicast action frames if possible In-Reply-To: <20240427081946.147826-1-m@xv97.com> References: <20240427081946.147826-1-m@xv97.com> Message-ID: <20240427081946.147826-4-m@xv97.com> The USD passive subscriber and solicited transmission only publisher require receiving multicast NAN action frames in order to work. Currently, we are not requesting to receive multicast when registering NAN action frames. As a result, USD passive subscribe or solicited only publish may not work. The NL80211_ATTR_RECEIVE_MULTICAST attribute corresponds to wiphy ext feature NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS, which requires driver support and only a few drivers in the kernel tree support it. Namely ath9k, ath9k_htc, ath10k and hwsim. We should fall back to register non multicast action frames if the driver has no support. It was confirmed that ath9k_htc starts to work after the patch. Note that even without requesting to receive multicast action frames, some drivers would still upload them. For example, rtl8192cu and hwsim. This is why test cases like test_nan_usd_match would not fail. Tested-on: TP-LINK TL-WN821N v3(AR7010+AR9287, ath9k_htc) Signed-off-by: Chien Wong --- src/drivers/driver_nl80211.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 149f51a74..5be0268c6 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2476,12 +2476,20 @@ static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) } -static int nl80211_register_action_frame(struct i802_bss *bss, - const u8 *match, size_t match_len) +static int nl80211_register_action_frame2(struct i802_bss *bss, + const u8 *match, size_t match_len, + bool multicast) { u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); return nl80211_register_frame(bss, bss->nl_mgmt, - type, match, match_len, false); + type, match, match_len, multicast); +} + + +static int nl80211_register_action_frame(struct i802_bss *bss, + const u8 *match, size_t match_len) +{ + return nl80211_register_action_frame2(bss, match, match_len, false); } @@ -2557,11 +2565,14 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_NAN_USD +#define NAN_PUB_ACTION ((u8 *) "\x04\x09\x50\x6f\x9a\x13") /* NAN SDF Public Action */ - if (nl80211_register_action_frame(bss, - (u8 *) "\x04\x09\x50\x6f\x9a\x13", - 6) < 0) - ret = -1; + if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6, true) < 0) { + /* fallback to non multicast */ + if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6, false) < 0) + ret = -1; + } +#undef NAN_PUB_ACTION #endif /* CONFIG_NAN_USD */ #ifdef CONFIG_DPP /* DPP Public Action */ -- 2.44.0 From m at xv97.com Sat Apr 27 01:19:44 2024 From: m at xv97.com (Chien Wong) Date: Sat, 27 Apr 2024 16:19:44 +0800 Subject: [PATCH 2/4] NAN: Reject undefined publish type In-Reply-To: <20240427081946.147826-1-m@xv97.com> References: <20240427081946.147826-1-m@xv97.com> Message-ID: <20240427081946.147826-2-m@xv97.com> The Wi-Fi Aware Specification v4.0 only defines unsolicited transmissions only, solicited transmissions only and both unsolicited and solicited transmissions publish. The other possibility is undefined so we should reject it. Signed-off-by: Chien Wong --- src/common/nan_de.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/nan_de.c b/src/common/nan_de.c index e1999a06b..7d8a952df 100644 --- a/src/common/nan_de.c +++ b/src/common/nan_de.c @@ -1209,6 +1209,11 @@ int nan_de_publish(struct nan_de *de, const char *service_name, return -1; } + if (!params->unsolicited && !params->solicited) { + wpa_printf(MSG_INFO, "NAN: Publish() - both unsolicited and solicited disabled is invalid"); + return -1; + } + publish_id = nan_de_get_handle(de); if (publish_id < 1) return -1; -- 2.44.0 From mail at david-bauer.net Sun Apr 28 06:13:39 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:39 +0200 Subject: [PATCH v2 0/5] wpa_supplicant: optimize OWE roaming behavior Message-ID: <20240428131344.334314-1-mail@david-bauer.net> This patchset aims for improving roaming when connected to an OWE transition-mode enabled network. Currently wpa_supplicant has the following shortcomings: The OWE transition-SSID is not added to the SSIDs scanned when a bgscan is triggered. This leads to a device being stuck on the current connection and not roaming to a better AP when the scan is triggered based on RSSI thresholds. OWE transition networks are added multiple times to the scan list (multiple entries for the same BSSID). This is due to the SSID being available for the scan-result when explicitly scanned for on connection establishment and not being available when scanned non-explicitly from the background. Forcing explicit roams by using wpa_cli does not work for OWE transition networks, as wpa_supplicant reports a mismatch in security-types. Changes from v1: - Improve scanning behavior when connected to a 6GHz OWE network with transition mode on 2.4GHz and 5GHz by storing the last-seen hidden SSID for the network. - Restore wpa_cli "roam" interface behavior to the one prior this patchset when compiled without OWE support. David Bauer (5): wpa_supplicant: reduce OWE transition network code duplication ctrl: enable roaming between OWE APs bss: don't add hidden OWE transition-networks to scan-list bgscan: add OWE transition SSID to OWE network scan scan: stored last-seen transition SSID wpa_supplicant/bgscan_learn.c | 4 ++ wpa_supplicant/bgscan_simple.c | 3 + wpa_supplicant/bss.c | 109 +++++++++++++++++++++++++++++- wpa_supplicant/bss.h | 4 ++ wpa_supplicant/ctrl_iface.c | 2 +- wpa_supplicant/events.c | 25 +++---- wpa_supplicant/scan.c | 38 +++++------ wpa_supplicant/scan.h | 3 + wpa_supplicant/wpa_supplicant.c | 17 ++--- wpa_supplicant/wpa_supplicant_i.h | 2 + 10 files changed, 157 insertions(+), 50 deletions(-) -- 2.43.0 From mail at david-bauer.net Sun Apr 28 06:13:44 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:44 +0200 Subject: [PATCH v2 5/5] scan: stored last-seen transition SSID In-Reply-To: <20240428131344.334314-1-mail@david-bauer.net> References: <20240428131344.334314-1-mail@david-bauer.net> Message-ID: <20240428131344.334314-6-mail@david-bauer.net> Store the last seen transition SSID when conneted to an OWE network enabling transition-mode. This speeds up discovery of hidden transition networks when connected on 6GHz. As 6 GHz does not use inter-band transition mode operation, the transition SSID name is missing from beacons on this band. When the scan-list times out, wpa_supplicant has to scan two consecutive times to first discover transition networks on 2.4 and 5GHz. Optimize this by storing the last transition-SSID independent from the scan-results. Signed-off-by: David Bauer --- wpa_supplicant/bss.c | 34 ++++++++++++++++++++++++++----- wpa_supplicant/events.c | 4 ++++ wpa_supplicant/scan.c | 12 +++++++++++ wpa_supplicant/wpa_supplicant_i.h | 2 ++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index b9bf06c45..7357e092e 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -909,7 +909,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time) { - const u8 *ssid, *p2p, *mesh, *owe, *rsn; + const u8 *ssid, *p2p, *mesh, *owe; struct wpa_bss *bss; if (wpa_s->conf->ignore_old_scan_res) { @@ -940,11 +940,35 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, return; } - /* Don't add hidden OWE transition networks with RSN. They are explicitly scanned for. */ - rsn = wpa_scan_get_ie(res, WLAN_EID_RSN); owe = wpa_scan_get_vendor_ie(res, OWE_IE_VENDOR_TYPE); - if (owe && rsn && (ssid[1] == 0 || ssid[2] == 0)) - return; + if (owe) { + const uint8_t *rsn; + + rsn = wpa_scan_get_ie(res, WLAN_EID_RSN); + + /** + * Don't add hidden OWE transition networks with RSN. + * They are explicitly scanned for. + */ + if (rsn && (ssid[1] == 0 || ssid[2] == 0)) + return; +#ifdef CONFIG_OWE + const uint8_t *owe_bssid, *owe_ssid; + size_t owe_ssid_len; + + /** + * If the network is an OWE transition network, store the transition SSID + * to allow scanning for it later when on a 6GHz network with expired + * scan results. + */ + if (!rsn && + !wpa_bss_get_owe_trans_network(wpa_s, owe, &owe_bssid, + &owe_ssid, &owe_ssid_len)) { + os_memcpy(wpa_s->owe_trans_ssid.ssid, owe_ssid, owe_ssid_len); + wpa_s->owe_trans_ssid.ssid_len = owe_ssid_len; + } +#endif /* CONFIG_OWE */ + } p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); #ifdef CONFIG_P2P diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index abeaefff1..e20eae549 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -383,6 +383,10 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->key_mgmt = 0; wpa_s->allowed_key_mgmts = 0; +#ifdef CONFIG_OWE + wpa_s->owe_trans_ssid.ssid_len = 0; +#endif + #ifndef CONFIG_NO_RRM wpas_rrm_reset(wpa_s); #endif /* CONFIG_NO_RRM */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 8a0387301..d9a7c9e10 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -976,6 +976,18 @@ void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, owe_ssid, owe_ssid_len); return; } + + /** + * Check if we can add a stored transition-mode SSID. + * This is helpful if the scan-list only contains 6 GHz networks + * which do not have a transition SSID. + */ + if (wpa_s->owe_trans_ssid.ssid_len == 0) + return; + + wpa_add_scan_ssid(wpa_s, params, max_ssids, + wpa_s->owe_trans_ssid.ssid, + wpa_s->owe_trans_ssid.ssid_len); #endif /* CONFIG_OWE */ } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 426d077d2..c785f0d04 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1602,6 +1602,8 @@ struct wpa_supplicant { /* An array of frequencies to scan for OWE transition mode BSSs when * owe_transition_search == 1 */ int *owe_trans_scan_freq; + /* The last transition-SSID seen if operating in transition-mode */ + struct wpa_ssid_value owe_trans_ssid; #endif /* CONFIG_OWE */ #ifdef CONFIG_NAN_USD -- 2.43.0 From mail at david-bauer.net Sun Apr 28 06:13:43 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:43 +0200 Subject: [PATCH v2 4/5] bgscan: add OWE transition SSID to OWE network scan In-Reply-To: <20240428131344.334314-1-mail@david-bauer.net> References: <20240428131344.334314-1-mail@david-bauer.net> Message-ID: <20240428131344.334314-5-mail@david-bauer.net> Add transition SSIDs to the SSID scan-list. This is currently missing and bgscan fails to scan for SSIDs on the transition network if one is currently used. Signed-off-by: David Bauer --- wpa_supplicant/bgscan_learn.c | 4 ++++ wpa_supplicant/bgscan_simple.c | 3 +++ wpa_supplicant/scan.c | 6 +++--- wpa_supplicant/scan.h | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index cab4ae2ac..922fe5440 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -280,6 +280,10 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) params.num_ssids = 1; params.ssids[0].ssid = data->ssid->ssid; params.ssids[0].ssid_len = data->ssid->ssid_len; + + /* Add transition mode SSID of current network */ + wpa_add_owe_scan_ssid(wpa_s, ¶ms, data->ssid, wpa_s->max_scan_ssids - params.num_ssids); + if (data->ssid->scan_freq) params.freqs = data->ssid->scan_freq; else { diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c index a90cf86e5..96e5e5a40 100644 --- a/wpa_supplicant/bgscan_simple.c +++ b/wpa_supplicant/bgscan_simple.c @@ -89,6 +89,9 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) params.ssids[0].ssid_len = data->ssid->ssid_len; params.freqs = data->ssid->scan_freq; + /* Add transition mode SSID of current network */ + wpa_add_owe_scan_ssid(wpa_s, ¶ms, data->ssid, wpa_s->max_scan_ssids - params.num_ssids); + /* * A more advanced bgscan module would learn about most like channels * over time and request scans only for some channels (probing others diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 52fe89746..8a0387301 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -940,9 +940,9 @@ static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s, } -static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - struct wpa_ssid *ssid, size_t max_ssids) +void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + const struct wpa_ssid *ssid, size_t max_ssids) { #ifdef CONFIG_OWE struct wpa_bss *bss; diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index d4c06c1ae..7ea99928c 100644 --- a/wpa_supplicant/scan.h +++ b/wpa_supplicant/scan.h @@ -104,5 +104,8 @@ int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len, enum chan_width cw); int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len, enum chan_width max_cw, int snr); +void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + const struct wpa_ssid *ssid, size_t max_ssids); #endif /* SCAN_H */ -- 2.43.0 From mail at david-bauer.net Sun Apr 28 06:13:41 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:41 +0200 Subject: [PATCH v2 2/5] ctrl: enable roaming between OWE APs In-Reply-To: <20240428131344.334314-1-mail@david-bauer.net> References: <20240428131344.334314-1-mail@david-bauer.net> Message-ID: <20240428131344.334314-3-mail@david-bauer.net> This allows to use the roam command with wpa_cli to force roaming on a transition network. Previously, this was not possible, as the open SSID is stored for the connection profile. Add a new function to also return OWE transition networks if the profile SSID is set as the transition-ssid for the OWE RSN network. Signed-off-by: David Bauer --- wpa_supplicant/bss.c | 47 +++++++++++++++++++++++++++++++++++++ wpa_supplicant/bss.h | 2 ++ wpa_supplicant/ctrl_iface.c | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index c213d15ad..e528af280 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -273,6 +273,53 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, return NULL; } +/** + * wpa_bss_get_connection - Fetch a BSS table entry based on BSSID and SSID. + * This function is similar to wpa_bss_get() but it will also return OWE-TM + * encrypted networks which transition-element matches @ssid. + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID, or %NULL to match any BSSID + * @ssid: SSID + * @ssid_len: Length of @ssid + * Returns: Pointer to the BSS entry or %NULL if not found + */ +struct wpa_bss * wpa_bss_get_connection(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; +#ifdef CONFIG_OWE + const u8 *owe, *owe_bssid, *owe_ssid; + size_t owe_ssid_len; +#endif /* CONFIG_OWE */ + + if (bssid && !wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bssid && os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0) + continue; + + if (bss->ssid_len == ssid_len && + os_memcmp(bss->ssid, ssid, ssid_len) == 0) + return bss; + +#ifdef CONFIG_OWE + /* Check if OWE-TM element is present and matches the SSID */ + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe) + continue; + + if (wpa_bss_get_owe_trans_network(wpa_s, owe, &owe_bssid, &owe_ssid, + &owe_ssid_len)) + continue; + + if (owe_ssid_len == ssid_len && + os_memcmp(owe_ssid, ssid, ssid_len) == 0) + return bss; +#endif /* CONFIG_OWE */ + } + return NULL; +} + void calculate_update_time(const struct os_reltime *fetch_time, unsigned int age_ms, diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 8acedbce7..fa72d7406 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -165,6 +165,8 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s); void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age); struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid, size_t ssid_len); +struct wpa_bss * wpa_bss_get_connection(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *ssid, size_t ssid_len); struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index bc013ad99..b5cf16650 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -6011,7 +6011,7 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, return -1; } - bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); + bss = wpa_bss_get_connection(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); -- 2.43.0 From mail at david-bauer.net Sun Apr 28 06:13:42 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:42 +0200 Subject: [PATCH v2 3/5] bss: don't add hidden OWE transition-networks to scan-list In-Reply-To: <20240428131344.334314-1-mail@david-bauer.net> References: <20240428131344.334314-1-mail@david-bauer.net> Message-ID: <20240428131344.334314-4-mail@david-bauer.net> When adding these networks hidden, they get re-added for the same BSSID when scanning for the transition-SSID. Skip adding the OWE-SSIDs in case the SSID was not explicitly scanned for. Signed-off-by: David Bauer --- wpa_supplicant/bss.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index e528af280..b9bf06c45 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -909,7 +909,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time) { - const u8 *ssid, *p2p, *mesh; + const u8 *ssid, *p2p, *mesh, *owe, *rsn; struct wpa_bss *bss; if (wpa_s->conf->ignore_old_scan_res) { @@ -940,6 +940,12 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, return; } + /* Don't add hidden OWE transition networks with RSN. They are explicitly scanned for. */ + rsn = wpa_scan_get_ie(res, WLAN_EID_RSN); + owe = wpa_scan_get_vendor_ie(res, OWE_IE_VENDOR_TYPE); + if (owe && rsn && (ssid[1] == 0 || ssid[2] == 0)) + return; + p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); #ifdef CONFIG_P2P if (p2p == NULL && -- 2.43.0 From mail at david-bauer.net Sun Apr 28 06:13:40 2024 From: mail at david-bauer.net (David Bauer) Date: Sun, 28 Apr 2024 15:13:40 +0200 Subject: [PATCH v2 1/5] wpa_supplicant: reduce OWE transition network code duplication In-Reply-To: <20240428131344.334314-1-mail@david-bauer.net> References: <20240428131344.334314-1-mail@david-bauer.net> Message-ID: <20240428131344.334314-2-mail@david-bauer.net> Reduce the code-duplication for methods handling the OWE transition mode. This is required for adding bgscan support for transition networks. Signed-off-by: David Bauer --- wpa_supplicant/bss.c | 30 ++++++++++++++++++++++++++++++ wpa_supplicant/bss.h | 2 ++ wpa_supplicant/events.c | 21 +++++---------------- wpa_supplicant/scan.c | 20 +++----------------- wpa_supplicant/wpa_supplicant.c | 17 +++++------------ 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 289035310..c213d15ad 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -817,6 +817,36 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s) wpa_s->last_scan_res_used = 0; } +int wpa_bss_get_owe_trans_network(struct wpa_supplicant *wpa_s, const u8 *owe_ie, + const u8 **bssid, const u8 **ssid, size_t *ssid_len) +{ +#ifdef CONFIG_OWE + const u8 *pos, *end; + u8 ssid_len_tmp; + + if (!owe_ie) + return 1; + + pos = owe_ie + 6; + end = owe_ie + 2 + owe_ie[1]; + + if (end - pos < ETH_ALEN + 1) + return 1; + *bssid = pos; + pos += ETH_ALEN; + ssid_len_tmp = *pos++; + if (end - pos < ssid_len_tmp || ssid_len_tmp > SSID_MAX_LEN) + return 1; + + *ssid = pos; + *ssid_len = ssid_len_tmp; + + return 0; +#else /* CONFIG_OWE */ + return 1; +#endif /* CONFIG_OWE */ +} + /** * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index cc0496324..8acedbce7 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -185,6 +185,8 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, u32 vendor_type); int wpa_bss_get_max_rate(const struct wpa_bss *bss); int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); +int wpa_bss_get_owe_trans_network(struct wpa_supplicant *wpa_s, const u8 *owe_ie, + const u8 **bssid, const u8 **ssid, size_t *ssid_len); struct wpa_bss_anqp * wpa_bss_anqp_alloc(void); int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss); const u8 * wpa_bss_get_fils_cache_id(const struct wpa_bss *bss); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 8dc618e5c..abeaefff1 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1108,30 +1108,19 @@ static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 **ret_ssid, size_t *ret_ssid_len) { #ifdef CONFIG_OWE - const u8 *owe, *pos, *end, *bssid; - u8 ssid_len; + const u8 *owe, *bssid; owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN)) return; - pos = owe + 6; - end = owe + 2 + owe[1]; - - if (end - pos < ETH_ALEN + 1) - return; - bssid = pos; - pos += ETH_ALEN; - ssid_len = *pos++; - if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + if (wpa_bss_get_owe_trans_network(wpa_s, owe, &bssid, ret_ssid, ret_ssid_len)) return; /* Match the profile SSID against the OWE transition mode SSID on the * open network. */ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR - " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len)); - *ret_ssid = pos; - *ret_ssid_len = ssid_len; + " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(*ret_ssid, *ret_ssid_len)); if (!(bss->flags & WPA_BSS_OWE_TRANSITION)) { struct wpa_ssid *ssid; @@ -1139,8 +1128,8 @@ static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (wpas_network_disabled(wpa_s, ssid)) continue; - if (ssid->ssid_len == ssid_len && - os_memcmp(ssid->ssid, pos, ssid_len) == 0) { + if (ssid->ssid_len == *ret_ssid_len && + os_memcmp(ssid->ssid, ret_ssid, *ret_ssid_len) == 0) { /* OWE BSS in transition mode for a currently * enabled OWE network. */ wpa_dbg(wpa_s, MSG_DEBUG, diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 8b59e409b..52fe89746 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -954,8 +954,7 @@ static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - const u8 *owe, *pos, *end; - const u8 *owe_ssid; + const u8 *owe, *owe_bssid, *owe_ssid; size_t owe_ssid_len; if (bss->ssid_len != ssid->ssid_len || @@ -966,21 +965,8 @@ static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, if (!owe || owe[1] < 4) continue; - pos = owe + 6; - end = owe + 2 + owe[1]; - - /* Must include BSSID and ssid_len */ - if (end - pos < ETH_ALEN + 1) - return; - - /* Skip BSSID */ - pos += ETH_ALEN; - owe_ssid_len = *pos++; - owe_ssid = pos; - - if ((size_t) (end - pos) < owe_ssid_len || - owe_ssid_len > SSID_MAX_LEN) - return; + if (wpa_bss_get_owe_trans_network(wpa_s, owe, &owe_bssid, &owe_ssid, &owe_ssid_len)) + continue; wpa_printf(MSG_DEBUG, "OWE: scan_ssids: transition mode OWE ssid=%s", diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index eeb667515..06161a6dc 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -5276,8 +5276,8 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *entry_ssid, size_t entry_ssid_len) { - const u8 *owe, *pos, *end; - u8 ssid_len; + const u8 *owe, *owe_bssid, *owe_ssid; + size_t owe_ssid_len; struct wpa_bss *bss; /* Check network profile SSID aganst the SSID in the @@ -5291,18 +5291,11 @@ static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, if (!owe) return 0; - pos = owe + 6; - end = owe + 2 + owe[1]; - - if (end - pos < ETH_ALEN + 1) - return 0; - pos += ETH_ALEN; - ssid_len = *pos++; - if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + if (wpa_bss_get_owe_trans_network(wpa_s, owe, &owe_bssid, &owe_ssid, &owe_ssid_len)) return 0; - return entry_ssid_len == ssid_len && - os_memcmp(pos, entry_ssid, ssid_len) == 0; + return entry_ssid_len == owe_ssid_len && + os_memcmp(owe_ssid, entry_ssid, owe_ssid_len) == 0; } #endif /* CONFIG_OWE */ -- 2.43.0 From gasmibal at gmail.com Sun Apr 28 12:47:21 2024 From: gasmibal at gmail.com (Baligh Gasmi gasmibal@gmail.com) Date: Sun, 28 Apr 2024 21:47:21 +0200 Subject: [PATCH] multi-ap: honor wds_sta even with multi_ap Message-ID: <20240428194721.1307781-1-gasmibal@gmail.com> When multi_ap is active, hostapd creates an AP-VLAN interface for 4addr stations, even though wds_sta is not configured. A check should be added to determine if wds_sta is active before re-enabling the WDS mode by creating an AP-VLAN for 4addr stations. Signed-off-by: Baligh Gasmi --- src/ap/ieee802_11.c | 1 + src/ap/sta_info.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 54cff1038..3a32aab44 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -6561,6 +6561,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, if ((sta->flags & WLAN_STA_WDS) || (sta->flags & WLAN_STA_MULTI_AP && (hapd->conf->multi_ap & BACKHAUL_BSS) && + hapd->conf->wds_sta && !(sta->flags & WLAN_STA_WPS))) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 87d63e188..b2f6e834f 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -235,6 +235,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if ((sta->flags & WLAN_STA_WDS) || (sta->flags & WLAN_STA_MULTI_AP && (hapd->conf->multi_ap & BACKHAUL_BSS) && + hapd->conf->wds_sta && !(sta->flags & WLAN_STA_WPS))) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); -- 2.43.0 From nico.escande at gmail.com Mon Apr 29 03:13:25 2024 From: nico.escande at gmail.com (Nicolas Escande) Date: Mon, 29 Apr 2024 12:13:25 +0200 Subject: [PATCH] hostapd: respect obss_interval on 40MHz intolerant disconnect Message-ID: <20240429101405.99454-1-nico.escande@gmail.com> When an AP is configured for HT operation on 40MHz but has overlapping BSS on the secondary channel, it will fallback to 20MHz only. Also a 40MHz AP will downgrade to 20MHz while at least 1 40MHz intolerant STA is connected. However in the current code, on such an AP, we do not check the obss_interval parameter on the last 40MHz intolerant STA disconnect & start a timer that will unconditionnaly bring the AP to 40MHz operation even if it had overlapping BSS. Signed-off-by: Nicolas Escande --- src/ap/ieee802_11_ht.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index f90f1254e..af3ff9110 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -417,6 +417,7 @@ void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) iface->num_sta_ht40_intolerant--; if (iface->num_sta_ht40_intolerant == 0 && + iface->conf->obss_interval && (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:43 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:43 +0200 Subject: [PATCH 02/16] WNM: Store whether disassociation address is an MLD Address In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-3-benjamin@sipsolutions.net> From: Benjamin Berg Commit 17a2aa822c26 ("WNM: Follow BTM procedure if the last link is dropped") added code to store either the MLD address or BSSID when being diassociated. However, it did not save which one was stored making the tests later on awkward. Store whether it was an MLD address or not and then do the test accordingly. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 19 +++++++++++-------- wpa_supplicant/wpa_supplicant_i.h | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index a383ed803..015a1948e 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1495,12 +1495,17 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, /* The last link is being removed (which must be the assoc link) */ wpa_s->wnm_link_removal = true; - os_memcpy(wpa_s->wnm_dissoc_addr, + wpa_s->wnm_disassoc_mld = false; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, ETH_ALEN); + } else if (wpa_s->valid_links) { + wpa_s->wnm_disassoc_mld = true; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->ap_mld_addr, + ETH_ALEN); } else { - os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ? - wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN); + wpa_s->wnm_disassoc_mld = false; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN); } if (disassoc_imminent) { @@ -2066,13 +2071,11 @@ bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) * In case disassociation imminent is set, do no try to use a BSS to * which we are connected. */ - if (wpa_s->wnm_link_removal || - !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) || - is_zero_ether_addr(bss->mld_addr)) { - if (ether_addr_equal(bss->bssid, wpa_s->wnm_dissoc_addr)) + if (!wpa_s->wnm_disassoc_mld) { + if (ether_addr_equal(bss->bssid, wpa_s->wnm_disassoc_addr)) return true; } else { - if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_dissoc_addr)) + if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_disassoc_addr)) return true; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 6019fd1e1..d4a1284c3 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1306,7 +1306,8 @@ struct wpa_supplicant { u8 wnm_num_neighbor_report; u8 wnm_mode; bool wnm_link_removal; - u8 wnm_dissoc_addr[ETH_ALEN]; + bool wnm_disassoc_mld; + u8 wnm_disassoc_addr[ETH_ALEN]; u16 wnm_dissoc_timer; u8 wnm_bss_termination_duration[12]; struct neighbor_report *wnm_neighbor_report_elements; -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:44 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:44 +0200 Subject: [PATCH 03/16] WNM: Use os_relatime_add_ms helper In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-4-benjamin@sipsolutions.net> From: Benjamin Berg Just simplify the code a little bit by using the helper instead of doing the math inline. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 015a1948e..7857e4ed8 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1598,11 +1598,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); - wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; - wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; - wpa_s->wnm_cand_valid_until.sec += - wpa_s->wnm_cand_valid_until.usec / 1000000; - wpa_s->wnm_cand_valid_until.usec %= 1000000; + os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms); /* * Try fetching the latest scan results from the kernel. -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:42 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:42 +0200 Subject: [PATCH 01/16] WNM: Only trigger selection logic for own scans In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-2-benjamin@sipsolutions.net> From: Benjamin Berg Commit e508c070c40e ("WNM: Keep BTM information until connection completes") changed the logic so that much of the information about a transition management request will be kept around for longer. However, doing this also implies that the scan logic can be called multiple times with wnm_dialog_token being set. Add a guard to bail out if the scan was not done for a BTM request. But, add it after the transition candidate validity check so that we reset the state when a new scan invalidated it. However, invalidation does not make sense for any scan, primarily an ML probe request during a connection attempt should not trigger invalidation. So move the call to wnm_scan_process further down in the list to avoid issues. Signed-off-by: Benjamin Berg --- wpa_supplicant/events.c | 6 +++--- wpa_supplicant/wnm_sta.c | 7 +++++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 8dc618e5c..4505d9e15 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2498,9 +2498,6 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } - if (wnm_scan_process(wpa_s, false) > 0) - goto scan_work_done; - if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; @@ -2530,6 +2527,9 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_wps_update_ap_info(wpa_s, scan_res); + if (wnm_scan_process(wpa_s, false) > 0) + goto scan_work_done; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING && wpa_s->wpa_state < WPA_COMPLETED) goto scan_work_done; diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 7dc93ebff..a383ed803 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1192,6 +1192,11 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) goto send_bss_resp_fail; } + if (!pre_scan_check && !wpa_s->wnm_transition_scan) + return 0; + + wpa_s->wnm_transition_scan = false; + /* Compare the Neighbor Report and scan results */ bss = compare_scan_neighbor_results(wpa_s, 0, &reason); @@ -1504,6 +1509,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning && (!wpa_s->current_ssid || !wpa_s->current_ssid->bssid_set)) { wpa_printf(MSG_DEBUG, "Trying to find another BSS"); + wpa_s->wnm_transition_scan = true; wpa_supplicant_req_scan(wpa_s, 0, 0); } } @@ -1617,6 +1623,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, "WNM: Scan only for a specific BSSID since there is only a single candidate " MACSTR, MAC2STR(wpa_s->next_scan_bssid)); } + wpa_s->wnm_transition_scan = true; wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 426d077d2..6019fd1e1 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1300,6 +1300,7 @@ struct wpa_supplicant { u8 *mac_addr_pno; #ifdef CONFIG_WNM + bool wnm_transition_scan; u8 wnm_dialog_token; u8 wnm_reply; u8 wnm_num_neighbor_report; -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:41 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:41 +0200 Subject: [PATCH 00/16] BTM refactorings and abridged bit handling Message-ID: <20240429115157.211073-1-benjamin@sipsolutions.net> From: Benjamin Berg This patchset refactors the BSS selection when handling BTM requests to use the normal selection mechanism. This is done by moving all checks into wnm_is_bss_excluded. The advantage of this is that the code is simpler overall and that the tests in wnm_is_bss_excluded are also used when doing link selection during MLO. As part of this, the behaviour of the BTM handling code is updated somewhat to handle the abridged bit and also parse the candidate list even if it is not mandatory to do so. Avraham Stern (1): MBO: Always accept BTM request with disassociation imminent bit set Benjamin Berg (15): WNM: Only trigger selection logic for own scans WNM: Store whether disassociation address is an MLD Address WNM: Use os_relatime_add_ms helper WNM: Split candidate list parsing into a separate function WNM: Remove unused age parameter for neighbor comparison WNM: Swap logic in wnm_is_bss_excluded to allow more checks WNM: Move neighbor report test into wnm_is_bss_excluded WNM: Move driver MBO transition rejection into wnm_is_bss_excluded tests: Set the abridged bit in BTM tests WNM: Use standard BSS selection and enable abridged bit handling WNM: Reject requests with an invalid dialog token WNM: Consolidate the scanning paths for BTM requests WNM: Always parse candidate list tests: Add a WNM tests to check candidate list is used if not required WNM: Scan for BSSID if there are forbidden neighbors tests/hwsim/test_ap_hs20.py | 1 + tests/hwsim/test_hapd_ctrl.py | 1 + tests/hwsim/test_wnm.py | 91 +++-- wpa_supplicant/events.c | 8 +- wpa_supplicant/wnm_sta.c | 600 ++++++++++++++---------------- wpa_supplicant/wnm_sta.h | 2 +- wpa_supplicant/wpa_supplicant_i.h | 9 +- 7 files changed, 349 insertions(+), 363 deletions(-) -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:45 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:45 +0200 Subject: [PATCH 04/16] WNM: Split candidate list parsing into a separate function In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-5-benjamin@sipsolutions.net> From: Benjamin Berg ieee802_11_rx_bss_trans_mgmt_req is already dealing with a lot of things including the decisions on how to act on the frame. Split out candidate list parsing to make it easier to work with the function. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 91 ++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 7857e4ed8..721baeddb 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1380,6 +1380,54 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) wpa_s->next_scan_freqs = freqs; } +static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, + const u8 *pos, const u8 *end) +{ + wpa_s->wnm_neighbor_report_elements = os_calloc( + WNM_MAX_NEIGHBOR_REPORT, + sizeof(struct neighbor_report)); + if (wpa_s->wnm_neighbor_report_elements == NULL) + return -1; + + while (end - pos >= 2 && + wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) + { + u8 tag = *pos++; + u8 len = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", + tag); + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "WNM: Truncated request"); + return -1; + } + if (tag == WLAN_EID_NEIGHBOR_REPORT) { + struct neighbor_report *rep; + rep = &wpa_s->wnm_neighbor_report_elements[ + wpa_s->wnm_num_neighbor_report]; + wnm_parse_neighbor_report(wpa_s, pos, len, rep); + if ((wpa_s->wnm_mode & + WNM_BSS_TM_REQ_DISASSOC_IMMINENT) && + ether_addr_equal(rep->bssid, wpa_s->bssid)) + rep->disassoc_imminent = 1; + + wpa_s->wnm_num_neighbor_report++; +#ifdef CONFIG_MBO + if (wpa_s->wnm_mbo_trans_reason_present && + wpa_s->wnm_num_neighbor_report == 1) { + rep->is_first = 1; + wpa_printf(MSG_DEBUG, + "WNM: First transition candidate is " + MACSTR, MAC2STR(rep->bssid)); + } +#endif /* CONFIG_MBO */ + } + + pos += len; + } + + return 0; +} static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, @@ -1529,48 +1577,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, unsigned int valid_ms; wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); - wpa_s->wnm_neighbor_report_elements = os_calloc( - WNM_MAX_NEIGHBOR_REPORT, - sizeof(struct neighbor_report)); - if (wpa_s->wnm_neighbor_report_elements == NULL) - return; - - while (end - pos >= 2 && - wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) - { - u8 tag = *pos++; - u8 len = *pos++; - - wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", - tag); - if (len > end - pos) { - wpa_printf(MSG_DEBUG, "WNM: Truncated request"); - return; - } - if (tag == WLAN_EID_NEIGHBOR_REPORT) { - struct neighbor_report *rep; - rep = &wpa_s->wnm_neighbor_report_elements[ - wpa_s->wnm_num_neighbor_report]; - wnm_parse_neighbor_report(wpa_s, pos, len, rep); - if ((wpa_s->wnm_mode & - WNM_BSS_TM_REQ_DISASSOC_IMMINENT) && - ether_addr_equal(rep->bssid, wpa_s->bssid)) - rep->disassoc_imminent = 1; - - wpa_s->wnm_num_neighbor_report++; -#ifdef CONFIG_MBO - if (wpa_s->wnm_mbo_trans_reason_present && - wpa_s->wnm_num_neighbor_report == 1) { - rep->is_first = 1; - wpa_printf(MSG_DEBUG, - "WNM: First transition candidate is " - MACSTR, MAC2STR(rep->bssid)); - } -#endif /* CONFIG_MBO */ - } - pos += len; - } + if (wnm_parse_candidate_list(wpa_s, pos, end) < 0) + return; if (!wpa_s->wnm_num_neighbor_report) { wpa_printf(MSG_DEBUG, -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:47 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:47 +0200 Subject: [PATCH 06/16] WNM: Swap logic in wnm_is_bss_excluded to allow more checks In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-7-benjamin@sipsolutions.net> From: Benjamin Berg Following commits will move more checks into wnm_is_bss_excluded, prepare for that by changing the logical flow so that further checks can be inserted. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index d3bc03202..7283ff47c 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -2056,19 +2056,20 @@ void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { - if (!(wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) - return false; - /* * In case disassociation imminent is set, do no try to use a BSS to * which we are connected. */ - if (!wpa_s->wnm_disassoc_mld) { - if (ether_addr_equal(bss->bssid, wpa_s->wnm_disassoc_addr)) - return true; - } else { - if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_disassoc_addr)) - return true; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + if (!wpa_s->wnm_disassoc_mld) { + if (ether_addr_equal(bss->bssid, + wpa_s->wnm_disassoc_addr)) + return true; + } else { + if (ether_addr_equal(bss->mld_addr, + wpa_s->wnm_disassoc_addr)) + return true; + } } return false; -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:46 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:46 +0200 Subject: [PATCH 05/16] WNM: Remove unused age parameter for neighbor comparison In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-6-benjamin@sipsolutions.net> From: Benjamin Berg compare_scan_neighbor_results was taking an age paramter to check whether the BSS has been seen recently. This was used historically in a codepath when no new scan was done. However, the logic was changed in commit 20ed289a785c ("WNM: Clean up old scan data processing") and the parameter is not used at all anymore as it is replaced by a different logic. Remove it. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 721baeddb..d3bc03202 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -740,7 +740,7 @@ static struct wpa_bss * find_better_target(struct wpa_bss *a, } static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, enum mbo_transition_reject_reason *reason) { u8 i; @@ -777,19 +777,6 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, continue; } - if (age_secs) { - struct os_reltime now; - - if (os_get_reltime(&now) == 0 && - os_reltime_expired(&now, &target->last_update, - age_secs)) { - wpa_printf(MSG_DEBUG, - "Candidate BSS is more than %ld seconds old", - age_secs); - continue; - } - } - /* * TODO: Could consider allowing transition to another ESS if * PMF was enabled for the association. @@ -1198,7 +1185,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) wpa_s->wnm_transition_scan = false; /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s, 0, &reason); + bss = compare_scan_neighbor_results(wpa_s, &reason); /* * If this is a pre-scan check, returning 0 will trigger a scan and -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:48 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:48 +0200 Subject: [PATCH 07/16] WNM: Move neighbor report test into wnm_is_bss_excluded In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-8-benjamin@sipsolutions.net> From: Benjamin Berg Having it in wnm_is_bss_excluded is more generic as it works for other locations (e.g. MLD link selection). So move the test and add a check for the abridged bit while at it. Note that without the abridged bit check another check would be needed (e.g. checking wnm_dialog_token) to ensure that there isn't a rejection unless a BTM is in progress. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 7283ff47c..e8776a14b 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -761,11 +761,6 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, struct neighbor_report *nei; nei = &wpa_s->wnm_neighbor_report_elements[i]; - if (nei->preference_present && nei->preference == 0) { - wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, - MAC2STR(nei->bssid)); - continue; - } target = wpa_bss_get_bssid(wpa_s, nei->bssid); if (!target) { @@ -2056,6 +2051,8 @@ void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { + int i; + /* * In case disassociation imminent is set, do no try to use a BSS to * which we are connected. @@ -2072,5 +2069,23 @@ bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) } } + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (!ether_addr_equal(nei->bssid, bss->bssid)) + continue; + + if (nei->preference_present && nei->preference == 0) + return true; + + break; + } + + /* If the abridged bit is set, then the BSS must be a known neighbor */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED && + wpa_s->wnm_num_neighbor_report == i) + return true; + return false; } -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:49 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:49 +0200 Subject: [PATCH 08/16] WNM: Move driver MBO transition rejection into wnm_is_bss_excluded In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-9-benjamin@sipsolutions.net> From: Benjamin Berg Change the logic a bit to not directly use the result of the wpa_drv_get_bss_trans_status call and instead use the same selection logic as usual but taking into account the driver rejections. This changes the logic in minor ways. The main change is that this aligns the ordering of BSSs to be identical in all cases. More precisely, we'll select the best BSS as found by find_better_target. Beyond that, it also means that in the case of an non-abridged BTM request we'll also consider candidates that were found through the scan and not in the neighbor report. In this case, the driver will not have a chance to reject them. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 138 +++++++++++++++++++-------------------- wpa_supplicant/wnm_sta.h | 2 +- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index e8776a14b..64124348f 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -615,38 +615,37 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, } -static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) -{ - unsigned int i; - - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) - wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; -} - #ifdef CONFIG_MBO -static struct wpa_bss * -get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, +static void +fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s, enum mbo_transition_reject_reason *reason) { - struct wpa_bss *target = NULL; struct wpa_bss_trans_info params; struct wpa_bss_candidate_info *info = NULL; - struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; - u8 *first_candidate_bssid = NULL, *pos; + struct neighbor_report *nei; + u8 *pos; unsigned int i; + if (!wpa_s->wnm_mbo_trans_reason_present) + return; + params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; params.n_candidates = 0; params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); if (!params.bssid) - return NULL; + return; pos = params.bssid; - for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { - if (nei->is_first) - first_candidate_bssid = nei->bssid; - if (!nei->acceptable) + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + nei = &wpa_s->wnm_neighbor_report_elements[i]; + + nei->drv_mbo_reject = 0; + + if (nei->preference_present && nei->preference == 0) continue; + + /* FIXME: Should we go through wpa_scan_res_match? */ + os_memcpy(pos, nei->bssid, ETH_ALEN); pos += ETH_ALEN; params.n_candidates++; @@ -656,62 +655,40 @@ get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, goto end; info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); - if (!info) { - /* If failed to get candidate BSS transition status from driver, - * get the first acceptable candidate from wpa_supplicant. - */ - target = wpa_bss_get_bssid(wpa_s, params.bssid); + if (!info) goto end; - } - /* Get the first acceptable candidate from driver */ for (i = 0; i < info->num; i++) { - if (info->candidates[i].is_accept) { - target = wpa_bss_get_bssid(wpa_s, - info->candidates[i].bssid); - goto end; - } - } + int j; - /* If Disassociation Imminent is set and driver rejects all the - * candidate select first acceptable candidate which has - * rssi > disassoc_imminent_rssi_threshold - */ - if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { - for (i = 0; i < info->num; i++) { - target = wpa_bss_get_bssid(wpa_s, - info->candidates[i].bssid); - if (target && - (target->level < - wpa_s->conf->disassoc_imminent_rssi_threshold)) + for (j = 0; j < wpa_s->wnm_num_neighbor_report; nei++, j++) { + nei = &wpa_s->wnm_neighbor_report_elements[j]; + + if (!ether_addr_equal(info->candidates[i].bssid, + nei->bssid)) continue; - goto end; - } - } - /* While sending BTM reject use reason code of the first candidate - * received in BTM request frame - */ - if (reason) { - for (i = 0; i < info->num; i++) { - if (first_candidate_bssid && - ether_addr_equal(first_candidate_bssid, - info->candidates[i].bssid)) { + nei->drv_mbo_reject = !info->candidates[i].is_accept; + + /* Use the reject reason from "first" candidate */ + if (nei->is_first && nei->drv_mbo_reject) *reason = info->candidates[i].reject_reason; - break; - } + + break; } } - target = NULL; - end: os_free(params.bssid); if (info) { os_free(info->candidates); os_free(info); } - return target; +} +#else +static void +fetch_drv_mbo_transition_candidate_info(struct wpa_supplicant *wpa_s) +{ } #endif /* CONFIG_MBO */ @@ -755,8 +732,6 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); - wnm_clear_acceptable(wpa_s); - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; @@ -796,21 +771,12 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, continue; } - nei->acceptable = 1; - best_target = find_better_target(target, best_target); if (target == bss) bss_in_list = bss; } -#ifdef CONFIG_MBO - if (wpa_s->wnm_mbo_trans_reason_present) - target = get_mbo_transition_candidate(wpa_s, reason); - else - target = best_target; -#else /* CONFIG_MBO */ target = best_target; -#endif /* CONFIG_MBO */ if (!target) return NULL; @@ -1179,8 +1145,39 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) wpa_s->wnm_transition_scan = false; + /* Fetch MBO transition candidate rejection information from driver */ + fetch_drv_mbo_candidate_info(wpa_s, &reason); + /* Compare the Neighbor Report and scan results */ bss = compare_scan_neighbor_results(wpa_s, &reason); +#ifdef CONFIG_MBO + if (!bss && wpa_s->wnm_mbo_trans_reason_present && + wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + int i; + + /* + * We didn't find any candidate, the driver had a say about + * which targets to reject and disassociation is immiment. + * + * We should still try to roam, so retry after ignoring the + * driver reject for any BSS that has an RSSI better than + * disassoc_imminent_rssi_threshold. + */ + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + + bss = wpa_bss_get_bssid(wpa_s, nei->bssid); + + if (bss->level > + wpa_s->conf->disassoc_imminent_rssi_threshold) + nei->drv_mbo_reject = 0; + } + + bss = compare_scan_neighbor_results(wpa_s, &reason); + } +#endif /* CONFIG_MBO */ /* * If this is a pre-scan check, returning 0 will trigger a scan and @@ -2079,6 +2076,9 @@ bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) if (nei->preference_present && nei->preference == 0) return true; + if (nei->drv_mbo_reject) + return true; + break; } diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 235a838fa..5994027a4 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -44,9 +44,9 @@ struct neighbor_report { unsigned int rm_capab_present:1; unsigned int bearing_present:1; unsigned int bss_term_present:1; - unsigned int acceptable:1; #ifdef CONFIG_MBO unsigned int is_first:1; + unsigned int drv_mbo_reject:1; #endif /* CONFIG_MBO */ struct measurement_pilot *meas_pilot; struct multiple_bssid *mul_bssid; -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:50 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:50 +0200 Subject: [PATCH 09/16] tests: Set the abridged bit in BTM tests In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-10-benjamin@sipsolutions.net> From: Benjamin Berg Many of the WNM tests assume that the supplicant will only select a neighbor that was included in the candidate list. However, without the abridged bit being set, the supplicant is allowed to also select any BSS that is not explicitly included in the list. Prepare for the supplicant handling the abridged bit by setting it in the tests where relevant. Signed-off-by: Benjamin Berg --- tests/hwsim/test_wnm.py | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/hwsim/test_wnm.py b/tests/hwsim/test_wnm.py index d1ad0a8fd..ecaf19009 100644 --- a/tests/hwsim/test_wnm.py +++ b/tests/hwsim/test_wnm.py @@ -613,7 +613,7 @@ def test_wnm_bss_tm_req(dev, apdev): # truncated BSS TM Request req = bss_tm_req(addr, apdev[0]['bssid'], - req_mode=0x08) + req_mode=0x0a) req['payload'] = struct.pack(" References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-11-benjamin@sipsolutions.net> From: Benjamin Berg Most of the logic to reject BSSs during transition has been moved into wnm_is_bss_excluded. In addition to this, since commit 67bf89f55442 ("WNM: Choose the best available BSS, not just the first one") we will simply choose the BSS with the best throughput. Overall, this matches the behaviour that the supplicant will use anyway in wpa_supplicant_select_bss. The only bigger difference is that using this will check all known BSSs instead of only the ones in the candidate list. This means that with this change the abridged bit is handled according to spec. Another slight change is that this drops the logic to reject candidates with a very low signal level. This does not seem to very relevant anymore as we prefere stronger BSSs to begin with. Signed-off-by: Benjamin Berg --- wpa_supplicant/events.c | 2 +- wpa_supplicant/wnm_sta.c | 129 +++--------------------------- wpa_supplicant/wpa_supplicant_i.h | 5 ++ 3 files changed, 18 insertions(+), 118 deletions(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 4505d9e15..31c807879 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1709,7 +1709,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, } -static struct wpa_bss * +struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid, diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 64124348f..8c51b3ddb 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -693,117 +693,6 @@ fetch_drv_mbo_transition_candidate_info(struct wpa_supplicant *wpa_s) #endif /* CONFIG_MBO */ -static struct wpa_bss * find_better_target(struct wpa_bss *a, - struct wpa_bss *b) -{ - if (!a) - return b; - if (!b) - return a; - - if (a->est_throughput > b->est_throughput) { - wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR - " est-tput: %d B: " MACSTR " est-tput: %d", - MAC2STR(a->bssid), a->est_throughput, - MAC2STR(b->bssid), b->est_throughput); - return a; - } - - wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR - " est-tput: %d B: " MACSTR " est-tput: %d", - MAC2STR(a->bssid), a->est_throughput, - MAC2STR(b->bssid), b->est_throughput); - return b; -} - -static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, - enum mbo_transition_reject_reason *reason) -{ - u8 i; - struct wpa_bss *bss = wpa_s->current_bss; - struct wpa_bss *target; - struct wpa_bss *best_target = NULL; - struct wpa_bss *bss_in_list = NULL; - - if (!bss) - return NULL; - - wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", - MAC2STR(wpa_s->bssid), bss->level); - - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { - struct neighbor_report *nei; - - nei = &wpa_s->wnm_neighbor_report_elements[i]; - - target = wpa_bss_get_bssid(wpa_s, nei->bssid); - if (!target) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) not found in scan results", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1); - continue; - } - - /* - * TODO: Could consider allowing transition to another ESS if - * PMF was enabled for the association. - */ - if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, - 1, 0)) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) does not match the current network profile", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1); - continue; - } - - if (target->level < bss->level && target->level < -80) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) does not have sufficient signal level (%d)", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1, - target->level); - continue; - } - - best_target = find_better_target(target, best_target); - if (target == bss) - bss_in_list = bss; - } - - target = best_target; - - if (!target) - return NULL; - - wpa_printf(MSG_DEBUG, - "WNM: Found an acceptable preferred transition candidate BSS " - MACSTR " (RSSI %d, tput: %d bss-tput: %d)", - MAC2STR(target->bssid), target->level, - target->est_throughput, bss->est_throughput); - - if (!bss_in_list) - return target; - - if ((!target->est_throughput && !bss_in_list->est_throughput) || - (target->est_throughput > bss_in_list->est_throughput && - target->est_throughput - bss_in_list->est_throughput > - bss_in_list->est_throughput >> 4)) { - /* It is more than 100/16 percent better, so switch. */ - return target; - } - - wpa_printf(MSG_DEBUG, - "WNM: Stay with our current BSS, not enough change in estimated throughput to switch"); - return bss_in_list; -} - - static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) { const u8 *ie_a, *ie_b; @@ -1121,11 +1010,12 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) { - struct wpa_bss *bss; + struct wpa_bss *bss, *current_bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; enum mbo_transition_reject_reason reason = MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; + struct wpa_ssid *selected_ssid = NULL; if (!wpa_s->wnm_dialog_token) return 0; @@ -1149,7 +1039,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) fetch_drv_mbo_candidate_info(wpa_s, &reason); /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s, &reason); + bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1); #ifdef CONFIG_MBO if (!bss && wpa_s->wnm_mbo_trans_reason_present && wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { @@ -1175,7 +1065,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) nei->drv_mbo_reject = 0; } - bss = compare_scan_neighbor_results(wpa_s, &reason); + bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1); } #endif /* CONFIG_MBO */ @@ -1200,9 +1090,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) return 0; #ifndef CONFIG_NO_ROAMING - if (wpa_s->current_bss && bss != wpa_s->current_bss && - wpa_supplicant_need_to_roam_within_ess(wpa_s, - wpa_s->current_bss, + if (current_bss && bss != current_bss && + wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss)) return 0; #endif /* CONFIG_NO_ROAMING */ @@ -1214,6 +1103,12 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) goto send_bss_resp_fail; } + wpa_printf(MSG_DEBUG, + "WNM: Found an acceptable preferred transition candidate BSS " + MACSTR " (RSSI %d, tput: %d bss-tput: %d)", + MAC2STR(bss->bssid), bss->level, bss->est_throughput, + current_bss ? current_bss->est_throughput : -1); + /* Associate to the network */ wnm_bss_tm_connect(wpa_s, bss, ssid, 1); return 1; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index d4a1284c3..64177546f 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1950,6 +1950,11 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, int only_first_ssid, int debug_print); +struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid, + int only_first_ssid); + int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type if_type, unsigned int *num, -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:52 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:52 +0200 Subject: [PATCH 11/16] MBO: Always accept BTM request with disassociation imminent bit set In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-12-benjamin@sipsolutions.net> From: Avraham Stern According to Multiband Operation specification (r17, section 3.5.2), a BSS Transition Management Request with the disassociation imminent bit set should always be accepted. This is enforced in case the request did not include a candidate list. However, in case a candidate list was included but none of the APs in the candidate list was found in the scan results, the request is rejected. Fix that by always accepting a request with the disassociation imminent bit set even if no roaming candidate was found. type=feature Signed-off-by: Avraham Stern Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 8c51b3ddb..4a3fd5eb1 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1114,11 +1114,18 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) return 1; send_bss_resp_fail: - /* Send reject response for all the failures */ + if (wpa_s->wnm_reply) { + /* If disassoc imminent is set, we must not reject */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT || + wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { + wpa_printf(MSG_DEBUG, + "WNM: Accept BTM request because disassociation imminent bit is set"); + status = WNM_BSS_TM_ACCEPT; + } - if (wpa_s->wnm_reply) wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason, 0, NULL); + } wnm_btm_reset(wpa_s); -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:53 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:53 +0200 Subject: [PATCH 12/16] WNM: Reject requests with an invalid dialog token In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-13-benjamin@sipsolutions.net> From: Benjamin Berg The dialog token must be non-zero. We are using this fact internally to track the state in some cases, so ensure that the assumption is valid. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 4a3fd5eb1..f0cbf914c 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1347,6 +1347,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); + if (!wpa_s->wnm_dialog_token) { + wpa_printf(MSG_DEBUG, "WNM: invalid dialog token"); + wnm_btm_reset(wpa_s); + return; + } + #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) if (wpa_s->reject_btm_req_reason) { wpa_printf(MSG_INFO, -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:54 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:54 +0200 Subject: [PATCH 13/16] WNM: Consolidate the scanning paths for BTM requests In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-14-benjamin@sipsolutions.net> From: Benjamin Berg There was an ancient code path to trigger a scan that was apparently forgotten when the code was extended over time. It does not make any sense to trigger a scan twice, so remove the earlier scan. The earlier scan call was avoiding to trigger a new scan if a fixed BSSID is configured. This seems like a reasonable restriction to do, so add this check before starting a scan. Consolidate everything so that scanning happens at the end of the functions unless we bail out before. Add a "reset" label for all other cases to ensure that we don't leave things in the a bad state. Signed-off-by: Benjamin Berg --- tests/hwsim/test_ap_hs20.py | 1 + tests/hwsim/test_hapd_ctrl.py | 1 + tests/hwsim/test_wnm.py | 2 + wpa_supplicant/wnm_sta.c | 129 +++++++++++++++++++--------------- 4 files changed, 75 insertions(+), 58 deletions(-) diff --git a/tests/hwsim/test_ap_hs20.py b/tests/hwsim/test_ap_hs20.py index 9cf2d9040..bfdbee1c5 100644 --- a/tests/hwsim/test_ap_hs20.py +++ b/tests/hwsim/test_ap_hs20.py @@ -2813,6 +2813,7 @@ def _test_ap_hs20_session_info(dev, apdev): 'password': "password"}) interworking_select(dev[0], bssid, freq="2412") interworking_connect(dev[0], bssid, "TTLS") + dev[0].flush_scan_cache() ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"], timeout=10) if ev is None: raise Exception("Timeout on ESS disassociation imminent notice") diff --git a/tests/hwsim/test_hapd_ctrl.py b/tests/hwsim/test_hapd_ctrl.py index 9cf8ac73c..5b8361f9f 100644 --- a/tests/hwsim/test_hapd_ctrl.py +++ b/tests/hwsim/test_hapd_ctrl.py @@ -249,6 +249,7 @@ def test_hapd_ctrl_disassoc_imminent(dev, apdev): raise Exception("Unexpected DISASSOC_IMMINENT success") if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44:55 2"): raise Exception("Unexpected DISASSOC_IMMINENT success") + dev[0].flush_scan_cache() dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412") addr = dev[0].p2p_interface_addr() if "OK" not in hapd.request("DISASSOC_IMMINENT " + addr + " 2"): diff --git a/tests/hwsim/test_wnm.py b/tests/hwsim/test_wnm.py index ecaf19009..4df9d77f3 100644 --- a/tests/hwsim/test_wnm.py +++ b/tests/hwsim/test_wnm.py @@ -120,6 +120,7 @@ def test_wnm_disassoc_imminent(dev, apdev): """WNM Disassociation Imminent""" hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True) dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412") + dev[0].flush_scan_cache() addr = dev[0].p2p_interface_addr() hapd.request("DISASSOC_IMMINENT " + addr + " 10") ev = dev[0].wait_event(["WNM: Disassociation Imminent"]) @@ -137,6 +138,7 @@ def test_wnm_disassoc_imminent_bssid_set(dev, apdev): hapd2 = start_wnm_ap(apdev[1], time_adv=True, wnm_sleep_mode=True) dev[0].connect("test-wnm", key_mgmt="NONE", bssid=hapd.own_addr(), scan_freq="2412") + dev[0].flush_scan_cache() addr = dev[0].own_addr() cmd = "BSS_TM_REQ " + addr + " pref=1 disassoc_imminent=1 disassoc_timer=100 neighbor=" + apdev[1]['bssid'] + ",0x0000," + "81,1,7,0301ff" if "OK" not in hapd.request(cmd): diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index f0cbf914c..f4986a3bc 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1349,8 +1349,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (!wpa_s->wnm_dialog_token) { wpa_printf(MSG_DEBUG, "WNM: invalid dialog token"); - wnm_btm_reset(wpa_s); - return; + goto reset; } #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) @@ -1361,7 +1360,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->reject_btm_req_reason, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); - return; + goto reset; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ @@ -1370,7 +1369,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); - return; + goto reset; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ @@ -1383,13 +1382,13 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (end - pos < 1) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); - return; + goto reset; } url_len = *pos++; if (url_len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition Management Request (URL truncated)"); - return; + goto reset; } os_memcpy(url, pos, url_len); url[url_len] = '\0'; @@ -1424,7 +1423,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL); - return; + goto reset; } /* The last link is being removed (which must be the assoc link) @@ -1443,16 +1442,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN); } - if (disassoc_imminent) { + if (disassoc_imminent) wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); - if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning && - (!wpa_s->current_ssid || !wpa_s->current_ssid->bssid_set)) { - wpa_printf(MSG_DEBUG, "Trying to find another BSS"); - wpa_s->wnm_transition_scan = true; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - } #ifdef CONFIG_MBO vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); @@ -1466,7 +1458,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); if (wnm_parse_candidate_list(wpa_s, pos, end) < 0) - return; + goto reset; if (!wpa_s->wnm_num_neighbor_report) { wpa_printf(MSG_DEBUG, @@ -1475,17 +1467,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); - return; - } - - if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) { - wpa_printf(MSG_DEBUG, - "WNM: Configuration prevents roaming (BSSID set)"); - wnm_send_bss_transition_mgmt_resp( - wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, - NULL); - return; + goto reset; } wnm_sort_cand_list(wpa_s); @@ -1495,36 +1477,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms); - - /* - * Try fetching the latest scan results from the kernel. - * This can help in finding more up-to-date information should - * the driver have done some internal scanning operations after - * the last scan result update in wpa_supplicant. - * - * It is not a new scan, this does not update the last_scan - * timestamp nor will it expire old BSSs. - */ - wpa_supplicant_update_scan_results(wpa_s, NULL); - if (wnm_scan_process(wpa_s, true) > 0) - return; - wpa_printf(MSG_DEBUG, - "WNM: No valid match in previous scan results - try a new scan"); - - wnm_set_scan_freqs(wpa_s); - if (wpa_s->wnm_num_neighbor_report == 1) { - os_memcpy(wpa_s->next_scan_bssid, - wpa_s->wnm_neighbor_report_elements[0].bssid, - ETH_ALEN); - wpa_printf(MSG_DEBUG, - "WNM: Scan only for a specific BSSID since there is only a single candidate " - MACSTR, MAC2STR(wpa_s->next_scan_bssid)); - } - wpa_s->wnm_transition_scan = true; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (reply) { + } else if (!disassoc_imminent) { enum bss_trans_mgmt_status_code status; + /* No candidate list and disassociation is not imminent */ + if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) || wpa_s->wnm_link_removal) status = WNM_BSS_TM_ACCEPT; @@ -1532,10 +1489,66 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } - wnm_send_bss_transition_mgmt_resp( - wpa_s, status, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); + + if (reply) + wnm_send_bss_transition_mgmt_resp( + wpa_s, status, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); + + goto reset; } + + /* + * Try fetching the latest scan results from the kernel. + * This can help in finding more up-to-date information should + * the driver have done some internal scanning operations after + * the last scan result update in wpa_supplicant. + * + * It is not a new scan, this does not update the last_scan + * timestamp nor will it expire old BSSs. + */ + wpa_supplicant_update_scan_results(wpa_s, NULL); + if (wnm_scan_process(wpa_s, true) > 0) + return; + wpa_printf(MSG_DEBUG, + "WNM: No valid match in previous scan results - try a new scan"); + + /* + * If we have a fixed BSSID configured, just reject at this point. + * NOTE: We could actually check if we are allowed to stay (and we do + * above if we have scan results available). + */ + if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) { + wpa_printf(MSG_DEBUG, "WNM: Fixed BSSID, rejecting request"); + + if (reply) + wnm_send_bss_transition_mgmt_resp( + wpa_s, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, + 0, 0, NULL); + + goto reset; + } + + wnm_set_scan_freqs(wpa_s); + if (wpa_s->wnm_num_neighbor_report == 1) { + os_memcpy(wpa_s->next_scan_bssid, + wpa_s->wnm_neighbor_report_elements[0].bssid, + ETH_ALEN); + wpa_printf(MSG_DEBUG, + "WNM: Scan only for a specific BSSID since there is only a single candidate " + MACSTR, MAC2STR(wpa_s->next_scan_bssid)); + } + wpa_s->wnm_transition_scan = true; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + /* Continue from scan handler */ + return; + +reset: + wnm_btm_reset(wpa_s); + return; } -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:55 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:55 +0200 Subject: [PATCH 14/16] WNM: Always parse candidate list In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-15-benjamin@sipsolutions.net> From: Benjamin Berg The candidate list included bit just makes parsing the list mandatory. The AP may still include a candidate list which we should use to be able to optimize scanning. As such, always parse out the list but still verify that if the list is not empty if the bit is set. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 48 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index f4986a3bc..b365fc4d3 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1264,12 +1264,6 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end) { - wpa_s->wnm_neighbor_report_elements = os_calloc( - WNM_MAX_NEIGHBOR_REPORT, - sizeof(struct neighbor_report)); - if (wpa_s->wnm_neighbor_report_elements == NULL) - return -1; - while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { @@ -1284,6 +1278,15 @@ static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, } if (tag == WLAN_EID_NEIGHBOR_REPORT) { struct neighbor_report *rep; + + if (!wpa_s->wnm_num_neighbor_report) { + wpa_s->wnm_neighbor_report_elements = os_calloc( + WNM_MAX_NEIGHBOR_REPORT, + sizeof(struct neighbor_report)); + if (wpa_s->wnm_neighbor_report_elements == NULL) + return -1; + } + rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); @@ -1452,23 +1455,22 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); #endif /* CONFIG_MBO */ - if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { - unsigned int valid_ms; - - wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); + if (wnm_parse_candidate_list(wpa_s, pos, end) < 0) + goto reset; - if (wnm_parse_candidate_list(wpa_s, pos, end) < 0) - goto reset; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED && + !wpa_s->wnm_num_neighbor_report) { + wpa_printf(MSG_DEBUG, + "WNM: Candidate list included bit is set, but no candidates found"); + wnm_send_bss_transition_mgmt_resp( + wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); + goto reset; + } - if (!wpa_s->wnm_num_neighbor_report) { - wpa_printf(MSG_DEBUG, - "WNM: Candidate list included bit is set, but no candidates found"); - wnm_send_bss_transition_mgmt_resp( - wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, - NULL); - goto reset; - } + if (wpa_s->wnm_num_neighbor_report) { + unsigned int valid_ms; wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); @@ -1477,7 +1479,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms); - } else if (!disassoc_imminent) { + } + + if (!wpa_s->wnm_num_neighbor_report && !disassoc_imminent) { enum bss_trans_mgmt_status_code status; /* No candidate list and disassociation is not imminent */ -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:56 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:56 +0200 Subject: [PATCH 15/16] tests: Add a WNM tests to check candidate list is used if not required In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-16-benjamin@sipsolutions.net> From: Benjamin Berg This tests that the candidate list is used even if it is not required because the abridged and preferred candidate list included bits are not set. Signed-off-by: Benjamin Berg --- tests/hwsim/test_wnm.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/hwsim/test_wnm.py b/tests/hwsim/test_wnm.py index 4df9d77f3..fe3417dc3 100644 --- a/tests/hwsim/test_wnm.py +++ b/tests/hwsim/test_wnm.py @@ -911,6 +911,29 @@ def test_wnm_bss_tm(dev, apdev): ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5) if ev is not None: raise Exception("Unexpected reassociation") + + dev[0].flush_scan_cache() + logger.info("Candidate list is used even if not required by spec (pref=0, abridged=0)") + if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=0 abridged=0 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,0301ff" + ' neighbor=' + apdev[1]['bssid'] + ",0x0000,115,36,7,030100"): + raise Exception("BSS_TM_REQ command failed") + ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10) + if ev is None: + raise Exception("No BSS Transition Management Response") + if "status_code=0" not in ev: + raise Exception("BSS transition request was not accepted: " + ev) + if "target_bssid=" + apdev[0]['bssid'] not in ev: + raise Exception("Unexpected target BSS: " + ev) + # This scans only one frequency + scan_ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0) + if scan_ev is None: + raise Exception("Expected scan started") + dev[0].wait_connected(timeout=15, error="No reassociation seen") + if apdev[0]['bssid'] not in ev: + raise Exception("Unexpected reassociation target: " + ev) + ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1) + if ev is not None: + raise Exception("Unexpected scan started") + finally: clear_regdom_state(dev, hapd, hapd2) -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 04:51:57 2024 From: benjamin at sipsolutions.net (benjamin at sipsolutions.net) Date: Mon, 29 Apr 2024 13:51:57 +0200 Subject: [PATCH 16/16] WNM: Scan for BSSID if there are forbidden neighbors In-Reply-To: <20240429115157.211073-1-benjamin@sipsolutions.net> References: <20240429115157.211073-1-benjamin@sipsolutions.net> Message-ID: <20240429115157.211073-17-benjamin@sipsolutions.net> From: Benjamin Berg The test to scan for a single BSSID assumed that there is only a single neighbor in the candidate list. Also do this optimization if there are multiple neighbors but only one of them is valid. Signed-off-by: Benjamin Berg --- wpa_supplicant/wnm_sta.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index b365fc4d3..f6443682b 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1262,8 +1262,11 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) } static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, - const u8 *pos, const u8 *end) + const u8 *pos, const u8 *end, + int *num_valid_candidates) { + *num_valid_candidates = 0; + while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { @@ -1295,6 +1298,9 @@ static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, ether_addr_equal(rep->bssid, wpa_s->bssid)) rep->disassoc_imminent = 1; + if (rep->preference_present && rep->preference) + *num_valid_candidates += 1; + wpa_s->wnm_num_neighbor_report++; #ifdef CONFIG_MBO if (wpa_s->wnm_mbo_trans_reason_present && @@ -1323,6 +1329,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *vendor; #endif /* CONFIG_MBO */ bool disassoc_imminent; + int num_valid_candidates; if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm) return; @@ -1455,7 +1462,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); #endif /* CONFIG_MBO */ - if (wnm_parse_candidate_list(wpa_s, pos, end) < 0) + if (wnm_parse_candidate_list(wpa_s, pos, end, + &num_valid_candidates) < 0) goto reset; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED && @@ -1536,7 +1544,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } wnm_set_scan_freqs(wpa_s); - if (wpa_s->wnm_num_neighbor_report == 1) { + if (num_valid_candidates == 1) { + /* Any invalid candidate was sorted to the end */ os_memcpy(wpa_s->next_scan_bssid, wpa_s->wnm_neighbor_report_elements[0].bssid, ETH_ALEN); -- 2.44.0 From benjamin at sipsolutions.net Mon Apr 29 05:16:12 2024 From: benjamin at sipsolutions.net (Benjamin Berg) Date: Mon, 29 Apr 2024 14:16:12 +0200 Subject: [PATCH] wpa_supplicant: MLD STA: find partner links by BSSID and SSID In-Reply-To: References: <20240409065023.30664-1-michael-cy.lee@mediatek.com> Message-ID: <0a0132b770248ccc7c8e10204f4cf68575133fa3.camel@sipsolutions.net> Hi Michael, On Mon, 2024-04-22 at 02:35 +0000, Michael-cy Lee (???) wrote: > On Wed, 2024-04-17 at 09:06 +0200, Benjamin Berg wrote: > > On Tue, 2024-04-09 at 14:50 +0800, Michael-CY Lee wrote: > > > MLD STA finds MLD AP's partner links by BSSID from the scan results. > > > However, if the scan results contain BSSs with same BSSID but different > > > BSS information, the MLD STA might assign a wrong BSS to one of the MLD > > > AP's partner links. > > > > As a clarification, why/how do we get into the situation that the > > same BSSIDs exists with the wrong SSID in the supplicant? > > > > Did the AP change its SSID and we still have the old one in the > > cache? > > Yes, that's exactly the situation we've faced. > To be more precise, after we changed the SSID and restarted the AP MLD, > we triggered the STA scan and saw the STA's scan results containing two > BSSs with the same BSSID but different SSIDs, then the STA incorrectly > matched one of the AP MLD's links to the wrong BSS (old SSID). > > > > > If that is the case, then maybe what we should be doing is to verify > > that the BSS parameter change count matches. If not, it makes sense to > > reject the BSS for now, which should trigger an ML probe request to > > update the information and the next attempt will work fine. > > We think the BSS parameter change count (BPCC) might not be helpful > here since a change in SSID will not trigger a critical update. > Additionally, An SSID change is usually followed by an AP restart, > which will reset the BPCC value to 0. Right, I guess that is fair then. I guess it feels a bit weird to me overall, because I would expect that we only ever have one BSS entry for a BSSID (except as a workaround for some broken legacy APs). And it seems to me like one could increase the BPCC across an AP restart. So, I don't have a good alternative to your solution, I think. That said, maybe we should still add an additional check for the BPCC? Benjamin From ilan.peer at intel.com Tue Apr 30 06:08:44 2024 From: ilan.peer at intel.com (Peer, Ilan) Date: Tue, 30 Apr 2024 13:08:44 +0000 Subject: [PATCH 4/7] SME: Explicitly deauthenticate on authentication/association timeout In-Reply-To: References: <20240408130701.531194-1-andrei.otcheretianski@intel.com> <20240408130701.531194-4-andrei.otcheretianski@intel.com> Message-ID: Hi, > -----Original Message----- > From: Jouni Malinen > Sent: Sunday, 21 April 2024 12:02 > To: Otcheretianski, Andrei > Cc: hostap at lists.infradead.org; Peer, Ilan > Subject: Re: [PATCH 4/7] SME: Explicitly deauthenticate on > authentication/association timeout > > On Mon, Apr 08, 2024 at 04:06:58PM +0300, Andrei Otcheretianski wrote: > > This would clear the local driver state and also the AP state, and > > would allow clean connection establishment next time. > > > > This is needed in cases where the AP sends the association response > > frame and sets the station state to associated, but the underlying > > driver, e.g., mac80211, fails to parse the association response and > > drops it, eventually clearing the association flow and sending a > > timeout event to user space. > > This seems to break the hwsim test case > eht_mld_sae_two_links_disable_enable in my test setup. The RECONNECT > command after having restarted the AP MLD seems to result in the Authentication > frames not being received (no ACK) by the AP MLD. Those frames seem to be > being sent to the old link addressed of the AP MLD and not the ones that were > assigned when restarting it. > Since the BSS table is not flushed in the wpa_supplicant, the BSS entries for the initial AP MLD are still valid and thus the wpa_supplicant tries to use them for the connection establishment. Flushing the BSS table before RECONNECT seems to solve the issue. This issue is there also before this change, however, with this change since the DEAUTH event is injected to the wpa_supplicant, the network gets temporarily disabled and thus the connection fails. Sorry for the late response. Regards, Ilan.