aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2026-08-10 14:23:31 +0000
committerCy Schubert <cy@FreeBSD.org>2026-08-11 03:54:02 +0000
commit71e72c9e91c4b8007a4292e09669e8b549c29e97 (patch)
treee2313ab1ebb7bfd09ec43d213c24b79b956e3f53
parent135df778543123a7dea08553c78da1b51a6b3098 (diff)
parent513264698a892550ba20aeb9da7f360417fdc63b (diff)
wpa: Update to 2.12
Fixes and new features include: hostapd: * support RSN overriding (e.g., WPA3-Personal Compatibility Mode) * EHT/IEEE 802.11be/Wi-Fi 7 - more complete support - fix message validation issues that could enable DoS attacks - fix group key rekeying * enable SAE group 20 by default if SAE-EXT-KEY is enabled * reject unexpected SAE password identifier to avoid DoS attack against a specific STA * mandate use of SAE H2E when using password identifiers * assign VLAN when using SAE with PMKSA caching * support SPP A-MSDU negotiation * support IEEE 802.11bi functionality - changing SAE password identifiers - EPPKE - IEEE 802.1X/EAP in Authentication frames - Association frame encryption - PMKID privacy * remove the driver interface for now obsolete Host AP driver * remove the driver interface for now obsolete Atheros WEXT interface * move supported, basic, and Beacon TX rate configuration to be at BSS level instead of per-radio for all BSSs * fix various issues in Multiple-BSSID functionality * support OpenSSL 3.0 API changes * EAP-TEAP: protocol changes based on RFC 9930; this is not compatible with previous versions * support Automated Frequency Coordination (AFC) on the 6 GHz band * improve GAS/ANQP processing to support larger ANQP responses * a large number of other fixes, cleanup, and extensions wpa_supplicant: * support RSN overriding (e.g., WPA3-Personal Compatibility Mode) * improve BSS transition management support * EHT/IEEE 802.11be/Wi-Fi 7 - more complete support - fix message validation issues that could enable DoS attacks * support Wi-Fi Direct R2 * support Wi-Fi Aware (add synchronized NAN; extend USD support) * support Proximity Ranging * support SPP A-MSDU negotiation * support IEEE 802.11bi functionality - changing SAE password identifiers - EPPKE - IEEE 802.1X/EAP in Authentication frames - Association frame encryption - PMKID privacy * enable layer 2/Wi-Fi multicast filtering for all networks (not just some Passpoint networks which enabled this before) * wpa_gui: port to Qt6 * support OpenSSL 3.0 API changes * EAP-TEAP: protocol changes based on RFC 9930; this is not compatible with previous versions * maintain configuration file permissions when writing updated configuration * add option to validate PKCS#11/OpenSC engine and module paths * fix PMKSA caching to enforce network context to avoid misuse of unexpected PMKSA cache entries * fix a potential DoS attack in SAE processing of an unexpected element * fix incomplete bounds checking of mesh AMPE messages that could have resulted in DoS attacks and memory corruption * a large number of other fixes, cleanup, and extensions MFC after: 2 months Merge commit '513264698a892550ba20aeb9da7f360417fdc63b'
-rw-r--r--contrib/wpa/Makefile10
-rw-r--r--contrib/wpa/README2
-rw-r--r--contrib/wpa/hostapd/Android.mk40
-rw-r--r--contrib/wpa/hostapd/ChangeLog30
-rw-r--r--contrib/wpa/hostapd/Makefile67
-rw-r--r--contrib/wpa/hostapd/README11
-rw-r--r--contrib/wpa/hostapd/README-MULTI-AP2
-rw-r--r--contrib/wpa/hostapd/android.config3
-rw-r--r--contrib/wpa/hostapd/config_file.c734
-rw-r--r--contrib/wpa/hostapd/ctrl_iface.c983
-rw-r--r--contrib/wpa/hostapd/ctrl_iface.h2
-rw-r--r--contrib/wpa/hostapd/defconfig34
-rw-r--r--contrib/wpa/hostapd/eap_register.c7
-rw-r--r--contrib/wpa/hostapd/hlr_auc_gw.c29
-rw-r--r--contrib/wpa/hostapd/hostapd.conf370
-rw-r--r--contrib/wpa/hostapd/hostapd_cli.c130
-rw-r--r--contrib/wpa/hostapd/main.c96
-rw-r--r--contrib/wpa/hs20/client/Android.mk5
-rw-r--r--contrib/wpa/hs20/client/Makefile5
-rw-r--r--contrib/wpa/hs20/client/devdetail.xml47
-rw-r--r--contrib/wpa/hs20/client/devinfo.xml7
-rw-r--r--contrib/wpa/hs20/client/est.c742
-rw-r--r--contrib/wpa/hs20/client/oma_dm_client.c1398
-rw-r--r--contrib/wpa/hs20/client/osu_client.c1866
-rw-r--r--contrib/wpa/hs20/client/osu_client.h97
-rw-r--r--contrib/wpa/hs20/client/spp_client.c1003
-rw-r--r--contrib/wpa/src/ap/Makefile6
-rw-r--r--contrib/wpa/src/ap/acs.c178
-rw-r--r--contrib/wpa/src/ap/afc.c1123
-rw-r--r--contrib/wpa/src/ap/airtime_policy.c7
-rw-r--r--contrib/wpa/src/ap/ap_config.c176
-rw-r--r--contrib/wpa/src/ap/ap_config.h168
-rw-r--r--contrib/wpa/src/ap/ap_drv_ops.c238
-rw-r--r--contrib/wpa/src/ap/ap_drv_ops.h40
-rw-r--r--contrib/wpa/src/ap/ap_mlme.c8
-rw-r--r--contrib/wpa/src/ap/authsrv.c116
-rw-r--r--contrib/wpa/src/ap/beacon.c1039
-rw-r--r--contrib/wpa/src/ap/ctrl_iface_ap.c495
-rw-r--r--contrib/wpa/src/ap/ctrl_iface_ap.h6
-rw-r--r--contrib/wpa/src/ap/dfs.c97
-rw-r--r--contrib/wpa/src/ap/dfs.h2
-rw-r--r--contrib/wpa/src/ap/dpp_hostapd.c56
-rw-r--r--contrib/wpa/src/ap/drv_callbacks.c540
-rw-r--r--contrib/wpa/src/ap/eap_user_db.c2
-rw-r--r--contrib/wpa/src/ap/gas_query_ap.c12
-rw-r--r--contrib/wpa/src/ap/gas_serv.c917
-rw-r--r--contrib/wpa/src/ap/hostapd.c1010
-rw-r--r--contrib/wpa/src/ap/hostapd.h187
-rw-r--r--contrib/wpa/src/ap/hs20.c107
-rw-r--r--contrib/wpa/src/ap/hw_features.c328
-rw-r--r--contrib/wpa/src/ap/hw_features.h22
-rw-r--r--contrib/wpa/src/ap/ieee802_11.c3758
-rw-r--r--contrib/wpa/src/ap/ieee802_11.h78
-rw-r--r--contrib/wpa/src/ap/ieee802_11_eht.c1779
-rw-r--r--contrib/wpa/src/ap/ieee802_11_he.c75
-rw-r--r--contrib/wpa/src/ap/ieee802_11_ht.c61
-rw-r--r--contrib/wpa/src/ap/ieee802_11_shared.c105
-rw-r--r--contrib/wpa/src/ap/ieee802_11_vht.c49
-rw-r--r--contrib/wpa/src/ap/ieee802_1x.c196
-rw-r--r--contrib/wpa/src/ap/ieee802_1x.h2
-rw-r--r--contrib/wpa/src/ap/interference.c592
-rw-r--r--contrib/wpa/src/ap/interference.h18
-rw-r--r--contrib/wpa/src/ap/nan_usd_ap.c64
-rw-r--r--contrib/wpa/src/ap/nan_usd_ap.h7
-rw-r--r--contrib/wpa/src/ap/neighbor_db.c66
-rw-r--r--contrib/wpa/src/ap/pmksa_cache_auth.c30
-rw-r--r--contrib/wpa/src/ap/pmksa_cache_auth.h14
-rw-r--r--contrib/wpa/src/ap/rrm.c7
-rw-r--r--contrib/wpa/src/ap/sta_info.c503
-rw-r--r--contrib/wpa/src/ap/sta_info.h95
-rw-r--r--contrib/wpa/src/ap/vlan_full.c22
-rw-r--r--contrib/wpa/src/ap/wnm_ap.c14
-rw-r--r--contrib/wpa/src/ap/wpa_auth.c1475
-rw-r--r--contrib/wpa/src/ap/wpa_auth.h147
-rw-r--r--contrib/wpa/src/ap/wpa_auth_ft.c116
-rw-r--r--contrib/wpa/src/ap/wpa_auth_glue.c349
-rw-r--r--contrib/wpa/src/ap/wpa_auth_i.h13
-rw-r--r--contrib/wpa/src/ap/wpa_auth_ie.c694
-rw-r--r--contrib/wpa/src/ap/wpa_auth_kay.c3
-rw-r--r--contrib/wpa/src/ap/wps_hostapd.c2
-rw-r--r--contrib/wpa/src/build.rules3
-rw-r--r--contrib/wpa/src/common/Makefile1
-rw-r--r--contrib/wpa/src/common/common_module_tests.c15
-rw-r--r--contrib/wpa/src/common/defs.h65
-rw-r--r--contrib/wpa/src/common/dpp.c83
-rw-r--r--contrib/wpa/src/common/dpp.h10
-rw-r--r--contrib/wpa/src/common/dpp_auth.c2
-rw-r--r--contrib/wpa/src/common/dpp_backup.c3
-rw-r--r--contrib/wpa/src/common/dpp_crypto.c1
-rw-r--r--contrib/wpa/src/common/dpp_i.h2
-rw-r--r--contrib/wpa/src/common/dpp_reconfig.c2
-rw-r--r--contrib/wpa/src/common/dpp_tcp.c4
-rw-r--r--contrib/wpa/src/common/gas.c6
-rw-r--r--contrib/wpa/src/common/gas_server.c14
-rw-r--r--contrib/wpa/src/common/gas_server.h2
-rw-r--r--contrib/wpa/src/common/hw_features_common.c48
-rw-r--r--contrib/wpa/src/common/hw_features_common.h2
-rw-r--r--contrib/wpa/src/common/ieee802_11_common.c975
-rw-r--r--contrib/wpa/src/common/ieee802_11_common.h75
-rw-r--r--contrib/wpa/src/common/ieee802_11_defs.h429
-rw-r--r--contrib/wpa/src/common/nan.h98
-rw-r--r--contrib/wpa/src/common/nan_de.c1970
-rw-r--r--contrib/wpa/src/common/nan_de.h193
-rw-r--r--contrib/wpa/src/common/nan_defs.h663
-rw-r--r--contrib/wpa/src/common/proc_coord.c817
-rw-r--r--contrib/wpa/src/common/proc_coord.h139
-rw-r--r--contrib/wpa/src/common/proximity_ranging.c2707
-rw-r--r--contrib/wpa/src/common/proximity_ranging.h683
-rw-r--r--contrib/wpa/src/common/ptksa_cache.c27
-rw-r--r--contrib/wpa/src/common/ptksa_cache.h4
-rw-r--r--contrib/wpa/src/common/qca-vendor.h6157
-rw-r--r--contrib/wpa/src/common/sae.c145
-rw-r--r--contrib/wpa/src/common/sae.h21
-rw-r--r--contrib/wpa/src/common/sae_pk.c6
-rw-r--r--contrib/wpa/src/common/version.h2
-rw-r--r--contrib/wpa/src/common/wpa_common.c1097
-rw-r--r--contrib/wpa/src/common/wpa_common.h96
-rw-r--r--contrib/wpa/src/common/wpa_ctrl.h76
-rw-r--r--contrib/wpa/src/crypto/crypto.h2
-rw-r--r--contrib/wpa/src/crypto/crypto_gnutls.c8
-rw-r--r--contrib/wpa/src/crypto/crypto_libtomcrypt.c3
-rw-r--r--contrib/wpa/src/crypto/crypto_linux.c5
-rw-r--r--contrib/wpa/src/crypto/crypto_openssl.c193
-rw-r--r--contrib/wpa/src/crypto/crypto_wolfssl.c285
-rw-r--r--contrib/wpa/src/crypto/sha256-prf.c2
-rw-r--r--contrib/wpa/src/crypto/sha256.h2
-rw-r--r--contrib/wpa/src/crypto/sha384-prf.c2
-rw-r--r--contrib/wpa/src/crypto/sha384.h2
-rw-r--r--contrib/wpa/src/crypto/sha512-prf.c2
-rw-r--r--contrib/wpa/src/crypto/tls_gnutls.c20
-rw-r--r--contrib/wpa/src/crypto/tls_openssl.c536
-rw-r--r--contrib/wpa/src/crypto/tls_wolfssl.c668
-rw-r--r--contrib/wpa/src/drivers/driver.h1203
-rw-r--r--contrib/wpa/src/drivers/driver_atheros.c2286
-rw-r--r--contrib/wpa/src/drivers/driver_bsd.c8
-rw-r--r--contrib/wpa/src/drivers/driver_common.c26
-rw-r--r--contrib/wpa/src/drivers/driver_hostap.c1208
-rw-r--r--contrib/wpa/src/drivers/driver_hostap.h208
-rw-r--r--contrib/wpa/src/drivers/driver_macsec_linux.c11
-rw-r--r--contrib/wpa/src/drivers/driver_ndis.c3
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211.c4050
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211.h116
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211_capa.c701
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211_event.c1400
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211_monitor.c385
-rw-r--r--contrib/wpa/src/drivers/driver_nl80211_scan.c83
-rw-r--r--contrib/wpa/src/drivers/driver_openbsd.c3
-rw-r--r--contrib/wpa/src/drivers/driver_privsep.c3
-rw-r--r--contrib/wpa/src/drivers/driver_wext.c124
-rw-r--r--contrib/wpa/src/drivers/driver_wext.h2
-rw-r--r--contrib/wpa/src/drivers/drivers.c6
-rw-r--r--contrib/wpa/src/drivers/drivers.mak22
-rw-r--r--contrib/wpa/src/drivers/drivers.mk18
-rw-r--r--contrib/wpa/src/drivers/nl80211_copy.h1624
-rw-r--r--contrib/wpa/src/eap_common/eap_pwd_common.c2
-rw-r--r--contrib/wpa/src/eap_common/eap_teap_common.c73
-rw-r--r--contrib/wpa/src/eap_common/eap_teap_common.h7
-rw-r--r--contrib/wpa/src/eap_common/ikev2_common.c29
-rw-r--r--contrib/wpa/src/eap_peer/eap.c26
-rw-r--r--contrib/wpa/src/eap_peer/eap_config.h2
-rw-r--r--contrib/wpa/src/eap_peer/eap_leap.c5
-rw-r--r--contrib/wpa/src/eap_peer/eap_teap.c691
-rw-r--r--contrib/wpa/src/eap_peer/eap_teap_pac.c931
-rw-r--r--contrib/wpa/src/eap_peer/eap_teap_pac.h50
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls.c55
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls_common.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls_common.h1
-rw-r--r--contrib/wpa/src/eap_server/eap.h2
-rw-r--r--contrib/wpa/src/eap_server/eap_server_teap.c522
-rw-r--r--contrib/wpa/src/eap_server/eap_server_tls.c54
-rw-r--r--contrib/wpa/src/eap_server/eap_server_tls_common.c8
-rw-r--r--contrib/wpa/src/eap_server/eap_server_ttls.c5
-rw-r--r--contrib/wpa/src/eap_server/eap_tls_common.h1
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm.c17
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm.h2
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h2
-rw-r--r--contrib/wpa/src/eapol_supp/eapol_supp_sm.c135
-rw-r--r--contrib/wpa/src/eapol_supp/eapol_supp_sm.h5
-rw-r--r--contrib/wpa/src/fst/fst.c8
-rw-r--r--contrib/wpa/src/fst/fst_session.c8
-rw-r--r--contrib/wpa/src/nan/Makefile9
-rw-r--r--contrib/wpa/src/nan/nan.c3677
-rw-r--r--contrib/wpa/src/nan/nan.h946
-rw-r--r--contrib/wpa/src/nan/nan_bootstrap.c741
-rw-r--r--contrib/wpa/src/nan/nan_crypto.c773
-rw-r--r--contrib/wpa/src/nan/nan_i.h867
-rw-r--r--contrib/wpa/src/nan/nan_module_test_cases.c817
-rw-r--r--contrib/wpa/src/nan/nan_module_tests.c1530
-rw-r--r--contrib/wpa/src/nan/nan_module_tests.h137
-rw-r--r--contrib/wpa/src/nan/nan_ndl.c1724
-rw-r--r--contrib/wpa/src/nan/nan_ndp.c1214
-rw-r--r--contrib/wpa/src/nan/nan_pairing.c1625
-rw-r--r--contrib/wpa/src/nan/nan_sec.c1653
-rw-r--r--contrib/wpa/src/nan/nan_util.c2043
-rw-r--r--contrib/wpa/src/p2p/p2p.c1774
-rw-r--r--contrib/wpa/src/p2p/p2p.h416
-rw-r--r--contrib/wpa/src/p2p/p2p_build.c170
-rw-r--r--contrib/wpa/src/p2p/p2p_go_neg.c284
-rw-r--r--contrib/wpa/src/p2p/p2p_group.c52
-rw-r--r--contrib/wpa/src/p2p/p2p_i.h236
-rw-r--r--contrib/wpa/src/p2p/p2p_invitation.c284
-rw-r--r--contrib/wpa/src/p2p/p2p_parse.c68
-rw-r--r--contrib/wpa/src/p2p/p2p_pd.c864
-rw-r--r--contrib/wpa/src/p2p/p2p_utils.c71
-rw-r--r--contrib/wpa/src/pae/ieee802_1x_kay.c33
-rw-r--r--contrib/wpa/src/pae/ieee802_1x_kay.h4
-rw-r--r--contrib/wpa/src/pasn/pasn_common.c168
-rw-r--r--contrib/wpa/src/pasn/pasn_common.h164
-rw-r--r--contrib/wpa/src/pasn/pasn_initiator.c606
-rw-r--r--contrib/wpa/src/pasn/pasn_responder.c491
-rw-r--r--contrib/wpa/src/radius/radius.c112
-rw-r--r--contrib/wpa/src/radius/radius.h10
-rw-r--r--contrib/wpa/src/radius/radius_client.c60
-rw-r--r--contrib/wpa/src/radius/radius_server.c306
-rw-r--r--contrib/wpa/src/radius/radius_server.h29
-rw-r--r--contrib/wpa/src/rsn_supp/pmksa_cache.c94
-rw-r--r--contrib/wpa/src/rsn_supp/pmksa_cache.h6
-rw-r--r--contrib/wpa/src/rsn_supp/preauth.c2
-rw-r--r--contrib/wpa/src/rsn_supp/tdls.c28
-rw-r--r--contrib/wpa/src/rsn_supp/wpa.c1342
-rw-r--r--contrib/wpa/src/rsn_supp/wpa.h104
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ft.c8
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_i.h35
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ie.c199
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ie.h8
-rw-r--r--contrib/wpa/src/tls/tlsv1_client.c2
-rw-r--r--contrib/wpa/src/tls/tlsv1_common.c8
-rw-r--r--contrib/wpa/src/tls/tlsv1_common.h4
-rw-r--r--contrib/wpa/src/tls/x509v3.c2
-rw-r--r--contrib/wpa/src/utils/bitfield.c160
-rw-r--r--contrib/wpa/src/utils/bitfield.h12
-rw-r--r--contrib/wpa/src/utils/common.c32
-rw-r--r--contrib/wpa/src/utils/common.h29
-rw-r--r--contrib/wpa/src/utils/eloop.c9
-rw-r--r--contrib/wpa/src/utils/ext_password_file.c21
-rw-r--r--contrib/wpa/src/utils/http-utils.h36
-rw-r--r--contrib/wpa/src/utils/http_curl.c962
-rw-r--r--contrib/wpa/src/utils/json.c138
-rw-r--r--contrib/wpa/src/utils/json.h3
-rw-r--r--contrib/wpa/src/utils/module_tests.h1
-rw-r--r--contrib/wpa/src/utils/os.h11
-rw-r--r--contrib/wpa/src/utils/os_unix.c75
-rw-r--r--contrib/wpa/src/utils/trace.c47
-rw-r--r--contrib/wpa/src/utils/trace.h25
-rw-r--r--contrib/wpa/src/utils/utils_module_tests.c97
-rw-r--r--contrib/wpa/src/utils/wpa_debug.c38
-rw-r--r--contrib/wpa/src/utils/wpa_debug.h49
-rw-r--r--contrib/wpa/src/utils/wpabuf.c57
-rw-r--r--contrib/wpa/src/utils/wpabuf.h11
-rw-r--r--contrib/wpa/src/utils/xml-utils.c31
-rw-r--r--contrib/wpa/src/utils/xml-utils.h19
-rw-r--r--contrib/wpa/src/utils/xml_libxml2.c212
-rw-r--r--contrib/wpa/src/wps/http_server.c9
-rw-r--r--contrib/wpa/src/wps/wps_er.c5
-rw-r--r--contrib/wpa/src/wps/wps_upnp_ap.c2
-rw-r--r--contrib/wpa/wpa_supplicant/Android.mk60
-rw-r--r--contrib/wpa/wpa_supplicant/ChangeLog32
-rw-r--r--contrib/wpa/wpa_supplicant/Makefile81
-rw-r--r--contrib/wpa/wpa_supplicant/README4
-rw-r--r--contrib/wpa/wpa_supplicant/README-HS2022
-rw-r--r--contrib/wpa/wpa_supplicant/README-NAN-USD16
-rw-r--r--contrib/wpa/wpa_supplicant/ap.c68
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan_learn.c5
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan_simple.c4
-rw-r--r--contrib/wpa/wpa_supplicant/bss.c966
-rw-r--r--contrib/wpa/wpa_supplicant/bss.h34
-rw-r--r--contrib/wpa/wpa_supplicant/config.c663
-rw-r--r--contrib/wpa/wpa_supplicant/config.h174
-rw-r--r--contrib/wpa/wpa_supplicant/config_file.c300
-rw-r--r--contrib/wpa/wpa_supplicant/config_none.c2
-rw-r--r--contrib/wpa/wpa_supplicant/config_ssid.h115
-rw-r--r--contrib/wpa/wpa_supplicant/config_winreg.c2
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface.c1678
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c35
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h3
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new.c552
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new.h87
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c645
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h20
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c47
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h2
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c2
-rw-r--r--contrib/wpa/wpa_supplicant/defconfig44
-rw-r--r--contrib/wpa/wpa_supplicant/dpp_supplicant.c84
-rw-r--r--contrib/wpa/wpa_supplicant/dpp_supplicant.h2
-rw-r--r--contrib/wpa/wpa_supplicant/driver_i.h223
-rw-r--r--contrib/wpa/wpa_supplicant/eap_register.c7
-rw-r--r--contrib/wpa/wpa_supplicant/eapol_test.c5
-rw-r--r--contrib/wpa/wpa_supplicant/events.c1447
-rw-r--r--contrib/wpa/wpa_supplicant/gas_query.c93
-rw-r--r--contrib/wpa/wpa_supplicant/hs20_supplicant.c997
-rw-r--r--contrib/wpa/wpa_supplicant/hs20_supplicant.h20
-rw-r--r--contrib/wpa/wpa_supplicant/ibss_rsn.c14
-rw-r--r--contrib/wpa/wpa_supplicant/interworking.c64
-rw-r--r--contrib/wpa/wpa_supplicant/main.c52
-rw-r--r--contrib/wpa/wpa_supplicant/mbo.c6
-rw-r--r--contrib/wpa/wpa_supplicant/mesh.c98
-rw-r--r--contrib/wpa/wpa_supplicant/mesh_mpm.c4
-rw-r--r--contrib/wpa/wpa_supplicant/mesh_rsn.c31
-rw-r--r--contrib/wpa/wpa_supplicant/nan_supplicant.c5003
-rw-r--r--contrib/wpa/wpa_supplicant/nan_supplicant.h252
-rw-r--r--contrib/wpa/wpa_supplicant/nan_usd.c513
-rw-r--r--contrib/wpa/wpa_supplicant/nan_usd.h46
-rw-r--r--contrib/wpa/wpa_supplicant/notify.c591
-rw-r--r--contrib/wpa/wpa_supplicant/notify.h98
-rw-r--r--contrib/wpa/wpa_supplicant/offchannel.c4
-rw-r--r--contrib/wpa/wpa_supplicant/op_classes.c9
-rw-r--r--contrib/wpa/wpa_supplicant/p2p_supplicant.c1914
-rw-r--r--contrib/wpa/wpa_supplicant/p2p_supplicant.h52
-rw-r--r--contrib/wpa/wpa_supplicant/pasn_supplicant.c811
-rw-r--r--contrib/wpa/wpa_supplicant/pr_supplicant.c1807
-rw-r--r--contrib/wpa/wpa_supplicant/pr_supplicant.h148
-rw-r--r--contrib/wpa/wpa_supplicant/preauth_test.c2
-rw-r--r--contrib/wpa/wpa_supplicant/robust_av.c278
-rw-r--r--contrib/wpa/wpa_supplicant/rrm.c121
-rw-r--r--contrib/wpa/wpa_supplicant/scan.c611
-rw-r--r--contrib/wpa/wpa_supplicant/scan.h3
-rw-r--r--contrib/wpa/wpa_supplicant/sme.c2465
-rw-r--r--contrib/wpa/wpa_supplicant/sme.h22
-rw-r--r--contrib/wpa/wpa_supplicant/wnm_sta.c976
-rw-r--r--contrib/wpa/wpa_supplicant/wnm_sta.h5
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_cli.c338
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_passphrase.c10
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.c1510
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.conf96
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant_i.h314
-rw-r--r--contrib/wpa/wpa_supplicant/wpas_glue.c69
-rw-r--r--contrib/wpa/wpa_supplicant/wpas_kay.c1
-rw-r--r--contrib/wpa/wpa_supplicant/wpas_module_tests.c5
-rw-r--r--contrib/wpa/wpa_supplicant/wps_supplicant.c7
-rw-r--r--usr.sbin/wpa/Makefile.inc11
-rw-r--r--usr.sbin/wpa/hostapd/Makefile9
-rw-r--r--usr.sbin/wpa/src/ap/Makefile7
-rw-r--r--usr.sbin/wpa/src/common/Makefile1
-rw-r--r--usr.sbin/wpa/src/crypto/Makefile5
-rw-r--r--usr.sbin/wpa/src/pasn/Makefile3
336 files changed, 89716 insertions, 24553 deletions
diff --git a/contrib/wpa/Makefile b/contrib/wpa/Makefile
new file mode 100644
index 000000000000..c4b1498cff9f
--- /dev/null
+++ b/contrib/wpa/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=$(wildcard hostapd) $(wildcard wpa_supplicant)
+
+all:
+ for dir in $(SUBDIRS); do $(MAKE) -C $$dir; done
+
+clean distclean:
+ for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean; done
+
+install:
+ for dir in $(SUBDIRS); do $(MAKE) -C $$dir install; done
diff --git a/contrib/wpa/README b/contrib/wpa/README
index 8392bb354fac..0addde360ee5 100644
--- a/contrib/wpa/README
+++ b/contrib/wpa/README
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2026, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
diff --git a/contrib/wpa/hostapd/Android.mk b/contrib/wpa/hostapd/Android.mk
index 573564d5b0de..bff81cac4a31 100644
--- a/contrib/wpa/hostapd/Android.mk
+++ b/contrib/wpa/hostapd/Android.mk
@@ -127,7 +127,7 @@ HOBJS += src/utils/trace.c
LDFLAGS += -rdynamic
L_CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
-L_CFLAGS += -DWPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
LIBS += -lbfd
LIBS_c += -lbfd
LIBS_h += -lbfd
@@ -259,9 +259,10 @@ L_CFLAGS += -DCONFIG_SAE
OBJS += src/common/sae.c
ifdef CONFIG_SAE_PK
L_CFLAGS += -DCONFIG_SAE_PK
-NEED_AES_SIV=y
+NEED_BASE64=y
OBJS += src/common/sae_pk.c
endif
+NEED_AES_SIV=y
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -588,6 +589,21 @@ OBJS += src/ap/nan_usd_ap.c
L_CFLAGS += -DCONFIG_NAN_USD
endif
+ifdef CONFIG_PMKSA_PRIVACY
+CONFIG_ENC_ASSOC=y
+L_CFLAGS += -DCONFIG_PMKSA_PRIVACY
+endif
+
+ifdef CONFIG_IEEE8021X_AUTH
+CONFIG_ENC_ASSOC=y
+L_CFLAGS += -DCONFIG_IEEE8021X_AUTH
+endif
+
+ifdef CONFIG_ENC_ASSOC
+CONFIG_PASN=y
+L_CFLAGS += -DCONFIG_ENC_ASSOC
+endif
+
ifdef CONFIG_PASN
L_CFLAGS += -DCONFIG_PASN
L_CFLAGS += -DCONFIG_PTKSA_CACHE
@@ -936,6 +952,20 @@ endif
endif
endif
+ifdef CONFIG_SAE
+ifdef CONFIG_IEEE80211BE
+NEED_SHA384=y
+endif
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
L_CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
@@ -1055,6 +1085,7 @@ OBJS += src/pasn/pasn_common.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
+OBJS += src/ap/interference.c
L_CFLAGS += -DNEED_AP_MLME
endif
OBJS += src/ap/ieee802_11_ht.c
@@ -1125,6 +1156,11 @@ ifdef CONFIG_ANDROID_LOG
L_CFLAGS += -DCONFIG_ANDROID_LOG
endif
+ifdef CONFIG_PROCESS_COORDINATION
+L_CFLAGS += -DCONFIG_PROCESS_COORDINATION
+OBJS += src/common/proc_coord.c
+endif
+
OBJS_c = hostapd_cli.c
OBJS_c += src/common/wpa_ctrl.c
OBJS_c += src/utils/os_$(CONFIG_OS).c
diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog
index 1c8240d333c4..ec26207d8d9d 100644
--- a/contrib/wpa/hostapd/ChangeLog
+++ b/contrib/wpa/hostapd/ChangeLog
@@ -1,5 +1,35 @@
ChangeLog for hostapd
+2026-08-07 - v2.12
+ * support RSN overriding (e.g., WPA3-Personal Compatibility Mode)
+ * EHT/IEEE 802.11be/Wi-Fi 7
+ - more complete support
+ - fix message validation issues that could enable DoS attacks
+ - fix group key rekeying
+ * enable SAE group 20 by default if SAE-EXT-KEY is enabled
+ * reject unexpected SAE password identifier to avoid DoS attack against
+ a specific STA
+ * mandate use of SAE H2E when using password identifiers
+ * assign VLAN when using SAE with PMKSA caching
+ * support SPP A-MSDU negotiation
+ * support IEEE 802.11bi functionality
+ - changing SAE password identifiers
+ - EPPKE
+ - IEEE 802.1X/EAP in Authentication frames
+ - Association frame encryption
+ - PMKID privacy
+ * remove the driver interface for now obsolete Host AP driver
+ * remove the driver interface for now obsolete Atheros WEXT interface
+ * move supported, basic, and Beacon TX rate configuration to be at BSS
+ level instead of per-radio for all BSSs
+ * fix various issues in Multiple-BSSID functionality
+ * support OpenSSL 3.0 API changes
+ * EAP-TEAP: protocol changes based on RFC 9930; this is not compatible
+ with previous versions
+ * support Automated Frequency Coordination (AFC) on the 6 GHz band
+ * improve GAS/ANQP processing to support larger ANQP responses
+ * a large number of other fixes, cleanup, and extensions
+
2024-07-20 - v2.11
* Wi-Fi Easy Connect
- add support for DPP release 3
diff --git a/contrib/wpa/hostapd/Makefile b/contrib/wpa/hostapd/Makefile
index ca4439234a11..f6fee0b46a33 100644
--- a/contrib/wpa/hostapd/Makefile
+++ b/contrib/wpa/hostapd/Makefile
@@ -116,7 +116,7 @@ HOBJS += ../src/utils/trace.o
LDFLAGS += -rdynamic
CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls
LIBS += -lbfd -ldl -liberty -lz
LIBS_c += -lbfd -ldl -liberty -lz
LIBS_h += -lbfd -ldl -liberty -lz
@@ -298,9 +298,10 @@ CFLAGS += -DCONFIG_SAE
OBJS += ../src/common/sae.o
ifdef CONFIG_SAE_PK
CFLAGS += -DCONFIG_SAE_PK
-NEED_AES_SIV=y
+NEED_BASE64=y
OBJS += ../src/common/sae_pk.o
endif
+NEED_AES_SIV=y
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -354,6 +355,14 @@ CFLAGS += -DCONFIG_IEEE80211AX
OBJS += ../src/ap/ieee802_11_he.o
endif
+ifdef CONFIG_IEEE80211AX
+ifdef CONFIG_AFC
+CFLAGS += -DCONFIG_AFC
+OBJS += ../src/ap/afc.o
+NEED_JSON=y
+endif
+endif
+
ifdef CONFIG_MBO
CFLAGS += -DCONFIG_MBO
OBJS += ../src/ap/mbo_ap.o
@@ -614,6 +623,21 @@ OBJS += ../src/ap/nan_usd_ap.o
CFLAGS += -DCONFIG_NAN_USD
endif
+ifdef CONFIG_PMKSA_PRIVACY
+CONFIG_ENC_ASSOC=y
+CFLAGS += -DCONFIG_PMKSA_PRIVACY
+endif
+
+ifdef CONFIG_IEEE8021X_AUTH
+CONFIG_ENC_ASSOC=y
+CFLAGS += -DCONFIG_IEEE8021X_AUTH
+endif
+
+ifdef CONFIG_ENC_ASSOC
+CONFIG_PASN=y
+CFLAGS += -DCONFIG_ENC_ASSOC
+endif
+
ifdef CONFIG_PASN
CFLAGS += -DCONFIG_PASN
CFLAGS += -DCONFIG_PTKSA_CACHE
@@ -708,6 +732,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
@@ -1065,6 +1090,20 @@ endif
endif
endif
+ifdef CONFIG_SAE
+ifdef CONFIG_IEEE80211BE
+NEED_SHA384=y
+endif
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
@@ -1201,6 +1240,7 @@ OBJS += ../src/pasn/pasn_common.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
+OBJS += ../src/ap/interference.o
CFLAGS += -DNEED_AP_MLME
endif
OBJS += ../src/ap/ieee802_11_ht.o
@@ -1298,6 +1338,11 @@ ifdef CONFIG_NO_TKIP
CFLAGS += -DCONFIG_NO_TKIP
endif
+ifdef CONFIG_PROCESS_COORDINATION
+CFLAGS += -DCONFIG_PROCESS_COORDINATION
+OBJS += ../src/common/proc_coord.o
+endif
+
$(DESTDIR)$(BINDIR)/%: %
install -D $(<) $(@)
@@ -1365,6 +1410,16 @@ SOBJS += ../src/common/sae.o
SOBJS += ../src/common/sae_pk.o
SOBJS += ../src/common/dragonfly.o
SOBJS += $(AESOBJS)
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
+SOBJS += ../src/crypto/sha384.o
+SOBJS += ../src/crypto/sha512.o
+endif
+endif
+endif
+endif
SOBJS += ../src/crypto/sha256-prf.o
SOBJS += ../src/crypto/sha384-prf.o
SOBJS += ../src/crypto/sha512-prf.o
@@ -1372,6 +1427,14 @@ SOBJS += ../src/crypto/dh_groups.o
SOBJS += ../src/crypto/sha256-kdf.o
SOBJS += ../src/crypto/sha384-kdf.o
SOBJS += ../src/crypto/sha512-kdf.o
+SOBJS += ../src/common/wpa_common.o
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+SOBJS += ../src/crypto/random.o
+endif
+SOBJS += ../src/crypto/sha1-prf.o
+SOBJS += ../src/utils/eloop.o
_OBJS_VAR := NOBJS
include ../src/objs.mk
diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README
index 1a0248fce422..89f65d58eae6 100644
--- a/contrib/wpa/hostapd/README
+++ b/contrib/wpa/hostapd/README
@@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
Authenticator and RADIUS authentication server
================================================================
-Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2026, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
@@ -69,11 +69,6 @@ Requirements
Current hardware/software requirements:
- drivers:
- Host AP driver for Prism2/2.5/3.
- (http://w1.fi/hostap-driver.html)
- Please note that station firmware version needs to be 1.7.0 or newer
- to work in WPA mode.
-
mac80211-based drivers that support AP mode (with driver=nl80211).
This includes drivers for Atheros (ath9k) and Broadcom (b43)
chipsets.
@@ -125,7 +120,7 @@ use an external Authentication Server and encapsulate EAP frame in the
frames used by that server. RADIUS is suitable for this, but IEEE
802.1X would also allow other mechanisms.
-Host AP driver includes PAE functionality in the kernel driver. It
+Drivers include PAE functionality in the kernel driver. It
is a relatively simple mechanism for denying normal frames going to
or coming from an unauthorized port. PAE allows IEEE 802.1X related
frames to be passed between the Supplicant and the Authenticator even
@@ -156,7 +151,7 @@ with the Authentication Server. After this is completed successfully,
the virtual port is set to Authorized state and frames from and to the
station are accepted.
-Host AP configuration for IEEE 802.1X
+hostapd configuration for IEEE 802.1X
-------------------------------------
The user space daemon has its own configuration file that can be used to
diff --git a/contrib/wpa/hostapd/README-MULTI-AP b/contrib/wpa/hostapd/README-MULTI-AP
index ccee69e13c08..6190cace4aa1 100644
--- a/contrib/wpa/hostapd/README-MULTI-AP
+++ b/contrib/wpa/hostapd/README-MULTI-AP
@@ -155,6 +155,6 @@ References
----------
[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
-[2] https://github.com/prplfoundation/prplMesh
+[2] https://gitlab.com/prpl-foundation/prplmesh/prplMesh
[3] https://www.wi-fi.org/file/multi-ap-specification-v10
(requires registration)
diff --git a/contrib/wpa/hostapd/android.config b/contrib/wpa/hostapd/android.config
index 522de87266d5..133a8111286d 100644
--- a/contrib/wpa/hostapd/android.config
+++ b/contrib/wpa/hostapd/android.config
@@ -9,9 +9,6 @@
# be modified from here. In most cass, these lines should use += in order not
# to override previous values of the variables.
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
-
# Driver interface for wired authenticator
#CONFIG_DRIVER_WIRED=y
diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c
index 3fb059770d49..59684808c794 100644
--- a/contrib/wpa/hostapd/config_file.c
+++ b/contrib/wpa/hostapd/config_file.c
@@ -713,14 +713,14 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
else if (os_strcmp(start, "DPP") == 0)
val |= WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
- else if (os_strcmp(start, "OSEN") == 0)
- val |= WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
#ifdef CONFIG_PASN
else if (os_strcmp(start, "PASN") == 0)
val |= WPA_KEY_MGMT_PASN;
#endif /* CONFIG_PASN */
+#ifdef CONFIG_ENC_ASSOC
+ else if (os_strcmp(start, "EPPKE") == 0)
+ val |= WPA_KEY_MGMT_EPPKE;
+#endif /* CONFIG_ENC_ASSOC */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -865,7 +865,7 @@ static int hostapd_parse_intlist(int **int_list, char *val)
break;
pos = end + 1;
}
- list[count] = -1;
+ list[count] = 0;
*int_list = list;
return 0;
@@ -1269,7 +1269,7 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val)
*end = '\0';
bitpos = atoi(pos);
- if (bitpos < 0 || bitpos > 64)
+ if (bitpos < 0 || bitpos > 63)
return -1;
bitmap[bitpos / 8] |= BIT(bitpos % 8);
@@ -1281,6 +1281,140 @@ 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 afc_cert_id *c = NULL, *tmp;
+ unsigned int i, count = 0;
+
+ while (pos && pos[0]) {
+ char *p;
+
+ tmp = os_realloc_array(c, count + 1, sizeof(*c));
+ if (!tmp)
+ goto error;
+ c = tmp;
+
+ i = count;
+ count++;
+ os_memset(&c[i], 0, sizeof(struct afc_cert_id));
+
+ p = os_strchr(pos, ':');
+ if (!p)
+ goto error;
+
+ *p++ = '\0';
+ if (!p || !p[0])
+ goto error;
+
+ c[i].ruleset = os_strdup(pos);
+ if (!c[i].ruleset)
+ goto error;
+
+ pos = p;
+ p = os_strchr(pos, ',');
+ if (p)
+ *p++ = '\0';
+
+ c[i].id = os_strdup(pos);
+ if (!c[i].id)
+ goto error;
+
+ pos = p;
+ }
+
+ hostapd_config_free_afc_cert_ids(conf);
+
+ conf->afc.n_cert_ids = count;
+ conf->afc.cert_ids = c;
+
+ return 0;
+
+error:
+ for (i = 0; i < count; i++) {
+ os_free(c[i].ruleset);
+ 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, *tmp;
+ unsigned int count = 0;
+
+ while (pos && pos[0]) {
+ char *p, *end;
+
+ tmp = os_realloc_array(d, count + 1, sizeof(*d));
+ if (!tmp)
+ goto error;
+ d = tmp;
+
+ p = os_strchr(pos, ':');
+ if (!p)
+ goto error;
+
+ *p++ = '\0';
+ if (!p || !p[0])
+ goto error;
+
+ d[count].longitude = strtod(pos, &end);
+ if (*end)
+ goto error;
+
+ pos = p;
+ p = os_strchr(pos, ',');
+ if (p)
+ *p++ = '\0';
+
+ d[count].latitude = strtod(pos, &end);
+ if (*end)
+ goto error;
+
+ pos = p;
+ count++;
+ }
+
+ os_free(*data);
+ *n_linear_polygon_data = count;
+ *data = d;
+
+ return 0;
+
+error:
+ os_free(d);
+ return -ENOMEM;
+}
+
+
+static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
+{
+ os_free(conf->afc.op_class);
+ conf->afc.op_class = NULL;
+
+ while (pos && pos[0]) {
+ char *p;
+
+ p = os_strchr(pos, ',');
+ if (p)
+ *p++ = '\0';
+
+ int_array_add_unique(&conf->afc.op_class, atoi(pos));
+ pos = p;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_AFC */
#endif /* CONFIG_IEEE80211AX */
@@ -1834,233 +1968,6 @@ static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
return 0;
}
-
-static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
-{
- struct hs20_icon *icon;
- char *end;
-
- icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
- sizeof(struct hs20_icon));
- if (icon == NULL)
- return -1;
- bss->hs20_icons = icon;
- icon = &bss->hs20_icons[bss->hs20_icons_count];
- os_memset(icon, 0, sizeof(*icon));
-
- icon->width = atoi(pos);
- pos = os_strchr(pos, ':');
- if (pos == NULL)
- return -1;
- pos++;
-
- icon->height = atoi(pos);
- pos = os_strchr(pos, ':');
- if (pos == NULL)
- return -1;
- pos++;
-
- end = os_strchr(pos, ':');
- if (end == NULL || end - pos > 3)
- return -1;
- os_memcpy(icon->language, pos, end - pos);
- pos = end + 1;
-
- end = os_strchr(pos, ':');
- if (end == NULL || end - pos > 255)
- return -1;
- os_memcpy(icon->type, pos, end - pos);
- pos = end + 1;
-
- end = os_strchr(pos, ':');
- if (end == NULL || end - pos > 255)
- return -1;
- os_memcpy(icon->name, pos, end - pos);
- pos = end + 1;
-
- if (os_strlen(pos) > 255)
- return -1;
- os_memcpy(icon->file, pos, os_strlen(pos));
-
- bss->hs20_icons_count++;
-
- return 0;
-}
-
-
-static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- size_t slen;
- char *str;
-
- str = wpa_config_parse_string(pos, &slen);
- if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
- os_free(str);
- return -1;
- }
-
- os_memcpy(bss->osu_ssid, str, slen);
- bss->osu_ssid_len = slen;
- os_free(str);
-
- return 0;
-}
-
-
-static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- struct hs20_osu_provider *p;
-
- p = os_realloc_array(bss->hs20_osu_providers,
- bss->hs20_osu_providers_count + 1, sizeof(*p));
- if (p == NULL)
- return -1;
-
- bss->hs20_osu_providers = p;
- bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
- bss->hs20_osu_providers_count++;
- os_memset(bss->last_osu, 0, sizeof(*p));
- bss->last_osu->server_uri = os_strdup(pos);
-
- return 0;
-}
-
-
-static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- if (bss->last_osu == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- if (parse_lang_string(&bss->last_osu->friendly_name,
- &bss->last_osu->friendly_name_count, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
- line, pos);
- return -1;
- }
-
- return 0;
-}
-
-
-static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- if (bss->last_osu == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- os_free(bss->last_osu->osu_nai);
- bss->last_osu->osu_nai = os_strdup(pos);
- if (bss->last_osu->osu_nai == NULL)
- return -1;
-
- return 0;
-}
-
-
-static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- if (bss->last_osu == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- os_free(bss->last_osu->osu_nai2);
- bss->last_osu->osu_nai2 = os_strdup(pos);
- if (bss->last_osu->osu_nai2 == NULL)
- return -1;
- bss->hs20_osu_providers_nai_count++;
-
- return 0;
-}
-
-
-static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
- int line)
-{
- if (bss->last_osu == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
- return -1;
- }
-
- return 0;
-}
-
-
-static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
- int line)
-{
- char **n;
- struct hs20_osu_provider *p = bss->last_osu;
-
- if (p == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
- if (n == NULL)
- return -1;
- p->icons = n;
- p->icons[p->icons_count] = os_strdup(pos);
- if (p->icons[p->icons_count] == NULL)
- return -1;
- p->icons_count++;
-
- return 0;
-}
-
-
-static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
- char *pos, int line)
-{
- if (bss->last_osu == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
- return -1;
- }
-
- if (parse_lang_string(&bss->last_osu->service_desc,
- &bss->last_osu->service_desc_count, pos)) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
- line, pos);
- return -1;
- }
-
- return 0;
-}
-
-
-static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
- int line)
-{
- char **n;
-
- n = os_realloc_array(bss->hs20_operator_icon,
- bss->hs20_operator_icon_count + 1, sizeof(char *));
- if (!n)
- return -1;
- bss->hs20_operator_icon = n;
- bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
- if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
- return -1;
- bss->hs20_operator_icon_count++;
-
- return 0;
-}
-
#endif /* CONFIG_HS20 */
@@ -2436,6 +2343,31 @@ static int get_u16(const char *pos, int line, u16 *ret_val)
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+static bool get_hexstream(const char *val, struct wpabuf **var,
+ const char *name, int line)
+{
+ struct wpabuf *tmp;
+ size_t len = os_strlen(val) / 2;
+
+ tmp = wpabuf_alloc(len);
+ if (!tmp)
+ return false;
+
+ if (hexstr2bin(val, wpabuf_put(tmp, len), len)) {
+ wpabuf_free(tmp);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'",
+ line, name, val);
+ return false;
+ }
+
+ wpabuf_free(*var);
+ *var = tmp;
+ return true;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
const char *buf, char *pos, int line)
@@ -2731,8 +2663,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
bss->eap_teap_auth = val;
- } else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
- bss->eap_teap_pac_no_inner = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_separate_result") == 0) {
bss->eap_teap_separate_result = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_id") == 0) {
@@ -3073,6 +3003,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
bss->extended_key_id = val;
+#ifdef CONFIG_ENC_ASSOC
+ } else if (os_strcmp(buf, "assoc_frame_encryption") == 0) {
+ bss->assoc_frame_encryption = atoi(pos);
+ } else if (os_strcmp(buf, "pmksa_caching_privacy") == 0) {
+ bss->pmksa_caching_privacy = atoi(pos);
+ } else if (os_strcmp(buf, "eap_using_authentication_frames") == 0) {
+ bss->eap_using_authentication_frames = atoi(pos);
+#endif /* CONFIG_ENC_ASSOC */
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1;
@@ -3156,6 +3094,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
if (bss->wpa_key_mgmt == -1)
return 1;
+ } else if (os_strcmp(buf, "rsn_override_key_mgmt") == 0) {
+ bss->rsn_override_key_mgmt =
+ hostapd_config_parse_key_mgmt(line, pos);
+ if (bss->rsn_override_key_mgmt == -1)
+ return 1;
+ } else if (os_strcmp(buf, "rsn_override_key_mgmt_2") == 0) {
+ bss->rsn_override_key_mgmt_2 =
+ hostapd_config_parse_key_mgmt(line, pos);
+ if (bss->rsn_override_key_mgmt_2 == -1)
+ return 1;
} else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
bss->wpa_psk_radius = atoi(pos);
if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
@@ -3187,6 +3135,32 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "rsn_override_pairwise") == 0) {
+ bss->rsn_override_pairwise =
+ hostapd_config_parse_cipher(line, pos);
+ if (bss->rsn_override_pairwise == -1 ||
+ bss->rsn_override_pairwise == 0)
+ return 1;
+ if (bss->rsn_override_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unsupported pairwise cipher suite '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "rsn_override_pairwise_2") == 0) {
+ bss->rsn_override_pairwise_2 =
+ hostapd_config_parse_cipher(line, pos);
+ if (bss->rsn_override_pairwise_2 == -1 ||
+ bss->rsn_override_pairwise_2 == 0)
+ return 1;
+ if (bss->rsn_override_pairwise_2 &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unsupported pairwise cipher suite '%s'",
+ line, pos);
+ return 1;
+ }
} else if (os_strcmp(buf, "group_cipher") == 0) {
bss->group_cipher = hostapd_config_parse_cipher(line, pos);
if (bss->group_cipher == -1 || bss->group_cipher == 0)
@@ -3208,6 +3182,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->rsn_preauth_interfaces);
bss->rsn_preauth_interfaces = os_strdup(pos);
#endif /* CONFIG_RSN_PREAUTH */
+ } else if (os_strcmp(buf, "rsn_override_omit_rsnxe") == 0) {
+ bss->rsn_override_omit_rsnxe = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_override_mlo_compat") == 0) {
+ bss->rsn_override_mlo_compat = atoi(pos);
} else if (os_strcmp(buf, "peerkey") == 0) {
wpa_printf(MSG_INFO,
"Line %d: Obsolete peerkey parameter ignored", line);
@@ -3319,6 +3297,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->radius_server_auth_port = atoi(pos);
} else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
bss->radius_server_acct_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_server_acct_log") == 0) {
+ bss->radius_server_acct_log = atoi(pos);
} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
bss->radius_server_ipv6 = atoi(pos);
#endif /* RADIUS_SERVER */
@@ -3494,18 +3474,19 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
bss->send_probe_response = val;
} else if (os_strcmp(buf, "supported_rates") == 0) {
- if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
+ if (hostapd_parse_intlist(&bss->supported_rates, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
line);
return 1;
}
} else if (os_strcmp(buf, "basic_rates") == 0) {
- if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
+ if (hostapd_parse_intlist(&bss->basic_rates, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
line);
return 1;
}
} else if (os_strcmp(buf, "beacon_rate") == 0) {
+ enum beacon_rate_type rate_type;
int val;
if (os_strncmp(pos, "ht:", 3) == 0) {
@@ -3516,8 +3497,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, val);
return 1;
}
- conf->rate_type = BEACON_RATE_HT;
- conf->beacon_rate = val;
+ rate_type = BEACON_RATE_HT;
} else if (os_strncmp(pos, "vht:", 4) == 0) {
val = atoi(pos + 4);
if (val < 0 || val > 9) {
@@ -3526,8 +3506,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, val);
return 1;
}
- conf->rate_type = BEACON_RATE_VHT;
- conf->beacon_rate = val;
+ rate_type = BEACON_RATE_VHT;
} else if (os_strncmp(pos, "he:", 3) == 0) {
val = atoi(pos + 3);
if (val < 0 || val > 11) {
@@ -3536,8 +3515,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, val);
return 1;
}
- conf->rate_type = BEACON_RATE_HE;
- conf->beacon_rate = val;
+ rate_type = BEACON_RATE_HE;
} else {
val = atoi(pos);
if (val < 10 || val > 10000) {
@@ -3546,9 +3524,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, val);
return 1;
}
- conf->rate_type = BEACON_RATE_LEGACY;
- conf->beacon_rate = val;
+ rate_type = BEACON_RATE_LEGACY;
}
+
+ bss->rate_type = rate_type;
+ bss->beacon_rate = val;
} else if (os_strcmp(buf, "preamble") == 0) {
if (atoi(pos))
conf->preamble = SHORT_PREAMBLE;
@@ -3642,6 +3622,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->use_driver_iface_addr = atoi(pos);
} else if (os_strcmp(buf, "ieee80211w") == 0) {
bss->ieee80211w = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_override_mfp") == 0) {
+ bss->rsn_override_mfp = atoi(pos);
+ } else if (os_strcmp(buf, "rsn_override_mfp_2") == 0) {
+ bss->rsn_override_mfp_2 = atoi(pos);
+ } else if (os_strcmp(buf, "spp_amsdu") == 0) {
+ bss->spp_amsdu = !!atoi(pos);
} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
if (os_strcmp(pos, "AES-128-CMAC") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
@@ -3888,6 +3874,89 @@ 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) {
+ os_free(conf->afc.socket);
+ conf->afc.socket = os_strdup(pos);
+ } else if (os_strcmp(buf, "afc_request_version") == 0) {
+ os_free(conf->afc.request.version);
+ conf->afc.request.version = os_strdup((pos));
+ } else if (os_strcmp(buf, "afc_serial_number") == 0) {
+ os_free(conf->afc.request.sn);
+ conf->afc.request.sn = os_strdup((pos));
+ } else if (os_strcmp(buf, "afc_cert_ids") == 0) {
+ if (hostapd_afc_parse_cert_ids(conf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid afc_cert_ids",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "afc_location_type") == 0) {
+ conf->afc.location.type = atoi(pos);
+ if (conf->afc.location.type != AFC_ELLIPSE &&
+ conf->afc.location.type != AFC_LINEAR_POLYGON &&
+ conf->afc.location.type != AFC_RADIAL_POLYGON) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid afc_location_type value",
+ line);
+ 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)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid afc_linear_polygon",
+ line);
+ 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)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid afc_radial_polygon",
+ line);
+ 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) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid afc_height value",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "afc_height_type") == 0) {
+ os_free(conf->afc.location.height_type);
+ conf->afc.location.height_type = os_strdup(pos);
+ } 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 (freq_range_list_parse(&conf->afc.freqs, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid afc_freq_range",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "afc_op_class") == 0) {
+ if (hostapd_afc_parse_op_class(conf, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid afc_op_class",
+ line);
+ return 1;
+ }
+#endif /* CONFIG_AFC */
} else if (os_strcmp(buf, "mbssid") == 0) {
int mbssid = atoi(pos);
if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
@@ -3897,6 +3966,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
conf->mbssid = mbssid;
+ } else if (os_strcmp(buf, "mbssid_index") == 0) {
+ bss->mbssid_index = atoi(pos);
+ } else if (os_strcmp(buf, "mbssid_max") == 0) {
+ conf->mbssid_max = atoi(pos);
#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
@@ -4248,12 +4321,24 @@ static int hostapd_config_fill(struct hostapd_config *conf,
tok_start = os_strchr(tok_prev, ',');
if (tok_start) {
domain_len = tok_start - tok_prev;
+ if (domain_len > 255) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: domain_name is too long (max 255)", line);
+ os_free(domain_list);
+ return 1;
+ }
*domain_ptr = domain_len;
os_memcpy(domain_ptr + 1, tok_prev, domain_len);
domain_ptr += domain_len + 1;
tok_prev = ++tok_start;
} else {
domain_len = os_strlen(tok_prev);
+ if (domain_len > 255) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: domain_name is too long (max 255)", line);
+ os_free(domain_list);
+ return 1;
+ }
*domain_ptr = domain_len;
os_memcpy(domain_ptr + 1, tok_prev, domain_len);
domain_ptr += domain_len + 1;
@@ -4314,8 +4399,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->disable_dgaf = atoi(pos);
} else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
bss->na_mcast_to_ucast = atoi(pos);
- } else if (os_strcmp(buf, "osen") == 0) {
- bss->osen = atoi(pos);
} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
bss->anqp_domain_id = atoi(pos);
} else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
@@ -4354,44 +4437,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->hs20_operating_class);
bss->hs20_operating_class = oper_class;
bss->hs20_operating_class_len = oper_class_len;
- } else if (os_strcmp(buf, "hs20_icon") == 0) {
- if (hs20_parse_icon(bss, pos) < 0) {
- wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
- line, pos);
- return 1;
- }
- } else if (os_strcmp(buf, "osu_ssid") == 0) {
- if (hs20_parse_osu_ssid(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_server_uri") == 0) {
- if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
- if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_nai") == 0) {
- if (hs20_parse_osu_nai(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_nai2") == 0) {
- if (hs20_parse_osu_nai2(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_method_list") == 0) {
- if (hs20_parse_osu_method_list(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_icon") == 0) {
- if (hs20_parse_osu_icon(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "osu_service_desc") == 0) {
- if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "operator_icon") == 0) {
- if (hs20_parse_operator_icon(bss, pos, line) < 0)
- return 1;
- } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
- os_free(bss->subscr_remediation_url);
- bss->subscr_remediation_url = os_strdup(pos);
- } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
- bss->subscr_remediation_method = atoi(pos);
} else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
os_free(bss->t_c_filename);
bss->t_c_filename = os_strdup(pos);
@@ -4400,9 +4445,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
os_free(bss->t_c_server_url);
bss->t_c_server_url = os_strdup(pos);
- } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
- os_free(bss->hs20_sim_provisioning_url);
- bss->hs20_sim_provisioning_url = os_strdup(pos);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
} else if (os_strcmp(buf, "mbo") == 0) {
@@ -4432,6 +4474,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
} else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
conf->ecsa_ie_only = atoi(pos);
+ } else if (os_strcmp(buf, "csa_ie_only") == 0) {
+ conf->csa_ie_only = atoi(pos);
} else if (os_strcmp(buf, "bss_load_test") == 0) {
WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
pos = os_strchr(pos, ':');
@@ -4462,23 +4506,29 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_NEIGHBOR_REPORT;
} else if (os_strcmp(buf, "own_ie_override") == 0) {
- struct wpabuf *tmp;
- size_t len = os_strlen(pos) / 2;
-
- tmp = wpabuf_alloc(len);
- if (!tmp)
+ if (!get_hexstream(pos, &bss->own_ie_override,
+ "own_ie_override", line))
return 1;
-
- if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
- wpabuf_free(tmp);
- wpa_printf(MSG_ERROR,
- "Line %d: Invalid own_ie_override '%s'",
- line, pos);
+ } else if (os_strcmp(buf, "rsne_override") == 0) {
+ if (!get_hexstream(pos, &bss->rsne_override,
+ "rsne_override", line))
+ return 1;
+ } else if (os_strcmp(buf, "rsnoe_override") == 0) {
+ if (!get_hexstream(pos, &bss->rsnoe_override,
+ "rsnoe_override", line))
+ return 1;
+ } else if (os_strcmp(buf, "rsno2e_override") == 0) {
+ if (!get_hexstream(pos, &bss->rsno2e_override,
+ "rsno2e_override", line))
+ return 1;
+ } else if (os_strcmp(buf, "rsnxe_override") == 0) {
+ if (!get_hexstream(pos, &bss->rsnxe_override,
+ "rsnxe_override", line))
+ return 1;
+ } else if (os_strcmp(buf, "rsnxoe_override") == 0) {
+ if (!get_hexstream(pos, &bss->rsnxoe_override,
+ "rsnxoe_override", line))
return 1;
- }
-
- wpabuf_free(bss->own_ie_override);
- bss->own_ie_override = tmp;
} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
bss->sae_reflection_attack = atoi(pos);
} else if (os_strcmp(buf, "sae_commit_status") == 0) {
@@ -4540,6 +4590,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
} else if (os_strcmp(buf, "eapol_m3_no_encrypt") == 0) {
bss->eapol_m3_no_encrypt = atoi(pos);
+ } else if (os_strcmp(buf, "eapol_key_reserved_random") == 0) {
+ bss->eapol_key_reserved_random = atoi(pos);
} else if (os_strcmp(buf, "test_assoc_comeback_type") == 0) {
bss->test_assoc_comeback_type = atoi(pos);
} else if (os_strcmp(buf, "presp_elements") == 0) {
@@ -4560,6 +4612,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line);
return 1;
}
+ } else if (os_strcmp(buf, "sae_password_psk") == 0) {
+ bss->sae_password_psk = atoi(pos);
+ } else if (os_strcmp(buf, "sae_track_password") == 0) {
+ bss->sae_track_password = atoi(pos);
#endif /* CONFIG_SAE */
} else if (os_strcmp(buf, "vendor_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
@@ -4585,6 +4641,13 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->sae_confirm_immediate = atoi(pos);
} else if (os_strcmp(buf, "sae_pwe") == 0) {
bss->sae_pwe = atoi(pos);
+ } else if (os_strcmp(buf, "sae_accept_h2e_without_use") == 0) {
+ bss->sae_accept_h2e_without_use = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pw_id_num") == 0) {
+ bss->sae_pw_id_num = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pw_id_key") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->sae_pw_id_key, pos))
+ return 1;
} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
int val = atoi(pos);
if (val < 0 || val > 255) {
@@ -4674,6 +4737,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
wpabuf_free(conf->lci);
conf->lci = NULL;
}
+ if (conf->lci) {
+ /* Enable LCI capability in RM Enabled Capabilities
+ * element */
+ bss->radio_measurements[1] |=
+ WLAN_RRM_CAPS_LCI_MEASUREMENT;
+ } else {
+ bss->radio_measurements[1] &=
+ ~WLAN_RRM_CAPS_LCI_MEASUREMENT;
+ }
} else if (os_strcmp(buf, "civic") == 0) {
wpabuf_free(conf->civic);
conf->civic = wpabuf_parse_bin(pos);
@@ -4681,6 +4753,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
wpabuf_free(conf->civic);
conf->civic = NULL;
}
+ if (conf->civic) {
+ /* Enable civic location capability in RM Enabled
+ * Capabilities element */
+ bss->radio_measurements[4] |=
+ WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT;
+ } else {
+ bss->radio_measurements[4] &=
+ ~WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT;
+ }
} else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
if (atoi(pos))
bss->radio_measurements[0] |=
@@ -4980,6 +5061,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
bss->macsec_csindex = macsec_csindex;
+ } else if (os_strcmp(buf, "macsec_icv_indicator") == 0) {
+ int macsec_icv_indicator = atoi(pos);
+
+ if (macsec_icv_indicator < 0 || macsec_icv_indicator > 1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid macsec_icv_indicator (%d): '%s'.",
+ line, macsec_icv_indicator, pos);
+ return 1;
+ }
+ bss->macsec_icv_indicator = macsec_icv_indicator;
} else if (os_strcmp(buf, "mka_cak") == 0) {
size_t len = os_strlen(pos);
@@ -5024,6 +5115,17 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->force_kdk_derivation = atoi(pos);
} else if (os_strcmp(buf, "pasn_corrupt_mic") == 0) {
bss->pasn_corrupt_mic = atoi(pos);
+ } else if (os_strcmp(buf, "pasn_test_groups") == 0) {
+ int *groups = NULL;
+
+ if (hostapd_parse_intlist(&groups, pos) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid pasn_test_groups value '%s'",
+ line, pos);
+ return 1;
+ }
+ os_free(bss->pasn_test_groups);
+ bss->pasn_test_groups = groups;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strcmp(buf, "pasn_groups") == 0) {
if (hostapd_parse_intlist(&bss->pasn_groups, pos)) {
@@ -5036,6 +5138,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->pasn_comeback_after = atoi(pos);
} else if (os_strcmp(buf, "pasn_noauth") == 0) {
bss->pasn_noauth = atoi(pos);
+#ifdef CONFIG_ENC_ASSOC
+ } else if (os_strcmp(buf, "eppke_unauth") == 0) {
+ bss->eppke_unauth = atoi(pos);
+#endif /* CONFIG_ENC_ASSOC */
+ } else if (os_strcmp(buf, "urnm_mfpr") == 0) {
+ bss->urnm_mfpr = !!atoi(pos);
+ } else if (os_strcmp(buf, "urnm_mfpr_x20") == 0) {
+ bss->urnm_mfpr_x20 = !!atoi(pos);
#endif /* CONFIG_PASN */
} else if (os_strcmp(buf, "ext_capa_mask") == 0) {
if (get_hex_config(bss->ext_capa_mask, EXT_CAPA_MAX_LEN,
@@ -5053,9 +5163,23 @@ static int hostapd_config_fill(struct hostapd_config *conf,
if (val < 0 || val > 1)
return 1;
bss->ssid_protection = val;
+ } else if (os_strcmp(buf, "known_sta_identification") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 1)
+ return 1;
+ bss->known_sta_identification = val;
+ } else if (os_strcmp(buf, "channel_usage") == 0) {
+ conf->channel_usage = atoi(pos);
+ } else if (os_strcmp(buf, "peer_to_peer_twt") == 0) {
+ conf->peer_to_peer_twt = atoi(pos);
#ifdef CONFIG_IEEE80211BE
} else if (os_strcmp(buf, "ieee80211be") == 0) {
conf->ieee80211be = atoi(pos);
+ } else if (os_strcmp(buf, "require_eht") == 0) {
+ conf->require_eht = atoi(pos);
+ } else if (os_strcmp(buf, "bss_require_eht") == 0) {
+ bss->bss_require_eht = atoi(pos);
} else if (os_strcmp(buf, "eht_oper_chwidth") == 0) {
conf->eht_oper_chwidth = atoi(pos);
} else if (os_strcmp(buf, "eht_oper_centr_freq_seg0_idx") == 0) {
@@ -5097,8 +5221,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
} else if (os_strcmp(buf, "mld_indicate_disabled") == 0) {
bss->mld_indicate_disabled = atoi(pos);
+ } else if (os_strcmp(buf, "disable_mcs15_rx") == 0) {
+ conf->disable_mcs15_rx = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
+ } else if (os_strcmp(buf, "i2r_lmr_policy") == 0) {
+ conf->i2r_lmr_policy = atoi(pos);
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c
index 39b9ef59dc57..e6ef61508325 100644
--- a/contrib/wpa/hostapd/ctrl_iface.c
+++ b/contrib/wpa/hostapd/ctrl_iface.c
@@ -30,6 +30,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/module_tests.h"
+#include "utils/trace.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/ctrl_iface_common.h"
@@ -38,8 +39,8 @@
#endif /* CONFIG_DPP */
#include "common/wpa_ctrl.h"
#include "common/ptksa_cache.h"
-#include "common/hw_features_common.h"
#include "common/nan_de.h"
+#include "common/proc_coord.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
@@ -596,31 +597,8 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
#endif /* CONFIG_WPS */
-#ifdef CONFIG_HS20
-
-static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
- const char *cmd)
-{
- u8 addr[ETH_ALEN];
- const char *url;
-
- if (hwaddr_aton(cmd, addr))
- return -1;
- url = cmd + 17;
- if (*url == '\0') {
- url = NULL;
- } else {
- if (*url != ' ')
- return -1;
- url++;
- if (*url == '\0')
- url = NULL;
- }
-
- return hs20_send_wnm_notification(hapd, addr, 1, url);
-}
-
+#ifdef CONFIG_HS20
static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
const char *cmd)
{
@@ -669,7 +647,6 @@ static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
wpabuf_free(req);
return ret;
}
-
#endif /* CONFIG_HS20 */
@@ -1240,6 +1217,16 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
hapd->ext_mgmt_frame_handling = atoi(value);
} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
hapd->ext_eapol_frame_io = atoi(value);
+ } else if (os_strcasecmp(cmd, "association_response_status_code") == 0)
+ {
+ if (os_strcasecmp(value, "disable") == 0)
+ hapd->conf->association_response_status_code = -1;
+ else
+ hapd->conf->association_response_status_code =
+ atoi(value);
+ wpa_printf(MSG_DEBUG,
+ "TESTING: association_response_status_code=%d",
+ hapd->conf->association_response_status_code);
} else if (os_strcasecmp(cmd, "force_backlog_bytes") == 0) {
hapd->force_backlog_bytes = atoi(value);
#ifdef CONFIG_DPP
@@ -1313,7 +1300,6 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
hostapd_neighbor_sync_own_report(hapd);
} else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
os_strncmp(cmd, "wmm_ac_", 7) == 0) {
- hapd->parameter_set_count++;
if (ieee802_11_update_beacons(hapd->iface))
wpa_printf(MSG_DEBUG,
"Failed to update beacons with WMM parameters");
@@ -2453,198 +2439,6 @@ static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
#endif /* CONFIG_TESTING_OPTIONS */
-#ifdef NEED_AP_MLME
-static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
- u16 punct_bitmap)
-{
- u32 start_freq;
-
- if (is_6ghz_freq(params->freq)) {
- const int bw_idx[] = { 20, 40, 80, 160, 320 };
- int idx, bw;
-
- /* The 6 GHz band requires HE to be enabled. */
- params->he_enabled = 1;
-
- if (params->center_freq1) {
- if (params->freq == 5935)
- idx = (params->center_freq1 - 5925) / 5;
- else
- idx = (params->center_freq1 - 5950) / 5;
-
- bw = center_idx_to_bw_6ghz(idx);
- if (bw < 0 || bw > (int) ARRAY_SIZE(bw_idx) ||
- bw_idx[bw] != params->bandwidth)
- return -1;
- }
- } else { /* Non-6 GHz channel */
- /* An EHT STA is also an HE STA as defined in
- * IEEE P802.11be/D5.0, 4.3.16a. */
- if (params->he_enabled || params->eht_enabled) {
- params->he_enabled = 1;
- /* An HE STA is also a VHT STA if operating in the 5 GHz
- * band and an HE STA is also an HT STA in the 2.4 GHz
- * band as defined in IEEE Std 802.11ax-2021, 4.3.15a.
- * A VHT STA is an HT STA as defined in IEEE
- * Std 802.11, 4.3.15. */
- if (IS_5GHZ(params->freq))
- params->vht_enabled = 1;
-
- params->ht_enabled = 1;
- }
- }
-
- switch (params->bandwidth) {
- case 0:
- /* bandwidth not specified: use 20 MHz by default */
- /* fall-through */
- case 20:
- if (params->center_freq1 &&
- params->center_freq1 != params->freq)
- return -1;
-
- if (params->center_freq2 || params->sec_channel_offset)
- return -1;
-
- if (punct_bitmap)
- return -1;
- break;
- case 40:
- if (params->center_freq2 || !params->sec_channel_offset)
- return -1;
-
- if (punct_bitmap)
- return -1;
-
- if (!params->center_freq1)
- break;
- switch (params->sec_channel_offset) {
- case 1:
- if (params->freq + 10 != params->center_freq1)
- return -1;
- break;
- case -1:
- if (params->freq - 10 != params->center_freq1)
- return -1;
- break;
- default:
- return -1;
- }
- break;
- case 80:
- if (!params->center_freq1 || !params->sec_channel_offset)
- return 1;
-
- switch (params->sec_channel_offset) {
- case 1:
- if (params->freq - 10 != params->center_freq1 &&
- params->freq + 30 != params->center_freq1)
- return 1;
- break;
- case -1:
- if (params->freq + 10 != params->center_freq1 &&
- params->freq - 30 != params->center_freq1)
- return -1;
- break;
- default:
- return -1;
- }
-
- if (params->center_freq2 && punct_bitmap)
- return -1;
-
- /* Adjacent and overlapped are not allowed for 80+80 */
- if (params->center_freq2 &&
- params->center_freq1 - params->center_freq2 <= 80 &&
- params->center_freq2 - params->center_freq1 <= 80)
- return 1;
- break;
- case 160:
- if (!params->center_freq1 || params->center_freq2 ||
- !params->sec_channel_offset)
- return -1;
-
- switch (params->sec_channel_offset) {
- case 1:
- if (params->freq + 70 != params->center_freq1 &&
- params->freq + 30 != params->center_freq1 &&
- params->freq - 10 != params->center_freq1 &&
- params->freq - 50 != params->center_freq1)
- return -1;
- break;
- case -1:
- if (params->freq + 50 != params->center_freq1 &&
- params->freq + 10 != params->center_freq1 &&
- params->freq - 30 != params->center_freq1 &&
- params->freq - 70 != params->center_freq1)
- return -1;
- break;
- default:
- return -1;
- }
- break;
- case 320:
- if (!params->center_freq1 || params->center_freq2 ||
- !params->sec_channel_offset)
- return -1;
-
- switch (params->sec_channel_offset) {
- case 1:
- if (params->freq + 150 != params->center_freq1 &&
- params->freq + 110 != params->center_freq1 &&
- params->freq + 70 != params->center_freq1 &&
- params->freq + 30 != params->center_freq1 &&
- params->freq - 10 != params->center_freq1 &&
- params->freq - 50 != params->center_freq1 &&
- params->freq - 90 != params->center_freq1 &&
- params->freq - 130 != params->center_freq1)
- return -1;
- break;
- case -1:
- if (params->freq + 130 != params->center_freq1 &&
- params->freq + 90 != params->center_freq1 &&
- params->freq + 50 != params->center_freq1 &&
- params->freq + 10 != params->center_freq1 &&
- params->freq - 30 != params->center_freq1 &&
- params->freq - 70 != params->center_freq1 &&
- params->freq - 110 != params->center_freq1 &&
- params->freq - 150 != params->center_freq1)
- return -1;
- break;
- }
- break;
- default:
- return -1;
- }
-
- if (!punct_bitmap)
- return 0;
-
- if (!params->eht_enabled) {
- wpa_printf(MSG_ERROR,
- "Preamble puncturing supported only in EHT");
- return -1;
- }
-
- if (params->freq >= 2412 && params->freq <= 2484) {
- wpa_printf(MSG_ERROR,
- "Preamble puncturing is not supported in 2.4 GHz");
- return -1;
- }
-
- start_freq = params->center_freq1 - (params->bandwidth / 2);
- if (!is_punct_bitmap_valid(params->bandwidth,
- (params->freq - start_freq) / 20,
- punct_bitmap)) {
- wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap");
- return -1;
- }
-
- return 0;
-}
-#endif /* NEED_AP_MLME */
-
-
static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
char *pos)
{
@@ -2658,24 +2452,29 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
unsigned int num_err = 0;
int err = 0;
- ret = hostapd_parse_csa_settings(pos, &settings);
+ ret = hostapd_parse_csa_settings(iface, pos, &settings);
if (ret)
return ret;
settings.link_id = -1;
#ifdef CONFIG_IEEE80211BE
+ /* Reject if EHT is disabled in channel switch settings but the
+ * interface has a BSS affiliated with an AP MLD where EHT is mandatory
+ * to be enabled. */
+ if (!settings.freq_params.eht_enabled) {
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->conf->mld_ap) {
+ wpa_printf(MSG_INFO,
+ "Do not allow EHT to be disabled when the interface has an ML BSS");
+ return -1;
+ }
+ }
+ }
+
if (iface->num_bss && iface->bss[0]->conf->mld_ap)
settings.link_id = iface->bss[0]->mld_link_id;
#endif /* CONFIG_IEEE80211BE */
- ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
- settings.punct_bitmap);
- if (ret) {
- wpa_printf(MSG_INFO,
- "chanswitch: invalid frequency settings provided");
- return ret;
- }
-
switch (settings.freq_params.bandwidth) {
case 40:
bandwidth = CHAN_WIDTH_40;
@@ -2735,6 +2534,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
return 0;
}
+ if (iface->cac_started) {
+ wpa_printf(MSG_DEBUG,
+ "CAC is in progress - switching channel without CSA");
+ return hostapd_force_channel_switch(iface, &settings);
+ }
+
for (i = 0; i < iface->num_bss; i++) {
/* Save CHAN_SWITCH VHT, HE, and EHT config */
@@ -2761,7 +2566,6 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface,
{
#ifdef NEED_AP_MLME
struct cca_settings settings;
- struct hostapd_data *hapd = iface->bss[0];
int ret, color;
unsigned int i;
char *end;
@@ -2807,13 +2611,16 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface,
return 0;
}
- if (hapd->cca_in_progress) {
+ if (hostapd_is_cca_in_progress(iface)) {
wpa_printf(MSG_ERROR,
"color_change: CCA is already in progress");
return -1;
}
- iface->conf->he_op.he_bss_color_disabled = 0;
+ /* IEEE Std 802.11-2024, 26.17.3.4: BSS Color Disabled shall be 1 during
+ * the time leading up to the BSS color change TBTT.
+ */
+ iface->conf->he_op.he_bss_color_disabled = 1;
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
@@ -2830,6 +2637,8 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface,
hostapd_cleanup_cca_params(bss);
continue;
}
+ eloop_cancel_timeout(hostapd_switch_color_timeout_handler,
+ bss, NULL);
wpa_printf(MSG_DEBUG, "Setting user selected color: %d", color);
ret = hostapd_drv_switch_color(bss, &settings);
@@ -2997,6 +2806,57 @@ static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd,
}
+#ifdef CONFIG_TESTING_OPTIONS
+static int hostapd_ctrl_iface_set_bw(struct hostapd_iface *iface, char *pos)
+{
+#ifdef NEED_AP_MLME
+ struct hostapd_freq_params freq_params;
+ int ret;
+ enum oper_chan_width chanwidth;
+ u8 chan, oper_class;
+
+ if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE))
+ return -1;
+
+ ret = hostapd_parse_freq_params(pos, &freq_params, iface->freq);
+ if (ret)
+ return ret;
+
+ chanwidth = hostapd_chan_width_from_freq_params(&freq_params);
+
+ if (ieee80211_freq_to_channel_ext(
+ freq_params.freq,
+ freq_params.sec_channel_offset,
+ chanwidth, &oper_class,
+ &chan) == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG,
+ "invalid channel: (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
+ freq_params.freq,
+ freq_params.sec_channel_offset,
+ freq_params.vht_enabled,
+ freq_params.he_enabled);
+ return -1;
+ }
+
+ freq_params.channel = chan;
+
+ /* FIXME: What if the newly extended channel overlaps radar ranges? */
+
+ ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &freq_params, NULL);
+ if (ret)
+ return ret;
+
+ ieee802_11_set_beacons(iface);
+ return 0;
+
+#else /* NEED_AP_MLME */
+ return -1;
+#endif /* NEED_AP_MLME */
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
int reply_size, const char *param)
{
@@ -3166,6 +3026,7 @@ static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
#ifdef NEED_AP_MLME
+
static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
@@ -3199,6 +3060,35 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
return pos - buf;
}
+
+
+static int hostapd_ctrl_iface_dump_beacon(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ struct beacon_data beacon;
+ char *pos, *end;
+ int ret;
+
+ if (hostapd_build_beacon_data(hapd, &beacon) < 0)
+ return -1;
+
+ if (2 * (beacon.head_len + beacon.tail_len) > buflen)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ ret = wpa_snprintf_hex(pos, end - pos, beacon.head, beacon.head_len);
+ pos += ret;
+
+ ret = wpa_snprintf_hex(pos, end - pos, beacon.tail, beacon.tail_len);
+ pos += ret;
+
+ free_beacon_data(&beacon);
+
+ return pos - buf;
+}
+
#endif /* NEED_AP_MLME */
@@ -3669,26 +3559,87 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
#ifdef CONFIG_TESTING_OPTIONS
-static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
- char *buf, size_t buflen)
+
+static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd)
{
- int ret;
u32 count = atoi(cmd);
if (!count)
count = 1;
- ret = hostapd_link_remove(hapd, count);
- if (ret == 0) {
- ret = os_snprintf(buf, buflen, "%s\n", "OK");
- if (os_snprintf_error(buflen, ret))
- ret = -1;
- else
- ret = 0;
+ return hostapd_link_remove(hapd, count);
+}
+
+
+static int hostapd_ctrl_iface_link_enable(struct hostapd_data *hapd, char *cmd)
+{
+ struct hapd_interfaces *interfaces = hapd->iface->interfaces;
+ const char *conf_file, *phy, *ifname;
+ struct hostapd_iface *iface = NULL;
+ size_t len = os_strlen(cmd) + 1;
+ struct hostapd_config *conf;
+ char *pos, *cmd_buf;
+ int ret = -1;
+ size_t i;
+
+ if (!hapd || !hapd->conf->mld_ap || !hapd->mld ||
+ os_strncmp(cmd, "bss_config=", 11) != 0)
+ return -1;
+
+ cmd_buf = os_malloc(len);
+ if (!cmd_buf)
+ return -1;
+
+ os_snprintf(cmd_buf, len, "%s", cmd);
+ phy = cmd_buf + 11;
+ pos = os_strchr(phy, ':');
+ if (!pos)
+ goto free_cmd_buffer;
+
+ *pos++ = '\0';
+ conf_file = pos;
+ if (*conf_file == '\0')
+ goto free_cmd_buffer;
+
+ conf = interfaces->config_read_cb(conf_file);
+ if (!conf)
+ goto free_cmd_buffer;
+
+ if (!conf->bss[0]->mld_ap)
+ goto free_config;
+
+ ifname = conf->bss[0]->iface;
+ if (!ifname || ifname[0] == '\0')
+ goto free_config;
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+ iface = interfaces->iface[i];
+ break;
+ }
+ }
+
+ if (!iface || iface->state != HAPD_IFACE_DISABLED)
+ goto free_config;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *h = iface->bss[i];
+
+ if (os_strncmp(ifname, h->conf->iface,
+ sizeof(h->conf->iface)) == 0) {
+ ret = hostapd_enable_iface(iface);
+ break;
+ }
}
+free_config:
+ hostapd_config_free(conf);
+free_cmd_buffer:
+ os_free(cmd_buf);
+
return ret;
}
+
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
@@ -3705,6 +3656,7 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
+ bool p2p = false;
os_memset(&params, 0, sizeof(params));
/* USD shall use both solicited and unsolicited transmissions */
@@ -3738,6 +3690,11 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
continue;
}
+ if (os_strcmp(token, "p2p=1") == 0) {
+ p2p = true;
+ continue;
+ }
+
if (os_strcmp(token, "solicited=0") == 0) {
params.solicited = false;
continue;
@@ -3759,7 +3716,7 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
}
publish_id = hostapd_nan_usd_publish(hapd, service_name, srv_proto_type,
- ssi, &params);
+ ssi, &params, p2p);
if (publish_id > 0)
ret = os_snprintf(buf, buflen, "%d", publish_id);
fail:
@@ -3842,6 +3799,7 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd,
struct wpabuf *ssi = NULL;
int ret = -1;
enum nan_service_protocol_type srv_proto_type = 0;
+ bool p2p = false;
os_memset(&params, 0, sizeof(params));
@@ -3875,6 +3833,11 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd,
continue;
}
+ if (os_strcmp(token, "p2p=1") == 0) {
+ p2p = true;
+ continue;
+ }
+
wpa_printf(MSG_INFO,
"CTRL: Invalid NAN_SUBSCRIBE parameter: %s",
token);
@@ -3883,7 +3846,7 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd,
subscribe_id = hostapd_nan_usd_subscribe(hapd, service_name,
srv_proto_type, ssi,
- &params);
+ &params, p2p);
if (subscribe_id > 0)
ret = os_snprintf(buf, buflen, "%d", subscribe_id);
fail:
@@ -3970,7 +3933,7 @@ static int hostapd_ctrl_nan_transmit(struct hostapd_data *hapd, char *cmd)
}
ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr,
- req_instance_id);
+ req_instance_id);
fail:
wpabuf_free(ssi);
return ret;
@@ -3979,6 +3942,126 @@ fail:
#endif /* CONFIG_NAN_USD */
+#ifdef CONFIG_SAE
+static int hostapd_ctrl_iface_sae_password_bind(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *password;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ password = os_strchr(cmd, ' ');
+ if (!password)
+ return -1;
+ password++;
+
+ return sae_password_bind(hapd, addr, password);
+}
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_PROCESS_COORDINATION
+
+static bool hapd_ctrl_proc_coord_cb(void *ctx, int src,
+ enum proc_coord_message_types msg_type,
+ enum proc_coord_commands cmd,
+ u32 seq, const struct wpabuf *msg)
+{
+ struct hostapd_data *hapd = ctx;
+
+ if (cmd != PROC_COORD_CMD_TEST)
+ return false;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "PROC-COORD-TEST RX src=%u msg_type=%d seq=%u msg_len=%zu",
+ src, msg_type, seq, wpabuf_len(msg));
+
+ if (msg_type == PROC_COORD_MSG_REQUEST)
+ proc_coord_send_response(hapd->iface->interfaces->pc,
+ src, cmd, seq, msg);
+ return false;
+}
+
+
+static void hapd_ctrl_proc_coord_test_cb(void *ctx, int pid,
+ const struct wpabuf *msg)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "PROC-COORD-TEST RX-RESP src=%u msg_len=%d",
+ pid, msg ? (int) wpabuf_len(msg) : -1);
+
+}
+
+
+static int hostapd_ctrl_iface_proc_coord_test(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ int res, dst;
+ struct wpabuf *msg;
+
+ if (!hapd->iface->interfaces->pc)
+ return -1;
+
+ dst = atoi(cmd);
+
+ msg = wpabuf_alloc(1);
+ if (!msg)
+ return -1;
+ wpabuf_put_u8(msg, 123);
+
+ res = proc_coord_send_request(hapd->iface->interfaces->pc,
+ dst, PROC_COORD_CMD_TEST, msg, 1000,
+ hapd_ctrl_proc_coord_test_cb, hapd);
+ wpabuf_free(msg);
+ return res < 0 ? -1 : 0;
+}
+
+#endif /* CONFIG_PROCESS_COORDINATION */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_AFC
+
+static int hostapd_ctrl_afc_get_request(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ size_t len;
+
+ if (!hapd->iface->afc_request)
+ return -1;
+
+ len = wpabuf_len(hapd->iface->afc_request);
+ if (buflen < len + 1)
+ return -1;
+ os_memcpy(buf, wpabuf_head(hapd->iface->afc_request), len);
+ buf[len] = '\n';
+ return len + 1;
+}
+
+
+static int hostapd_ctrl_afc_get_response(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ if (!hapd->iface->afc_response)
+ return -1;
+
+ return os_snprintf(buf, buflen, "%s\n", hapd->iface->afc_response);
+}
+
+
+static int hostapd_ctrl_afc_send_request(struct hostapd_data *hapd, char *cmd)
+{
+ hostapd_afc_send_request(hapd->iface);
+ return 0;
+}
+
+#endif /* CONFIG_AFC */
+
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -4000,6 +4083,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
wpa_debug_stop_log();
} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
+ wpa_trace_set_context(buf + 5);
} else if (os_strcmp(buf, "STATUS") == 0) {
reply_len = hostapd_ctrl_iface_status(hapd, reply,
reply_size);
@@ -4133,9 +4217,6 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = -1;
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
- } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
- if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
- reply_len = -1;
} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
reply_len = -1;
@@ -4258,6 +4339,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) {
if (hostapd_ctrl_register_frame(hapd, buf + 16) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "SET_BW ", 7) == 0) {
+ /* note: preserve the space for hostapd_parse_freq_params() */
+ if (hostapd_ctrl_iface_set_bw(hapd->iface, buf + 6))
+ reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -4291,6 +4376,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
reply_len = hostapd_ctrl_iface_track_sta_list(
hapd, reply, reply_size);
+ } else if (os_strcmp(buf, "DUMP_BEACON") == 0) {
+ reply_len = hostapd_ctrl_iface_dump_beacon(hapd, reply,
+ reply_size);
#endif /* NEED_AP_MLME */
} else if (os_strcmp(buf, "PMKSA") == 0) {
reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
@@ -4577,11 +4665,37 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = -1;
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strncmp(buf, "LINK_REMOVE ", 12) == 0) {
- if (hostapd_ctrl_iface_link_remove(hapd, buf + 12,
- reply, reply_size))
+ if (hostapd_ctrl_iface_link_remove(hapd, buf + 12))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "LINK_ENABLE ", 12) == 0) {
+ if (hostapd_ctrl_iface_link_enable(hapd, buf + 12))
reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_SAE
+ } else if (os_strncmp(buf, "SAE_PASSWORD_BIND ", 18) == 0) {
+ if (hostapd_ctrl_iface_sae_password_bind(hapd, buf + 18))
+ reply_len = -1;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_PROCESS_COORDINATION
+ } else if (os_strncmp(buf, "PROC_COORD_TEST ", 16) == 0) {
+ if (hostapd_ctrl_iface_proc_coord_test(hapd, buf + 16))
+ reply_len = -1;
+#endif /* CONFIG_PROCESS_COORDINATION */
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_AFC
+ } else if (os_strncmp(buf, "AFC_GET_REQUEST", 15) == 0) {
+ reply_len = hostapd_ctrl_afc_get_request(hapd, buf + 15,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "AFC_GET_RESPONSE", 16) == 0) {
+ reply_len = hostapd_ctrl_afc_get_response(hapd, buf + 16,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "AFC_SEND_REQUEST", 16) == 0) {
+ if (hostapd_ctrl_afc_send_request(hapd, buf + 16) < 0)
+ reply_len = -1;
+#endif /* CONFIG_AFC */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -4605,7 +4719,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply, *pos = buf;
- const int reply_size = 4096;
+ const int reply_size = 8192;
int reply_len;
int level = MSG_DEBUG;
#ifdef CONFIG_CTRL_IFACE_UDP
@@ -4682,23 +4796,360 @@ done:
}
+#ifdef CONFIG_IEEE80211BE
+#ifndef CONFIG_CTRL_IFACE_UDP
+
+static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
+ char *buf, char *reply,
+ size_t reply_size,
+ struct sockaddr_storage *from,
+ socklen_t fromlen)
+{
+ struct hostapd_data *link_hapd, *link_itr;
+ int reply_len = -1, link_id = -1;
+ char *cmd;
+ bool found = false;
+
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ cmd = buf;
+
+ /* Check whether the link ID is provided in the command */
+ if (os_strncmp(cmd, "LINKID ", 7) == 0) {
+ cmd += 7;
+ link_id = atoi(cmd);
+ if (link_id < 0 || link_id >= 15) {
+ os_memcpy(reply, "INVALID LINK ID\n", 16);
+ reply_len = 16;
+ goto out;
+ }
+
+ cmd = os_strchr(cmd, ' ');
+ if (!cmd)
+ goto out;
+ cmd++;
+ }
+ if (link_id >= 0) {
+ link_hapd = mld->fbss;
+ if (!link_hapd) {
+ os_memcpy(reply, "NO LINKS ACTIVE\n", 16);
+ reply_len = 16;
+ goto out;
+ }
+
+ for_each_mld_link(link_itr, link_hapd) {
+ if (link_itr->mld_link_id == link_id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto out;
+
+ link_hapd = link_itr;
+ } else {
+ link_hapd = mld->fbss;
+ }
+
+ if (os_strcmp(cmd, "PING") == 0) {
+ os_memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (os_strcmp(cmd, "ATTACH") == 0) {
+ if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(cmd, "ATTACH ", 7) == 0) {
+ if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, cmd + 7))
+ reply_len = -1;
+ } else if (os_strcmp(cmd, "DETACH") == 0) {
+ if (ctrl_iface_detach(&mld->ctrl_dst, from, fromlen))
+ reply_len = -1;
+ } else {
+ if (link_id == -1)
+ wpa_printf(MSG_DEBUG,
+ "Link ID not provided, using the first link BSS (if available)");
+
+ if (!link_hapd)
+ reply_len = -1;
+ else
+ reply_len =
+ hostapd_ctrl_iface_receive_process(
+ link_hapd, cmd, reply, reply_size,
+ from, fromlen);
+ }
+
+out:
+ 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 size_t reply_size = 8192;
+ 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) {
+ 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)
+{
+ size_t len;
+ char *buf;
+ int ret;
+
+ if (!mld->ctrl_interface)
+ return NULL;
+
+ len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2;
+
+ buf = os_malloc(len);
+ if (!buf)
+ return NULL;
+
+ ret = os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name);
+ if (os_snprintf_error(len, ret)) {
+ os_free(buf);
+ return NULL;
+ }
+
+ 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)
+ 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)
+ 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)
{
char *buf;
size_t len;
+ const char *ctrl_sock_iface;
+
+#ifdef CONFIG_IEEE80211BE
+ ctrl_sock_iface = hapd->ctrl_sock_iface;
+#else /* CONFIG_IEEE80211BE */
+ 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;
}
@@ -4814,6 +5265,7 @@ fail:
struct sockaddr_un addr;
int s = -1;
char *fname = NULL;
+ size_t iflen;
if (hapd->ctrl_sock > -1) {
wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
@@ -4868,8 +5320,13 @@ fail:
}
#endif /* ANDROID */
+#ifdef CONFIG_IEEE80211BE
+ iflen = os_strlen(hapd->ctrl_sock_iface);
+#else /* CONFIG_IEEE80211BE */
+ iflen = os_strlen(hapd->conf->iface);
+#endif /* CONFIG_IEEE80211BE */
if (os_strlen(hapd->conf->ctrl_interface) + 1 +
- os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
+ iflen >= sizeof(addr.sun_path))
goto fail;
s = socket(PF_UNIX, SOCK_DGRAM, 0);
@@ -4951,6 +5408,14 @@ fail:
hapd->msg_ctx = hapd;
wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_PROCESS_COORDINATION
+ if (hapd->iface->interfaces->pc)
+ proc_coord_register_handler(hapd->iface->interfaces->pc,
+ hapd_ctrl_proc_coord_cb, hapd);
+#endif /* CONFIG_PROCESS_COORDINATION */
+#endif /* CONFIG_TESTING_OPTIONS */
+
return 0;
fail:
@@ -5006,6 +5471,14 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
#ifdef CONFIG_TESTING_OPTIONS
l2_packet_deinit(hapd->l2_test);
hapd->l2_test = NULL;
+#ifdef CONFIG_PROCESS_COORDINATION
+ if (hapd->iface->interfaces->pc) {
+ proc_coord_unregister_handler(hapd->iface->interfaces->pc,
+ hapd_ctrl_proc_coord_cb, hapd);
+ proc_coord_cancel_wait(hapd->iface->interfaces->pc,
+ hapd_ctrl_proc_coord_test_cb, hapd);
+ }
+#endif /* CONFIG_PROCESS_COORDINATION */
#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -5353,7 +5826,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
socklen_t fromlen = sizeof(from);
char *reply;
int reply_len;
- const int reply_size = 4096;
+ const int reply_size = 8192;
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
@@ -5822,7 +6295,7 @@ static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst,
&dst->addr, dst->addrlen);
msg.msg_name = &dst->addr;
msg.msg_namelen = dst->addrlen;
- if (sendmsg(sock, &msg, 0) < 0) {
+ if (sendmsg(sock, &msg, MSG_DONTWAIT) < 0) {
int _errno = errno;
wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
"%d - %s",
diff --git a/contrib/wpa/hostapd/ctrl_iface.h b/contrib/wpa/hostapd/ctrl_iface.h
index 3341a66bdc6c..6ce209d22c91 100644
--- a/contrib/wpa/hostapd/ctrl_iface.h
+++ b/contrib/wpa/hostapd/ctrl_iface.h
@@ -14,6 +14,8 @@ 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);
+int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld);
+void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld);
#else /* CONFIG_NO_CTRL_IFACE */
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
{
diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig
index 550db697bc0a..13bce7aaa88b 100644
--- a/contrib/wpa/hostapd/defconfig
+++ b/contrib/wpa/hostapd/defconfig
@@ -9,9 +9,6 @@
# be modified from here. In most cass, these lines should use += in order not
# to override previous values of the variables.
-# Driver interface for Host AP driver
-CONFIG_DRIVER_HOSTAP=y
-
# Driver interface for wired authenticator
#CONFIG_DRIVER_WIRED=y
@@ -152,7 +149,6 @@ CONFIG_IPV6=y
#CONFIG_DRIVER_RADIUS_ACL=y
# Wireless Network Management (IEEE Std 802.11v-2011)
-# Note: This is experimental and not complete implementation.
#CONFIG_WNM=y
# IEEE 802.11ac (Very High Throughput) support
@@ -163,9 +159,6 @@ CONFIG_IPV6=y
# IEEE 802.11be EHT support
# CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE.
-# Note: This is experimental and work in progress. The definitions are still
-# subject to change and this should not be expected to interoperate with the
-# final IEEE 802.11be version.
#CONFIG_IEEE80211BE=y
# Simultaneous Authentication of Equals (SAE), WPA3-Personal
@@ -384,8 +377,7 @@ CONFIG_IPV6=y
#CONFIG_WPA_CLI_EDIT=y
# Opportunistic Wireless Encryption (OWE)
-# Experimental implementation of draft-harkins-owe-07.txt
-#CONFIG_OWE=y
+CONFIG_OWE=y
# Airtime policy support
#CONFIG_AIRTIME_POLICY=y
@@ -410,18 +402,32 @@ CONFIG_IPV6=y
#CONFIG_NO_TKIP=y
# Pre-Association Security Negotiation (PASN)
-# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol
-# design is still subject to change. As such, this should not yet be enabled in
-# production use.
#CONFIG_PASN=y
# Device Provisioning Protocol (DPP) (also known as Wi-Fi Easy Connect)
CONFIG_DPP=y
# DPP version 2 support
CONFIG_DPP2=y
-# DPP version 3 support (experimental and still changing; do not enable for
-# production use)
+# DPP version 3 support
#CONFIG_DPP3=y
# Wi-Fi Aware unsynchronized service discovery (NAN USD)
#CONFIG_NAN_USD=y
+
+# Enhanced Privacy Protection (IEEE P802.11bi)
+# Note: These features are experimental and still changing; do not enable for
+# production use.
+# Encrypted association frame support (needed for EPPKE and EAP in
+# Authentication frames)
+#CONFIG_ENC_ASSOC=y
+#
+# PMKSA caching privacy support
+#CONFIG_PMKSA_PRIVACY=y
+#
+# IEEE 802.1X (EAP) authentication in Authentication frames
+# IEEE P802.11bi/D4.0, 12.16.5 (IEEE 802.1X authentication utilizing
+# Authentication frames)
+#CONFIG_IEEE8021X_AUTH=y
+
+# Automated Frequency Coordination for 6 GHz outdoor
+#CONFIG_AFC=y
diff --git a/contrib/wpa/hostapd/eap_register.c b/contrib/wpa/hostapd/eap_register.c
index 3e870c7f0670..8bb25f2345a3 100644
--- a/contrib/wpa/hostapd/eap_register.c
+++ b/contrib/wpa/hostapd/eap_register.c
@@ -44,13 +44,6 @@ int eap_server_register_methods(void)
ret = eap_server_unauth_tls_register();
#endif /* EAP_SERVER_TLS */
-#ifdef EAP_SERVER_TLS
-#ifdef CONFIG_HS20
- if (ret == 0)
- ret = eap_server_wfa_unauth_tls_register();
-#endif /* CONFIG_HS20 */
-#endif /* EAP_SERVER_TLS */
-
#ifdef EAP_SERVER_MSCHAPV2
if (ret == 0)
ret = eap_server_mschapv2_register();
diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c
index 5caa779dd648..291acf9ffc14 100644
--- a/contrib/wpa/hostapd/hlr_auc_gw.c
+++ b/contrib/wpa/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
/*
* HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -66,6 +66,7 @@ static int update_milenage = 0;
static int sqn_changes = 0;
static int ind_len = 5;
static int stdout_debug = 1;
+static bool stop = false;
/* GSM triplets */
struct gsm_triplet {
@@ -877,6 +878,12 @@ static int process_cmd(char *cmd, char *resp, size_t resp_len)
if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
return aka_auts(cmd + 9, resp, resp_len);
+ if (strncmp(cmd, "TERMINATE", 9) == 0) {
+ stop = true;
+ resp[0] = '\0';
+ return 0;
+ }
+
printf("Unknown request: %s\n", cmd);
return -1;
}
@@ -931,10 +938,13 @@ static void cleanup(void)
struct gsm_triplet *g, *gprev;
struct milenage_parameters *m, *prev;
- if (update_milenage && milenage_file && sqn_changes)
+ if (update_milenage && milenage_file && sqn_changes) {
+ sqn_changes = 0;
update_milenage_file(milenage_file);
+ }
g = gsm_db;
+ gsm_db = NULL;
while (g) {
gprev = g;
g = g->next;
@@ -942,16 +952,21 @@ static void cleanup(void)
}
m = milenage_db;
+ milenage_db = NULL;
while (m) {
prev = m;
m = m->next;
os_free(prev);
}
- if (serv_sock >= 0)
+ if (serv_sock >= 0) {
close(serv_sock);
- if (socket_path)
+ serv_sock = -1;
+ }
+ if (socket_path) {
unlink(socket_path);
+ socket_path = NULL;
+ }
#ifdef CONFIG_SQLITE
if (sqlite_db) {
@@ -965,6 +980,7 @@ static void cleanup(void)
static void handle_term(int sig)
{
printf("Signal %d - terminate\n", sig);
+ cleanup();
exit(0);
}
@@ -973,7 +989,7 @@ static void usage(void)
{
printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
"database/authenticator\n"
- "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
+ "Copyright (c) 2005-2024, Jouni Malinen <j@w1.fi>\n"
"\n"
"usage:\n"
"hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
@@ -1081,8 +1097,9 @@ int main(int argc, char *argv[])
signal(SIGTERM, handle_term);
signal(SIGINT, handle_term);
- for (;;)
+ while (!stop)
process(serv_sock);
+ cleanup();
} else {
char buf[1000];
socket_path = NULL;
diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf
index d875d5fc63b0..b81ce11b846c 100644
--- a/contrib/wpa/hostapd/hostapd.conf
+++ b/contrib/wpa/hostapd/hostapd.conf
@@ -20,11 +20,11 @@ interface=wlan0
# interface is also created.
#bridge=br0
-# Driver interface type (hostap/wired/none/nl80211/bsd);
-# default: hostap). nl80211 is used with all Linux mac80211 drivers.
+# Driver interface type (wired/none/nl80211/bsd);
+# default: nl80211). nl80211 is used with all Linux mac80211 drivers.
# Use driver=none if building hostapd as a standalone RADIUS server that does
# not control any wireless/wired driver.
-# driver=hostap
+# driver=nl80211
# Driver interface parameters (mainly for development testing use)
# driver_params=<params>
@@ -313,7 +313,15 @@ fragm_threshold=-1
# Station MAC address -based authentication
# Please note that this kind of access control requires a driver that uses
# hostapd to take care of management frame processing and as such, this can be
-# used with driver=hostap or driver=nl80211, but not with driver=atheros.
+# used with driver=nl80211, but not with driver=bsd.
+#
+# Especially in cases where not all STAs with authentication credentials are
+# fully trusted, unexpected MAC addresses should be marked to be rejected, e.g.,
+# by including them in the deny list (deny_mac_file). This should cover MAC
+# addresses that are known to be used in the LAN, e.g., the MAC address of the
+# default gateway and any server like RADIUS server or proxy. BSSIDs and MLD
+# MAC addresses of other APs in the same network should also be disallowed.
+#
# 0 = accept unless in deny list
# 1 = deny unless in accept list
# 2 = use external RADIUS server (accept/deny lists are searched first)
@@ -1030,6 +1038,62 @@ wmm_ac_vo_acm=0
# Valid range: 0..20 TUs; default is 0 (disabled)
#unsol_bcast_probe_resp_interval=0
+#channel_usage: Whether Channel Usage procedures is supported by AP.
+# 0 = Channel Usage support is disabled (default)
+# 1 = Channel Usage support is enabled
+#channel_usage=0
+
+#peer_to_peer_twt: Indicates an HE AP supports negotiating a peer-to-peer
+# TWT schedule that is requested by a non-AP STA to establish a
+# channel-usage-aidable BSS or an off-channel TDLS direct link.
+# 0 = Does not support Peer-to-peer TWT (default)
+# 1 = Supports Peer-to-peer TWT
+#peer_to_peer_twt=0
+
+##### Automated Frequency Coordination for 6 GHz 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_serial_number=abcdefg
+# Comma separated list of certification IDs in <rule set>:<ID> format
+#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
+# Frequency list can be provided as range using hyphen ('-') or individual
+# frequencies can be specified by comma (',') separated values
+#afc_freq_range=5925-6425,6525-6875
+
+# AFC request operation classes
+#afc_op_class=131,132,133,134,136,137
+
##### IEEE 802.11be related configuration #####################################
#ieee80211be: Whether IEEE 802.11be (EHT) is enabled
@@ -1037,6 +1101,14 @@ wmm_ac_vo_acm=0
# 1 = enabled
#ieee80211be=1
+# Require stations to support EHT PHY (reject association if they do not).
+# This is a radio-level parameter and applies to all BSSs in the radio.
+#require_eht=1
+
+# Require stations to support EHT PHY for a specific BSS only.
+# This is a per-BSS parameter.
+#bss_require_eht=1
+
#disable_11be: Boolean (0/1) to disable EHT for a specific BSS
#disable_11be=0
@@ -1102,6 +1174,13 @@ wmm_ac_vo_acm=0
# will be used as the AP MLD MAC address.
#mld_addr=02:03:04:05:06:07
+# Disable EHT-MCS 15 RX
+# If set to 1, the MCS15 Disable subfield in the EHT Operation element is set
+# to 1, disabling EHT-MCS 15. If set to 0, the subfield is set to 0, enabling
+# EHT-MCS 15. The default value is 1, indicating EHT-MCS 15 reception capability
+# is disabled.
+#disable_mcs15_rx=1
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
@@ -1207,6 +1286,10 @@ eapol_key_index_workaround=0
# mka_priority (Priority of MKA Actor)
# Range: 0..255 (default: 255)
#
+# macsec_icv_indicator: Always include ICV indicator
+# 0 = ICV Indicator is not included when ICV has default length (default)
+# 1 = ICV Indicator is always included (compatibility mode)
+#
# macsec_csindex: IEEE 802.1X/MACsec cipher suite
# 0 = GCM-AES-128 (default)
# 1 = GCM-AES-256 (default)
@@ -1786,6 +1869,9 @@ own_ip_addr=127.0.0.1
# accounting while still enabling RADIUS authentication.
#radius_server_acct_port=1813
+# Log received RADIUS accounting data
+#radius_server_acct_log=1
+
# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
#radius_server_ipv6=1
@@ -1869,8 +1955,8 @@ own_ip_addr=127.0.0.1
# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
# DPP = Device Provisioning Protocol
-# OSEN = Hotspot 2.0 online signup with encryption
# (dot11RSNAConfigAuthenticationSuitesTable)
+# EPPKE = Enhanced Privacy Protection Key Exchange
#wpa_key_mgmt=WPA-PSK WPA-EAP
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
@@ -2021,6 +2107,12 @@ own_ip_addr=127.0.0.1
# 1 = enabled
#beacon_prot=0
+# SPP (Signaling and Payload Protected) A-MSDU.
+# This depends on driver support and CCMP/GCMP cipher suite being used.
+# 0 = disabled (default)
+# 1 = enabled if driver indicates support for this
+#spp_amsdu=1
+
# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
# (maximum time to wait for a SA Query response)
# dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -2110,6 +2202,29 @@ own_ip_addr=127.0.0.1
# contains and entry in the same format as sae_password uses.
#sae_password_file=/tc/hostapd.sae_passwords
+# Whether to use wpa_passphrase as an SAE password
+# 0 = enabled if no matching sae_password is set (default)
+# 1 = disabled in all cases
+#sae_password_psk=0
+
+# Tracking of SAE password use
+# While SAE design does not allow the AP to determine the used password robustly
+# if multiple password are configured without use of password identifiers, a
+# small number of such passwords might be usable with minimal impact to STAs.
+# This parameter can be used to enable such mechanism by tracking which password
+# STAs have tried and either succeeded or failed to complete authentication
+# with. Configured passwords are then tried one by one until success. This shows
+# up as a potential attack to the STA, though, and as such, may result in the AP
+# getting rejected after a couple of attempts. Only one password can be tested
+# per attempt, so this limits this mechanism to only a small number (e.g., 2-3)
+# passwords without showing significant usability issues with some STAs. This
+# is meant as a workaround until SAE with password identifiers is deployed on
+# STAs.
+# This parameter sets the maximum number of STA MAC addresses to track per
+# SAE password. This should be set sufficiently high to cover the expected
+# number of active STAs.
+#sae_track_password=0
+
# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
# This parameter defines how many open SAE instances can be in progress at the
# same time before the anti-clogging mechanism is taken into use.
@@ -2159,6 +2274,32 @@ own_ip_addr=127.0.0.1
# regardless of the sae_pwe parameter value.
#sae_pwe=0
+# Accept SAE association when the STA advertises SAE H2E support in the RSNXE
+# but authenticated using the hunting-and-pecking loop (i.e., did not use H2E).
+# By default (0), hostapd rejects such an association as a potential downgrade
+# when sae_pwe=2. This option is a workaround for WPA3-Personal Compatibility
+# Mode and RSNXE-override interoperability with certain stations (e.g., some MLO
+# STAs that add only the RSNXE Override element and do not use H2E on 2.4/5
+# GHz).
+#
+# Enabling this weakens SAE downgrade protection and should only be used when
+# RSN overriding is enabled in the hostapd configuration and such
+# interoperability is required.
+#sae_accept_h2e_without_use=0
+
+# Changing SAE password identifiers
+# AP can send a set of unique SAE password identifiers to allow the long term
+# password identifier to be changed for over the air exchanges to avoid tracking
+# of devices using password identifiers.
+#
+# Key for generating and interpreting encrypted password identifiers. This must
+# be same for each AP in the network. 32 octets as a hexdump
+#sae_pw_id_key=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
+#
+# How many over the air password identifier values to provide to a STA each time
+# SAE is used.
+#sae_pw_id_num=2
+
# FILS Cache Identifier (16-bit value in hexdump format)
#fils_cache_id=0011
@@ -2279,6 +2420,17 @@ own_ip_addr=127.0.0.1
# (default: 1 = activated)
#pasn_noauth=1
+# Unauthenticated EPPKE activated
+# This indicates whether EPPKE without a base AKM (i.e., using
+# WPA_KEY_MGMT_EPPKE as the selected AKMP in the RSNE without mutual
+# authentication) is allowed.
+#
+# When enabled, the AP advertises support for unauthenticated EPPKE via the
+# Unauthenticated EPPKE bit in the RSNXE (WLAN_RSNX_CAPAB_UNAUTH_EPPKE).
+# See IEEE P802.11bi/D4.0, 12.16.9.1 for details.
+# (default: 0 = disabled)
+#eppke_unauth=0
+
# SSID protection in 4-way handshake
# The IEEE 802.11i-2004 RSN design did not provide means for protecting the
# SSID in the general case. IEEE P802.11REVme/D6.0 added support for this in
@@ -2298,6 +2450,129 @@ own_ip_addr=127.0.0.1
#
#ssid_protection=0
+# Known STA Identification
+# IEEE Std 802.11-2024 adds a mechanism that allows the SA Query procedure on
+# (re)association to the previously used AP to be skipped when that AP still
+# has a valid security association. This can speed up cases where a STA needs to
+# reassociate back to the same AP to update some association parameters.
+#
+# 0 = Do not process Known STA Identification (default)
+# 1 = Allow Known STA Identification to be used to skip SA Query procedure
+#known_sta_identification=0
+
+# RSNE/RSNXE override
+#
+# These parameters can be used to configure RSN parameters for STAs that support
+# the override elements. The RSN parameters for STAs that do not support these
+# mechanisms are configured in the referenced configuration parameters. The AP
+# allows STAs to use either of the configured sets for negotiating RSN
+# parameters.
+#
+# The main purpose of this mechanism is to make the AP look like it is using an
+# older security mechanism (e.g., WPA2-Personal) to older STAs while allowing
+# new stations use newer security mechanisms (e.g., WPA3-Personal) based on the
+# override values. This might be needed to work around issues with deployed
+# STAs that do not implement RSNE extensibility correctly and may fail to
+# connect when the AP is using a transition mode like WPA3-Personal transition
+# mode.
+#
+# Key management; see wpa_key_mgmt for RSNE configuration
+#rsn_override_key_mgmt=<accepted key management algorithms>
+#
+# Pairwise cipher suites; see rsn_pairwise for RSNE configuration
+#rsn_override_pairwise=<accepted cipher suites)
+#
+# Management frame protection (MFP/PMF); see ieee80211w for RSNE configuration
+# 0 = disabled
+# 1 = optional
+# 2 = required
+#rsn_override_mfp=<0/1/2>
+#
+# Second set of similar parameters. These are required to be used for
+# Wi-Fi 7 (EHT/MLO) associations with RSN overriding and can optionally be used
+# in cases that do not use Wi-Fi 7.
+#rsn_override_key_mgmt_2
+#rsn_override_pairwise_2
+#rsn_override_mfp_2
+#
+# The RSNXE is normally included if any of the extended RSN capabilities is
+# enabled/supported. When using RSN overriding, a separate RSNXOE is included
+# and it may be more interoperable to omit the RSNXE completely. This
+# configuration parameter can be used to do that.
+# 0 = Include the RSNXE if any extended RSN capability is enabled/supported
+# (default).
+# 1 = Do not include the RSNXE.
+#rsn_override_omit_rsnxe=0
+
+# Allow MLO partner link association from a non-RSNO-capable STA that
+# negotiates an AKM from the override set (e.g., SAE on the 6 GHz primary
+# link) but whose partner link per-STA profile RSNE would not match the base
+# wpa_key_mgmt of that link (e.g., WPA-PSK on 2.4 GHz in WPA3-Personal
+# Compatibility Mode). When enabled, the AP MLD falls back to
+# rsn_override_key_mgmt for the partner link key_mgmt validation to allow the
+# stronger AKM to be used for ML associations even if when RSNE does not
+# advertise support for this on all links.
+# 0 = Disabled (default)
+# 1 = Enabled
+#rsn_override_mlo_compat=0
+#
+# Example configuration for WPA2-Personal/PMF-optional in RSNE and
+# WPA3-Personal/PMF-required/MLO in override elements
+#wpa_key_mgmt=WPA-PSK
+#rsn_pairwise=CCMP
+#ieee80211w=1
+#rsn_override_key_mgmt=SAE
+#rsn_override_pairwise=GCMP-256
+#rsn_override_mfp=2
+#rsn_override_key_mgmt_2=SAE-EXT-KEY
+#rsn_override_pairwise_2=GCMP-256
+#rsn_override_mfp_2=2
+#beacon_prot=1
+#sae_groups=19 20
+#sae_require_mfp=1
+#sae_pwe=2
+
+##### IEEE 802.11az configuration #############################################
+#
+# urnm_mfpr:
+# urnm_mfpr mandates use of management frame protection and establishment of
+# PTKSA across all supported bandwidths, regardless of the urnm_mfpr_x20
+# setting for unassociated negotiation and measurement operations.
+#
+# urnm_mfpr_x20:
+# urnm_mfpr_x20 allows unassociated negotiation and measurement
+# operations using 20 MHz bandwidth without requiring management frame
+# protection and a PTKSA while mandating the security for higher bandwidth
+# cases. This is only valid if urnm_mfpr is false.
+#
+# Behavior Summary:
+# - urnm_mfpr = 1:
+# - PTKSA is required for all supported bandwidths.
+# - urnm_mfpr_x20 is ignored.
+#
+# - urnm_mfpr = 0 and urnm_mfpr_x20 = 1:
+# - PTKSA is required only for bandwidths greater than 20 MHz.
+#
+# - urnm_mfpr = 0 and urnm_mfpr_x20 = 0:
+# - PTKSA is not required for any bandwidths.
+#urnm_mfpr=0
+#urnm_mfpr_x20=0
+
+#i2r_lmr_policy: Policy for I2R LMR feedback
+# This indicates whether initiating stations are required to support the
+# capability to generate and transmit Initiator-to-Responder Location
+# Measurement Reports (I2R LMRs).
+# This parameter matches the dot11I2RLMRFeedbackPolicy MIB variable.
+#
+# When set to 1:
+# Do not require initiating stations to support I2R LMR generation and
+# transmission.
+#
+# When set to 0:
+# Request initiating stations to negotiate and support the transmission of
+# I2R LMRs during location measurement operations.
+#i2r_lmr_policy=1
+
##### IEEE 802.11r configuration ##############################################
# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -2402,6 +2677,34 @@ own_ip_addr=127.0.0.1
# 1 = enabled
#ft_psk_generate_local=0
+##### IEEE 802.11bi related configuration #####################################
+
+# IEEE Std 802.11bi introduces following Enhanced Privacy Protection (EPP)
+# features.
+#
+# (Re)Association Request/Response frame encryption: Indicates if the AP
+# supports encryption of (Re)Association Request and Response frames
+# 0 = disabled (default)
+# 1 = enabled
+#assoc_frame_encryption=0
+
+# PMKSA caching privacy: Indicates if the AP supports recomputation of the PMKID
+# after the indicated PMKID in the RSNE identifies a cached PMKSA and a PTKSA
+# was established using the identified PMKSA. The recomputed PMKID will be
+# delivered to the non-AP STA in the Key Delivery element added in the encrypted
+# (Re)Association Response frame.
+# 0 = disabled (default)
+# 1 = enabled
+#pmksa_caching_privacy=0
+
+# IEEE 802.1X (EAP) Authentication utilizing Authentication frames: Indicates
+# if the AP supports encapsulation of EAP PDUs within Authentication frames
+# in addition to the older design of EAPOL frames (Data frames after
+# association).
+# 0 = disabled (default)
+# 1 = enabled
+#eap_using_authentication_frames=0
+
##### Neighbor table ##########################################################
# Maximum number of entries kept in AP table (either for neighbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -2573,7 +2876,7 @@ own_ip_addr=127.0.0.1
# message when acting as a Registrar. If skip_cred_build=1, this data will also
# be able to override the Credential attribute that would have otherwise been
# automatically generated based on network configuration. This configuration
-# option points to an external file that much contain the WPS Credential
+# option points to an external file that contains the WPS Credential
# attribute(s) as binary data.
#extra_cred=hostapd.cred
@@ -2970,9 +3273,6 @@ own_ip_addr=127.0.0.1
# forging such frames to other stations in the BSS.
#disable_dgaf=1
-# OSU Server-Only Authenticated L2 Encryption Network
-#osen=1
-
# ANQP Domain ID (0..65535)
# An identifier for a set of APs in an ESS that share the same common ANQP
# information. 0 = Some of the ANQP information is unique to this AP (default).
@@ -3053,42 +3353,6 @@ own_ip_addr=127.0.0.1
# @1@ = MAC address of the STA (colon separated hex octets)
#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
-# OSU and Operator icons
-# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
-#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
-#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
-
-# OSU SSID (see ssid2 for format description)
-# This is the SSID used for all OSU connections to all the listed OSU Providers.
-#osu_ssid="example"
-
-# OSU Providers
-# One or more sets of following parameter. Each OSU provider is started by the
-# mandatory osu_server_uri item. The other parameters add information for the
-# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
-# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
-# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
-#
-#osu_server_uri=https://example.com/osu/
-#osu_friendly_name=eng:Example operator
-#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
-#osu_nai=anonymous@example.com
-#osu_nai2=anonymous@example.com
-#osu_method_list=1 0
-#osu_icon=icon32
-#osu_icon=icon64
-#osu_service_desc=eng:Example services
-#osu_service_desc=fin:Esimerkkipalveluja
-#
-#osu_server_uri=...
-
-# Operator Icons
-# Operator icons are specified using references to the hs20_icon entries
-# (Name subfield). This information, if present, is advertsised in the
-# Operator Icon Metadata ANQO-element.
-#operator_icon=icon32
-#operator_icon=icon64
-
##### Multiband Operation (MBO) ###############################################
#
# MBO enabled
@@ -3267,6 +3531,10 @@ own_ip_addr=127.0.0.1
# (channel switch operating class is needed)
#ecsa_ie_only=0
#
+# Include only CSA IE without ECSA IE
+# (the operating class is not mentioned)
+#csa_ie_only=0
+
# Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last
# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
# previous attempts and thus delays the frame. (testing only)
@@ -3350,6 +3618,18 @@ own_ip_addr=127.0.0.1
# 2 = Enhanced multiple BSSID advertisement enabled.
#mbssid=0
#
+# Maximum number of BSSs that can be added into a Multiple BSSID set
+# This is a radio level parameter. If not set (or 0), the maximum is determined
+# automatically based on the configured BSSs which may limit dynamic addition
+# of new BSSs.
+#mbssid_max=0
+#
+# Multiple BSSID Index override
+# This is a BSS level parameter. If not set (or 0), the BSSID index is
+# determined automatically based on the configured BSSs which may limit dynamic
+# addition of new BSSs.
+#mbssid_index=0
+#
# The transmitting interface should be added with the 'interface' option while
# the non-transmitting interfaces should be added using the 'bss' option.
# Security configuration should be added separately per interface, if required.
diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c
index eb8a38350bd1..94bc36d6a631 100644
--- a/contrib/wpa/hostapd/hostapd_cli.c
+++ b/contrib/wpa/hostapd/hostapd_cli.c
@@ -21,7 +21,7 @@
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2026, Jouni Malinen <j@w1.fi> and contributors";
static struct wpa_ctrl *ctrl_conn;
static int hostapd_cli_quit = 0;
@@ -54,7 +54,11 @@ static void usage(void)
fprintf(stderr, "%s\n", hostapd_cli_version);
fprintf(stderr,
"\n"
- "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
+ "usage: hostapd_cli [-p<path>] [-i<ifname>] "
+#ifdef CONFIG_IEEE80211BE
+ "[-l<link_id>] "
+#endif /* CONFIG_IEEE80211BE */
+ "[-hvBr] "
"[-a<path>] \\\n"
" [-P<pid file>] [-G<ping interval>] [command..]\n"
"\n"
@@ -74,7 +78,11 @@ static void usage(void)
" -B run a daemon in the background\n"
" -i<ifname> Interface to listen on (default: first "
"interface found in the\n"
- " socket path)\n\n");
+ " socket path)\n"
+#ifdef CONFIG_IEEE80211BE
+ " -l<link_id> Link ID of the interface in case of Multi-Link Operation\n"
+#endif /* CONFIG_IEEE80211BE */
+ "\n");
print_help(stderr, NULL);
}
@@ -192,7 +200,7 @@ static void hostapd_cli_msg_cb(char *msg, size_t len)
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
{
- char buf[4096];
+ char buf[8192];
size_t len;
int ret;
@@ -213,6 +221,8 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
if (print) {
buf[len] = '\0';
printf("%s", buf);
+ if (interactive && len > 0 && buf[len - 1] != '\n')
+ printf("\n");
}
return 0;
}
@@ -1223,6 +1233,13 @@ static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl,
}
+static int hostapd_cli_cmd_set_bw(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "SET_BW", 0, argc, argv);
+}
+
+
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1265,6 +1282,29 @@ static int hostapd_cli_cmd_enable_mld(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int hostapd_cli_cmd_remove_link(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ unsigned int count = (argc && atoi(argv[0]) > 0) ? atoi(argv[0]) : 1;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "LINK_REMOVE %u", count);
+
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_link_enable(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "LINK_ENABLE", 1, argc, argv);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
static int hostapd_cli_cmd_disable_mld(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1654,6 +1694,31 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
#endif /* ANDROID */
+#ifdef CONFIG_AFC
+
+static int hostapd_cli_cmd_afc_get_request(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "AFC_GET_REQUEST", 0, 0, NULL);
+}
+
+
+static int hostapd_cli_cmd_afc_get_response(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "AFC_GET_RESPONSE", 0, 0, NULL);
+}
+
+
+static int hostapd_cli_cmd_afc_send_request(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "AFC_SEND_REQUEST", 0, 0, NULL);
+}
+
+#endif /* CONFIG_AFC */
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1762,6 +1827,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
#endif /* CONFIG_IEEE80211AX */
{ "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
"<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
+ { "set_bw", hostapd_cli_cmd_set_bw, NULL,
+ "[sec_channel_offset=] [center_freq1=]\n"
+ " [center_freq2=] [bandwidth=] [ht|vht]\n"
+ " = change channel bandwidth" },
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
"<addr> <url>\n"
" = send WNM-Notification Subscription Remediation Request" },
@@ -1783,6 +1852,13 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
"= disable hostapd on current interface" },
{ "enable_mld", hostapd_cli_cmd_enable_mld, NULL,
"= enable AP MLD to which the interface is affiliated" },
+#ifdef CONFIG_TESTING_OPTIONS
+ { "remove_link", hostapd_cli_cmd_remove_link, NULL,
+ "<count> = remove MLO link and send Reconfiguration MLE" },
+ { "link_enable", hostapd_cli_cmd_link_enable, NULL,
+ " bss_config=<phy>:<config file>\n"
+ " = Enable a link previously removed from the MLD AP" },
+#endif /* CONFIG_TESTING_OPTIONS */
{ "disable_mld", hostapd_cli_cmd_disable_mld, NULL,
"= disable AP MLD to which the interface is affiliated" },
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
@@ -1882,6 +1958,14 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "driver", hostapd_cli_cmd_driver, NULL,
"<driver sub command> [<hex formatted data>] = send driver command data" },
#endif /* ANDROID */
+#ifdef CONFIG_AFC
+ { "afc_get_request", hostapd_cli_cmd_afc_get_request, NULL,
+ " = Show latest AFC request data"},
+ { "afc_get_response", hostapd_cli_cmd_afc_get_response, NULL,
+ " = Show latest AFC response data"},
+ { "afc_send_request", hostapd_cli_cmd_afc_send_request, NULL,
+ " = Send another AFC request and upadte the timeout"},
+#endif /* CONFIG_AFC */
{ NULL, NULL, NULL, NULL }
};
@@ -2174,9 +2258,13 @@ static void hostapd_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
hostapd_cli_action_cb) < 0 ||
len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
- printf("hostapd did not reply to PING command - exiting\n");
- eloop_terminate();
- return;
+ printf("hostapd did not reply to PING command - open a new connection\n");
+ hostapd_cli_close_connection();
+ if (hostapd_cli_reconnect(ctrl_ifname)) {
+ printf("Failed to establish new connection - exit\n");
+ eloop_terminate();
+ return;
+ }
}
eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping,
ctrl, NULL);
@@ -2212,12 +2300,15 @@ int main(int argc, char *argv[])
int c;
int daemonize = 0;
int reconnect = 0;
+#ifdef CONFIG_IEEE80211BE
+ int link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
if (os_program_init())
return -1;
for (;;) {
- c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
+ c = getopt(argc, argv, "a:BhG:i:l:p:P:rs:v");
if (c < 0)
break;
switch (c) {
@@ -2252,6 +2343,11 @@ int main(int argc, char *argv[])
case 's':
client_socket_dir = optarg;
break;
+#ifdef CONFIG_IEEE80211BE
+ case 'l':
+ link_id = atoi(optarg);
+ break;
+#endif /* CONFIG_IEEE80211BE */
default:
usage();
return -1;
@@ -2285,6 +2381,24 @@ int main(int argc, char *argv[])
closedir(dir);
}
}
+
+#ifdef CONFIG_IEEE80211BE
+ if (link_id >= 0 && ctrl_ifname) {
+ int ret;
+ char buf[300];
+
+ ret = os_snprintf(buf, sizeof(buf), "%s_%s%d",
+ ctrl_ifname, WPA_CTRL_IFACE_LINK_NAME,
+ link_id);
+ if (os_snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ os_free(ctrl_ifname);
+ ctrl_ifname = os_strdup(buf);
+ link_id = -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hostapd_cli_reconnect(ctrl_ifname);
if (ctrl_conn) {
if (warning_displayed)
diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c
index 00e02bb034c9..de78a046ca4f 100644
--- a/contrib/wpa/hostapd/main.c
+++ b/contrib/wpa/hostapd/main.c
@@ -20,6 +20,7 @@
#include "crypto/tls.h"
#include "common/version.h"
#include "common/dpp.h"
+#include "common/proc_coord.h"
#include "drivers/driver.h"
#include "eap_server/eap.h"
#include "eap_server/tncs.h"
@@ -160,6 +161,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
struct wpa_driver_capa capa;
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *h_hapd = NULL;
+ void *shared_hapd = NULL;
#endif /* CONFIG_IEEE80211BE */
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
@@ -168,8 +170,16 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
}
#ifdef CONFIG_IEEE80211BE
- if (conf->mld_ap)
+ if (conf->mld_ap) {
+ hostapd_bss_setup_multi_link(hapd, iface->interfaces);
+ if (!hapd->mld) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to initialize context for %s",
+ conf->iface);
+ return -1;
+ }
h_hapd = hostapd_mld_get_first_bss(hapd);
+ }
if (h_hapd) {
hapd->drv_priv = h_hapd->drv_priv;
@@ -191,7 +201,6 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
os_memcpy(hapd->own_addr, b, ETH_ALEN);
}
- hostapd_mld_add_link(hapd);
wpa_printf(MSG_DEBUG,
"Setup of non first link (%d) BSS of MLD %s",
hapd->mld_link_id, hapd->conf->iface);
@@ -254,6 +263,42 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
params.own_addr = hapd->own_addr;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->driver->can_share_drv &&
+ hapd->driver->can_share_drv(hapd, &params, &shared_hapd)) {
+ char force_ifname[IFNAMSIZ];
+ const u8 *addr = params.bssid;
+ u8 if_addr[ETH_ALEN];
+
+ if (!shared_hapd) {
+ wpa_printf(MSG_ERROR, "Failed to get the shared drv");
+ os_free(params.bridge);
+ return -1;
+ }
+
+ /* Share an already initialized driver interface instance
+ * using an AP mode BSS in it instead of adding a new driver
+ * interface instance for the same driver. */
+ if (hostapd_if_add(shared_hapd, WPA_IF_AP_BSS,
+ params.ifname, addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ params.num_bridge && params.bridge[0] ?
+ params.bridge[0] : NULL,
+ 1)) {
+ wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ MACSTR ")", MAC2STR(hapd->own_addr));
+ os_free(params.bridge);
+ return -1;
+ }
+ os_free(params.bridge);
+
+ hapd->interface_added = 1;
+ os_memcpy(params.own_addr, addr ? addr : if_addr, ETH_ALEN);
+
+ goto pre_setup_mld;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
os_free(params.bridge);
if (hapd->drv_priv == NULL) {
@@ -264,6 +309,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
}
#ifdef CONFIG_IEEE80211BE
+pre_setup_mld:
/*
* This is the first interface added to the AP MLD, so have the
* interface hardware address be the MLD address, while the link address
@@ -278,7 +324,6 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
else
os_memcpy(hapd->own_addr, b, ETH_ALEN);
- hostapd_mld_add_link(hapd);
wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
hapd->mld_link_id, hapd->conf->iface);
}
@@ -338,8 +383,14 @@ setup_mld:
hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
MAC2STR(hapd->own_addr));
- hostapd_drv_link_add(hapd, hapd->mld_link_id,
- hapd->own_addr);
+ if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
+ hapd->own_addr)) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to add link %d in MLD %s",
+ hapd->mld_link_id, hapd->conf->iface);
+ return -1;
+ }
+ hostapd_mld_add_link(hapd);
}
#endif /* CONFIG_IEEE80211BE */
@@ -555,7 +606,7 @@ static void show_version(void)
"hostapd v%s\n"
"User space daemon for IEEE 802.11 AP management,\n"
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
- "Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> "
+ "Copyright (c) 2002-2026, Jouni Malinen <j@w1.fi> "
"and contributors\n",
VERSION_STR);
}
@@ -570,6 +621,9 @@ static void usage(void)
"\\\n"
" [-g <global ctrl_iface>] [-G <group>]\\\n"
" [-i <comma-separated list of interface names>]\\\n"
+#ifdef CONFIG_PROCESS_COORDINATION
+ " [-z<process coordination directory>] \\\n"
+#endif /* CONFIG_PROCESS_COORDINATION */
" <configuration file(s)>\n"
"\n"
"options:\n"
@@ -595,6 +649,9 @@ static void usage(void)
" -S start all the interfaces synchronously\n"
" -t include timestamps in some debug messages\n"
" -v show hostapd version\n"
+#ifdef CONFIG_PROCESS_COORDINATION
+ " -z process coordination directory\n"
+#endif /* CONFIG_PROCESS_COORDINATION */
" -q show less debug messages (-qq for even less)\n");
exit(1);
@@ -748,6 +805,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;
}
@@ -779,6 +837,9 @@ int main(int argc, char *argv[])
#ifdef CONFIG_DPP
struct dpp_global_config dpp_conf;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_PROCESS_COORDINATION
+ const char *proc_coord_dir = NULL;
+#endif /* CONFIG_PROCESS_COORDINATION */
if (os_program_init())
return -1;
@@ -793,6 +854,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);
@@ -809,7 +874,7 @@ int main(int argc, char *argv[])
#endif /* CONFIG_DPP */
for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
+ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:qz:");
if (c < 0)
break;
switch (c) {
@@ -885,6 +950,11 @@ int main(int argc, char *argv[])
case 'q':
wpa_debug_level++;
break;
+#ifdef CONFIG_PROCESS_COORDINATION
+ case 'z':
+ proc_coord_dir = optarg;
+ break;
+#endif /* CONFIG_PROCESS_COORDINATION */
default:
usage();
break;
@@ -933,6 +1003,14 @@ int main(int argc, char *argv[])
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
hostapd_periodic, &interfaces, NULL);
+#ifdef CONFIG_PROCESS_COORDINATION
+ if (proc_coord_dir) {
+ interfaces.pc = proc_coord_init(proc_coord_dir);
+ if (!interfaces.pc)
+ goto out;
+ }
+#endif /* CONFIG_PROCESS_COORDINATION */
+
if (fst_global_init()) {
wpa_printf(MSG_ERROR,
"Failed to initialize global FST context");
@@ -1045,6 +1123,10 @@ int main(int argc, char *argv[])
dpp_global_deinit(interfaces.dpp);
#endif /* CONFIG_DPP */
+#ifdef CONFIG_PROCESS_COORDINATION
+ proc_coord_deinit(interfaces.pc);
+#endif /* CONFIG_PROCESS_COORDINATION */
+
if (interfaces.eloop_initialized)
eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
diff --git a/contrib/wpa/hs20/client/Android.mk b/contrib/wpa/hs20/client/Android.mk
index 8d208b25901b..1e1082be2e98 100644
--- a/contrib/wpa/hs20/client/Android.mk
+++ b/contrib/wpa/hs20/client/Android.mk
@@ -30,10 +30,7 @@ L_CFLAGS += -DCONFIG_CTRL_IFACE
L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-OBJS = spp_client.c
-OBJS += oma_dm_client.c
-OBJS += osu_client.c
-OBJS += est.c
+OBJS = osu_client.c
OBJS += ../../src/common/wpa_ctrl.c
OBJS += ../../src/common/wpa_helpers.c
OBJS += ../../src/utils/xml-utils.c
diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile
index 4dcfe2d3bf2c..9e65dc63e8d5 100644
--- a/contrib/wpa/hs20/client/Makefile
+++ b/contrib/wpa/hs20/client/Makefile
@@ -24,10 +24,7 @@ LIBS += $(GTKLIBS)
endif
endif
-OBJS=spp_client.o
-OBJS += oma_dm_client.o
-OBJS += osu_client.o
-OBJS += est.o
+OBJS = osu_client.o
OBJS += ../../src/utils/xml-utils.o
CFLAGS += -DCONFIG_CTRL_IFACE
CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
diff --git a/contrib/wpa/hs20/client/devdetail.xml b/contrib/wpa/hs20/client/devdetail.xml
deleted file mode 100644
index 6d0389e8a133..000000000000
--- a/contrib/wpa/hs20/client/devdetail.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
- <Ext>
- <org.wi-fi>
- <Wi-Fi>
- <EAPMethodList>
- <EAPMethod1>
- <EAPType>13</EAPType>
- </EAPMethod1>
- <EAPMethod2>
- <EAPType>21</EAPType>
- <InnerMethod>MS-CHAP-V2</InnerMethod>
- </EAPMethod2>
- <EAPMethod3>
- <EAPType>18</EAPType>
- </EAPMethod3>
- <EAPMethod4>
- <EAPType>23</EAPType>
- </EAPMethod4>
- <EAPMethod5>
- <EAPType>50</EAPType>
- </EAPMethod5>
- </EAPMethodList>
- <ManufacturingCertificate>false</ManufacturingCertificate>
- <Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
- <IMSI>310026000000000</IMSI>
- <IMEI_MEID>imei:490123456789012</IMEI_MEID>
- <ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
- <Ops>
- <launchBrowserToURI></launchBrowserToURI>
- <negotiateClientCertTLS></negotiateClientCertTLS>
- <getCertificate></getCertificate>
- </Ops>
- </Wi-Fi>
- </org.wi-fi>
- </Ext>
- <URI>
- <MaxDepth>0</MaxDepth>
- <MaxTotLen>0</MaxTotLen>
- <MaxSegLen>0</MaxSegLen>
- </URI>
- <DevType>MobilePhone</DevType>
- <OEM>Manufacturer</OEM>
- <FwV>1.0</FwV>
- <SwV>1.0</SwV>
- <HwV>1.0</HwV>
- <LrgObj>false</LrgObj>
-</DevDetail>
diff --git a/contrib/wpa/hs20/client/devinfo.xml b/contrib/wpa/hs20/client/devinfo.xml
deleted file mode 100644
index d48a520a98a1..000000000000
--- a/contrib/wpa/hs20/client/devinfo.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
- <DevId>urn:Example:HS20-station:123456</DevId>
- <Man>Manufacturer</Man>
- <Mod>HS20-station</Mod>
- <DmV>1.2</DmV>
- <Lang>en</Lang>
-</DevInfo>
diff --git a/contrib/wpa/hs20/client/est.c b/contrib/wpa/hs20/client/est.c
deleted file mode 100644
index 425b72d18e6a..000000000000
--- a/contrib/wpa/hs20/client/est.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Hotspot 2.0 OSU client - EST client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs7.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-#include <openssl/opensslv.h>
-#include <openssl/buffer.h>
-
-#include "common.h"
-#include "utils/base64.h"
-#include "utils/xml-utils.h"
-#include "utils/http-utils.h"
-#include "osu_client.h"
-
-
-static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
- size_t len, char *pem_file, char *der_file)
-{
-#ifdef OPENSSL_IS_BORINGSSL
- CBS pkcs7_cbs;
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7 *p7 = NULL;
- const unsigned char *p = pkcs7;
-#endif /* OPENSSL_IS_BORINGSSL */
- STACK_OF(X509) *certs;
- int i, num, ret = -1;
- BIO *out = NULL;
-
-#ifdef OPENSSL_IS_BORINGSSL
- certs = sk_X509_new_null();
- if (!certs)
- goto fail;
- CBS_init(&pkcs7_cbs, pkcs7, len);
- if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
- wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- write_result(ctx, "Could not parse PKCS#7 object from EST");
- goto fail;
- }
-#else /* OPENSSL_IS_BORINGSSL */
- p7 = d2i_PKCS7(NULL, &p, len);
- if (p7 == NULL) {
- wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- write_result(ctx, "Could not parse PKCS#7 object from EST");
- goto fail;
- }
-
- switch (OBJ_obj2nid(p7->type)) {
- case NID_pkcs7_signed:
- certs = p7->d.sign->cert;
- break;
- case NID_pkcs7_signedAndEnveloped:
- certs = p7->d.signed_and_enveloped->cert;
- break;
- default:
- certs = NULL;
- break;
- }
-#endif /* OPENSSL_IS_BORINGSSL */
-
- if (!certs || ((num = sk_X509_num(certs)) == 0)) {
- wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
- write_result(ctx, "No certificates found in PKCS#7 object");
- goto fail;
- }
-
- if (der_file) {
- FILE *f = fopen(der_file, "wb");
- if (f == NULL)
- goto fail;
- i2d_X509_fp(f, sk_X509_value(certs, 0));
- fclose(f);
- }
-
- if (pem_file) {
- out = BIO_new(BIO_s_file());
- if (out == NULL ||
- BIO_write_filename(out, pem_file) <= 0)
- goto fail;
-
- for (i = 0; i < num; i++) {
- X509 *cert = sk_X509_value(certs, i);
- X509_print(out, cert);
- PEM_write_bio_X509(out, cert);
- BIO_puts(out, "\n");
- }
- }
-
- ret = 0;
-
-fail:
-#ifdef OPENSSL_IS_BORINGSSL
- if (certs)
- sk_X509_pop_free(certs, X509_free);
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7_free(p7);
-#endif /* OPENSSL_IS_BORINGSSL */
- if (out)
- BIO_free_all(out);
-
- return ret;
-}
-
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
-{
- char *buf, *resp;
- size_t buflen;
- unsigned char *pkcs7;
- size_t pkcs7_len, resp_len;
- int res;
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
-
- os_snprintf(buf, buflen, "%s/cacerts", url);
- wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
- write_summary(ctx, "Download EST cacerts from %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
- ctx->ca_fname);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- if (res < 0) {
- wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
- buf);
- write_result(ctx, "Failed to download EST cacerts from %s",
- buf);
- os_free(buf);
- return -1;
- }
- os_free(buf);
-
- resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
- write_result(ctx, "Could not read EST cacerts");
- return -1;
- }
-
- pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
- if (pkcs7 && pkcs7_len < resp_len / 2) {
- wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
- (unsigned int) pkcs7_len, (unsigned int) resp_len);
- os_free(pkcs7);
- pkcs7 = NULL;
- }
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
- pkcs7 = os_malloc(resp_len);
- if (pkcs7) {
- os_memcpy(pkcs7, resp, resp_len);
- pkcs7_len = resp_len;
- }
- }
- os_free(resp);
-
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
- write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
- return -1;
- }
-
- res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
- NULL);
- os_free(pkcs7);
- if (res < 0) {
- wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
- write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
- return -1;
- }
- unlink("Cert/est-cacerts.txt");
-
- return 0;
-}
-
-
-/*
- * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
- *
- * AttrOrOID ::= CHOICE {
- * oid OBJECT IDENTIFIER,
- * attribute Attribute }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
- */
-
-typedef struct {
- ASN1_OBJECT *type;
- STACK_OF(ASN1_OBJECT) *values;
-} Attribute;
-
-typedef struct {
- int type;
- union {
- ASN1_OBJECT *oid;
- Attribute *attribute;
- } d;
-} AttrOrOID;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-DEFINE_STACK_OF(AttrOrOID)
-#endif
-
-typedef struct {
- int type;
- STACK_OF(AttrOrOID) *attrs;
-} CsrAttrs;
-
-ASN1_SEQUENCE(Attribute) = {
- ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
- ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
-} ASN1_SEQUENCE_END(Attribute);
-
-ASN1_CHOICE(AttrOrOID) = {
- ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
- ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
-} ASN1_CHOICE_END(AttrOrOID);
-
-ASN1_CHOICE(CsrAttrs) = {
- ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
-} ASN1_CHOICE_END(CsrAttrs);
-
-IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
-
-
-static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100];
- int res;
-
- if (!oid)
- return;
-
- res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- return;
-
- if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
- wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
- } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
- wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
- } else {
- wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
- }
-}
-
-
-static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
- STACK_OF(ASN1_OBJECT) *values,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100];
- int i, num, res;
-
- num = sk_ASN1_OBJECT_num(values);
- for (i = 0; i < num; i++) {
- ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
-
- res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- continue;
-
- if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq macAddress");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq imei");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq meid");
- } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
- wpa_printf(MSG_INFO, "TODO: extReq DevId");
- } else {
- wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
- txt);
- }
- }
-}
-
-
-static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
- STACK_OF(X509_EXTENSION) *exts)
-{
- char txt[100], txt2[100];
- int i, num, res;
-
- if (!attr || !attr->type || !attr->values)
- return;
-
- res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
- if (res < 0 || res >= (int) sizeof(txt))
- return;
-
- if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
- add_csrattrs_ext_req(ctx, attr->values, exts);
- return;
- }
-
- num = sk_ASN1_OBJECT_num(attr->values);
- for (i = 0; i < num; i++) {
- ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
-
- res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
- if (res < 0 || res >= (int) sizeof(txt2))
- continue;
-
- wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
- txt, txt2);
- }
-}
-
-
-static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
- STACK_OF(X509_EXTENSION) *exts)
-{
- int i, num;
-
- if (!csrattrs || ! csrattrs->attrs)
- return;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- num = sk_AttrOrOID_num(csrattrs->attrs);
-#else
- num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
-#endif
- for (i = 0; i < num; i++) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
-#else
- AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
-#endif
- switch (ao->type) {
- case 0:
- add_csrattrs_oid(ctx, ao->d.oid, exts);
- break;
- case 1:
- add_csrattrs_attr(ctx, ao->d.attribute, exts);
- break;
- }
- }
-}
-
-
-static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
- char *csr_pem, char *est_req, char *old_cert,
- CsrAttrs *csrattrs)
-{
- EVP_PKEY_CTX *pctx = NULL;
- EVP_PKEY *pkey = NULL;
- X509_REQ *req = NULL;
- int ret = -1;
- unsigned int val;
- X509_NAME *subj = NULL;
- char name[100];
- STACK_OF(X509_EXTENSION) *exts = NULL;
- X509_EXTENSION *ex;
- BIO *out;
- CONF *ctmp = NULL;
-
- wpa_printf(MSG_INFO, "Generate RSA private key");
- write_summary(ctx, "Generate RSA private key");
- pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
- if (!pctx)
- return -1;
-
- if (EVP_PKEY_keygen_init(pctx) <= 0)
- goto fail;
-
- if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
- goto fail;
-
- if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
- goto fail;
- EVP_PKEY_CTX_free(pctx);
- pctx = NULL;
-
- if (key_pem) {
- FILE *f = fopen(key_pem, "wb");
- if (f == NULL)
- goto fail;
- if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
- wpa_printf(MSG_INFO, "Could not write private key: %s",
- ERR_error_string(ERR_get_error(), NULL));
- fclose(f);
- goto fail;
- }
- fclose(f);
- }
-
- wpa_printf(MSG_INFO, "Generate CSR");
- write_summary(ctx, "Generate CSR");
- req = X509_REQ_new();
- if (req == NULL)
- goto fail;
-
- if (old_cert) {
- FILE *f;
- X509 *cert;
- int res;
-
- f = fopen(old_cert, "r");
- if (f == NULL)
- goto fail;
- cert = PEM_read_X509(f, NULL, NULL, NULL);
- fclose(f);
-
- if (cert == NULL)
- goto fail;
- res = X509_REQ_set_subject_name(req,
- X509_get_subject_name(cert));
- X509_free(cert);
- if (!res)
- goto fail;
- } else {
- os_get_random((u8 *) &val, sizeof(val));
- os_snprintf(name, sizeof(name), "cert-user-%u", val);
- subj = X509_NAME_new();
- if (subj == NULL ||
- !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
- (unsigned char *) name,
- -1, -1, 0) ||
- !X509_REQ_set_subject_name(req, subj))
- goto fail;
- X509_NAME_free(subj);
- subj = NULL;
- }
-
- if (!X509_REQ_set_pubkey(req, pkey))
- goto fail;
-
- exts = sk_X509_EXTENSION_new_null();
- if (!exts)
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
- "CA:FALSE");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
- "nonRepudiation,digitalSignature,keyEncipherment");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
- "1.3.6.1.4.1.40808.1.1.2");
- if (ex == NULL ||
- !sk_X509_EXTENSION_push(exts, ex))
- goto fail;
-
- add_csrattrs(ctx, csrattrs, exts);
-
- if (!X509_REQ_add_extensions(req, exts))
- goto fail;
- sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
- exts = NULL;
-
- if (!X509_REQ_sign(req, pkey, EVP_sha256()))
- goto fail;
-
- out = BIO_new(BIO_s_mem());
- if (out) {
- char *txt;
- size_t rlen;
-
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
- X509_REQ_print(out, req);
-#endif
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (txt) {
- int res = BIO_read(out, txt, rlen);
- if (res > 0) {
- txt[res] = '\0';
- wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
- txt);
- }
- os_free(txt);
- }
- BIO_free(out);
- }
-
- if (csr_pem) {
- FILE *f = fopen(csr_pem, "w");
- if (f == NULL)
- goto fail;
-#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
- X509_REQ_print_fp(f, req);
-#endif
- if (!PEM_write_X509_REQ(f, req)) {
- fclose(f);
- goto fail;
- }
- fclose(f);
- }
-
- if (est_req) {
- BIO *mem = BIO_new(BIO_s_mem());
- BUF_MEM *ptr;
- char *pos, *end, *buf_end;
- FILE *f;
-
- if (mem == NULL)
- goto fail;
- if (!PEM_write_bio_X509_REQ(mem, req)) {
- BIO_free(mem);
- goto fail;
- }
-
- BIO_get_mem_ptr(mem, &ptr);
- pos = ptr->data;
- buf_end = pos + ptr->length;
-
- /* Remove START/END lines */
- while (pos < buf_end && *pos != '\n')
- pos++;
- if (pos == buf_end) {
- BIO_free(mem);
- goto fail;
- }
- pos++;
-
- end = pos;
- while (end < buf_end && *end != '-')
- end++;
-
- f = fopen(est_req, "w");
- if (f == NULL) {
- BIO_free(mem);
- goto fail;
- }
- fwrite(pos, end - pos, 1, f);
- fclose(f);
-
- BIO_free(mem);
- }
-
- ret = 0;
-fail:
- if (exts)
- sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
- if (subj)
- X509_NAME_free(subj);
- if (req)
- X509_REQ_free(req);
- if (pkey)
- EVP_PKEY_free(pkey);
- if (pctx)
- EVP_PKEY_CTX_free(pctx);
- return ret;
-}
-
-
-int est_build_csr(struct hs20_osu_client *ctx, const char *url)
-{
- char *buf;
- size_t buflen;
- int res;
- char old_cert_buf[200];
- char *old_cert = NULL;
- CsrAttrs *csrattrs = NULL;
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL)
- return -1;
-
- os_snprintf(buf, buflen, "%s/csrattrs", url);
- wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
- write_summary(ctx, "Download EST csrattrs from %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
- ctx->ca_fname);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- os_free(buf);
- if (res < 0) {
- wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
- } else {
- size_t resp_len;
- char *resp;
- unsigned char *attrs;
- const unsigned char *pos;
- size_t attrs_len;
-
- resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Could not read csrattrs");
- return -1;
- }
-
- attrs = base64_decode(resp, resp_len, &attrs_len);
- os_free(resp);
-
- if (attrs == NULL) {
- wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
- return -1;
- }
- unlink("Cert/est-csrattrs.txt");
-
- pos = attrs;
- csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
- os_free(attrs);
- if (csrattrs == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
- /* Continue assuming no additional requirements */
- }
- }
-
- if (ctx->client_cert_present) {
- os_snprintf(old_cert_buf, sizeof(old_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- old_cert = old_cert_buf;
- }
-
- res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
- "Cert/est-req.b64", old_cert, csrattrs);
- if (csrattrs)
- CsrAttrs_free(csrattrs);
-
- return res;
-}
-
-
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
- const char *user, const char *pw)
-{
- char *buf, *resp, *req, *req2;
- size_t buflen, resp_len, len, pkcs7_len;
- unsigned char *pkcs7;
- char client_cert_buf[200];
- char client_key_buf[200];
- const char *client_cert = NULL, *client_key = NULL;
- int res;
-
- req = os_readfile("Cert/est-req.b64", &len);
- if (req == NULL) {
- wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
- return -1;
- }
- req2 = os_realloc(req, len + 1);
- if (req2 == NULL) {
- os_free(req);
- return -1;
- }
- req2[len] = '\0';
- req = req2;
- wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
-
- buflen = os_strlen(url) + 100;
- buf = os_malloc(buflen);
- if (buf == NULL) {
- os_free(req);
- return -1;
- }
-
- if (ctx->client_cert_present) {
- os_snprintf(buf, buflen, "%s/simplereenroll", url);
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- } else
- os_snprintf(buf, buflen, "%s/simpleenroll", url);
- wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
- write_summary(ctx, "EST simpleenroll URL: %s", buf);
- ctx->no_osu_cert_validation = 1;
- http_ocsp_set(ctx->http, 1);
- resp = http_post(ctx->http, buf, req, "application/pkcs10",
- "Content-Transfer-Encoding: base64",
- ctx->ca_fname, user, pw, client_cert, client_key,
- &resp_len);
- http_ocsp_set(ctx->http,
- (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
- os_free(buf);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "EST certificate enrollment failed");
- write_result(ctx, "EST certificate enrollment failed");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
-
- pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
- pkcs7 = os_malloc(resp_len);
- if (pkcs7) {
- os_memcpy(pkcs7, resp, resp_len);
- pkcs7_len = resp_len;
- }
- }
- os_free(resp);
-
- if (pkcs7 == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
- write_result(ctx, "Failed to parse EST simpleenroll base64 response");
- return -1;
- }
-
- res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
- "Cert/est_cert.der");
- os_free(pkcs7);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
- write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
- ctx->client_cert_present ? "re" : "");
- write_summary(ctx, "EST simple%senroll completed successfully",
- ctx->client_cert_present ? "re" : "");
-
- return 0;
-}
diff --git a/contrib/wpa/hs20/client/oma_dm_client.c b/contrib/wpa/hs20/client/oma_dm_client.c
deleted file mode 100644
index bcd68b8775d5..000000000000
--- a/contrib/wpa/hs20/client/oma_dm_client.c
+++ /dev/null
@@ -1,1398 +0,0 @@
-/*
- * Hotspot 2.0 - OMA DM client
- * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/browser.h"
-#include "osu_client.h"
-
-
-#define DM_SERVER_INITIATED_MGMT 1200
-#define DM_CLIENT_INITIATED_MGMT 1201
-#define DM_GENERIC_ALERT 1226
-
-/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
-#define DM_RESP_OK 200
-#define DM_RESP_AUTH_ACCEPTED 212
-#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
-#define DM_RESP_NOT_EXECUTED 215
-#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
-#define DM_RESP_NOT_MODIFIED 304
-#define DM_RESP_BAD_REQUEST 400
-#define DM_RESP_UNAUTHORIZED 401
-#define DM_RESP_FORBIDDEN 403
-#define DM_RESP_NOT_FOUND 404
-#define DM_RESP_COMMAND_NOT_ALLOWED 405
-#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
-#define DM_RESP_MISSING_CREDENTIALS 407
-#define DM_RESP_CONFLICT 409
-#define DM_RESP_GONE 410
-#define DM_RESP_INCOMPLETE_COMMAND 412
-#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
-#define DM_RESP_URI_TOO_LONG 414
-#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
-#define DM_RESP_REQ_TOO_BIG 416
-#define DM_RESP_ALREADY_EXISTS 418
-#define DM_RESP_DEVICE_FULL 420
-#define DM_RESP_SIZE_MISMATCH 424
-#define DM_RESP_PERMISSION_DENIED 425
-#define DM_RESP_COMMAND_FAILED 500
-#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
-#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
-
-#define DM_HS20_SUBSCRIPTION_CREATION \
- "org.wi-fi.hotspot2dot0.SubscriptionCreation"
-#define DM_HS20_SUBSCRIPTION_PROVISIONING \
- "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
-#define DM_HS20_SUBSCRIPTION_REMEDIATION \
- "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
-#define DM_HS20_POLICY_UPDATE \
- "org.wi-fi.hotspot2dot0.PolicyUpdate"
-
-#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
-#define DM_URI_LAUNCH_BROWSER \
- "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *locuri, const char *data);
-
-
-static const char * int2str(int val)
-{
- static char buf[20];
- snprintf(buf, sizeof(buf), "%d", val);
- return buf;
-}
-
-
-static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
- xml_node_t *node)
-{
- xml_node_t *locuri;
- char *uri, *ret = NULL;
-
- locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
- if (locuri == NULL)
- return NULL;
-
- uri = xml_node_get_text(ctx->xml, locuri);
- if (uri)
- ret = os_strdup(uri);
- xml_node_get_text_free(ctx->xml, uri);
- return ret;
-}
-
-
-static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *element, const char *uri)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, element);
- if (node == NULL)
- return;
- xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
-}
-
-
-static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml, *synchdr;
- xml_namespace_t *ns;
-
- if (!ctx->devid) {
- wpa_printf(MSG_ERROR,
- "DevId from devinfo.xml is not available - cannot use OMA DM");
- return NULL;
- }
-
- syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
- "SyncML");
-
- synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
- xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
- xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
- xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
- xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
-
- oma_dm_add_locuri(ctx, synchdr, "Target", url);
- oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
-
- return syncml;
-}
-
-
-static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid)
-{
- xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
-}
-
-
-static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid, int data)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Alert");
- if (node == NULL)
- return NULL;
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
-
- return node;
-}
-
-
-static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
- int msgref, int cmdref, int cmdid,
- const char *cmd, int data, const char *targetref)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Status");
- if (node == NULL)
- return NULL;
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
- if (cmdref)
- xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
- int2str(cmdref));
- xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
- xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
- if (targetref) {
- xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
- targetref);
- }
-
- return node;
-}
-
-
-static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
- int msgref, int cmdref, int cmdid,
- const char *locuri, const char *data)
-{
- xml_node_t *node;
-
- node = xml_node_create(ctx->xml, parent, NULL, "Results");
- if (node == NULL)
- return NULL;
-
- oma_dm_add_cmdid(ctx, node, cmdid);
- xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
- xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
- add_item(ctx, node, locuri, data);
-
- return node;
-}
-
-
-static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
- const char *fname)
-{
- xml_node_t *fnode, *tnds;
- char *str;
-
- fnode = node_from_file(ctx->xml, fname);
- if (!fnode)
- return NULL;
- tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
- xml_node_free(ctx->xml, fnode);
- if (!tnds)
- return NULL;
-
- str = xml_node_to_str(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (str == NULL)
- return NULL;
- wpa_printf(MSG_INFO, "MgmtTree: %s", str);
-
- return str;
-}
-
-
-static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
- const char *locuri, const char *data)
-{
- xml_node_t *item, *node;
-
- item = xml_node_create(ctx->xml, parent, NULL, "Item");
- oma_dm_add_locuri(ctx, item, "Source", locuri);
- node = xml_node_create(ctx->xml, item, NULL, "Meta");
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
- "Chr");
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
- "text/plain");
- xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
- int cmdid)
-{
- xml_node_t *info, *child, *replace;
- const char *name;
- char locuri[200], *txt;
-
- info = node_from_file(ctx->xml, "devinfo.xml");
- if (info == NULL) {
- wpa_printf(MSG_INFO, "Could not read devinfo.xml");
- return;
- }
-
- replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
- if (replace == NULL) {
- xml_node_free(ctx->xml, info);
- return;
- }
- oma_dm_add_cmdid(ctx, replace, cmdid);
-
- xml_node_for_each_child(ctx->xml, child, info) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
- txt = xml_node_get_text(ctx->xml, child);
- if (txt) {
- add_item(ctx, replace, locuri, txt);
- xml_node_get_text_free(ctx->xml, txt);
- }
- }
-
- xml_node_free(ctx->xml, info);
-}
-
-
-static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
- xml_node_t *syncbody,
- int cmdid, const char *oper,
- const char *data)
-{
- xml_node_t *node, *item;
- char buf[200];
-
- node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
-
- item = xml_node_create(ctx->xml, node, NULL, "Item");
- oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
- node = xml_node_create(ctx->xml, item, NULL, "Meta");
- snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
- xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
- "xml");
- xml_node_create_text(ctx->xml, item, NULL, "Data", data);
-}
-
-
-static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
- const char *url, int msgid, const char *oper)
-{
- xml_node_t *syncml, *syncbody;
- char *str;
- int cmdid = 0;
-
- syncml = oma_dm_build_hdr(ctx, url, msgid);
- if (syncml == NULL)
- return NULL;
-
- syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
- if (syncbody == NULL) {
- xml_node_free(ctx->xml, syncml);
- return NULL;
- }
-
- cmdid++;
- add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
-
- str = mo_str(ctx, NULL, "devdetail.xml");
- if (str == NULL) {
- xml_node_free(ctx->xml, syncml);
- return NULL;
- }
- cmdid++;
- oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
- os_free(str);
-
- cmdid++;
- add_replace_devinfo(ctx, syncbody, cmdid);
-
- xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid,
- DM_HS20_SUBSCRIPTION_PROVISIONING);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
-
- return syncml;
-}
-
-
-static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
- const char *url, int msgid)
-{
- xml_node_t *syncml;
-
- syncml = build_oma_dm_1(ctx, url, msgid,
- DM_HS20_SUBSCRIPTION_REMEDIATION);
- if (syncml)
- debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
-
- return syncml;
-}
-
-
-static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- xml_node_t *node;
- char *data;
- int res;
-
- node = get_node(ctx->xml, exec, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Invalid data");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Data: %s", data);
- wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
- write_summary(ctx, "Launch browser to URI '%s'", data);
- res = hs20_web_browser(data, 1);
- xml_node_get_text_free(ctx->xml, data);
- if (res > 0) {
- wpa_printf(MSG_INFO, "User response in browser completed successfully");
- write_summary(ctx, "User response in browser completed successfully");
- return DM_RESP_OK;
- } else {
- wpa_printf(MSG_INFO, "Failed to receive user response");
- write_summary(ctx, "Failed to receive user response");
- return DM_RESP_COMMAND_FAILED;
- }
-}
-
-
-static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- xml_node_t *node, *getcert;
- char *data;
- const char *name;
- int res;
-
- wpa_printf(MSG_INFO, "Client certificate enrollment");
- write_summary(ctx, "Client certificate enrollment");
-
- node = get_node(ctx->xml, exec, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Invalid data");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Data: %s", data);
- getcert = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
-
- if (getcert == NULL) {
- wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
-
- name = xml_node_get_localname(ctx->xml, getcert);
- if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
- wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
- name);
- return DM_RESP_BAD_REQUEST;
- }
-
- res = osu_get_certificate(ctx, getcert);
-
- xml_node_free(ctx->xml, getcert);
-
- return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
-}
-
-
-static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
-{
- char *locuri;
- int ret;
-
- locuri = oma_dm_get_target_locuri(ctx, exec);
- if (locuri == NULL) {
- wpa_printf(MSG_INFO, "No Target LocURI node found");
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
-
- if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
- "launchBrowserToURI") == 0) {
- ret = oma_dm_exec_browser(ctx, exec);
- } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
- "getCertificate") == 0) {
- ret = oma_dm_exec_get_cert(ctx, exec);
- } else {
- wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
- ret = DM_RESP_NOT_FOUND;
- }
- os_free(locuri);
-
- return ret;
-}
-
-
-static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
- xml_node_t *add, xml_node_t *pps,
- const char *pps_fname)
-{
- const char *pos;
- size_t fqdn_len;
- xml_node_t *node, *tnds, *unode, *pps_node;
- char *data, *uri, *upos, *end;
- int use_tnds = 0;
- size_t uri_len;
-
- wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
-
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL)
- return DM_RESP_COMMAND_FAILED;
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
- ctx->fqdn);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node) {
- wpa_printf(MSG_INFO, "Specified PPS node exists already");
- return DM_RESP_ALREADY_EXISTS;
- }
-
- uri = os_strdup(pos);
- if (uri == NULL)
- return DM_RESP_COMMAND_FAILED;
- while (!pps_node) {
- upos = os_strrchr(uri, '/');
- if (!upos)
- break;
- upos[0] = '\0';
- pps_node = get_node(ctx->xml, pps, uri);
- wpa_printf(MSG_INFO, "Node %s %s", uri,
- pps_node ? "exists" : "does not exist");
- }
-
- wpa_printf(MSG_INFO, "Parent URI: %s", uri);
-
- if (!pps_node) {
- /* Add at root of PPS MO */
- pps_node = pps;
- }
-
- uri_len = os_strlen(uri);
- os_strlcpy(uri, pos + uri_len, os_strlen(pos));
- upos = uri;
- while (*upos == '/')
- upos++;
- wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
-
- for (;;) {
- end = os_strchr(upos, '/');
- if (!end)
- break;
- *end = '\0';
- wpa_printf(MSG_INFO, "Adding interim node %s", upos);
- pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
- if (pps_node == NULL) {
- os_free(uri);
- return DM_RESP_COMMAND_FAILED;
- }
- upos = end + 1;
- }
-
- wpa_printf(MSG_INFO, "Adding node %s", upos);
-
- node = get_node(ctx->xml, add, "Item/Meta/Type");
- if (node) {
- char *type;
- type = xml_node_get_text(ctx->xml, node);
- if (type == NULL) {
- wpa_printf(MSG_ERROR, "Could not find type text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
- use_tnds = node &&
- os_strstr(type, "application/vnd.syncml.dmtnds+xml");
- }
-
- node = get_node(ctx->xml, add, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Add/Item/Data found");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
-
- if (use_tnds) {
- tnds = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO,
- "Could not parse Add/Item/Data text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "Could not parse TNDS text");
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- xml_node_add_child(ctx->xml, pps_node, unode);
- } else {
- /* TODO: What to do here? */
- os_free(uri);
- return DM_RESP_BAD_REQUEST;
- }
-
- os_free(uri);
-
- if (update_pps_file(ctx, pps_fname, pps) < 0)
- return DM_RESP_COMMAND_FAILED;
-
- ctx->pps_updated = 1;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
- xml_node_t *pps, const char *pps_fname)
-{
- xml_node_t *node;
- char *locuri;
- char fname[300];
- int ret;
-
- node = get_node(ctx->xml, add, "Item/Target/LocURI");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Target LocURI node found");
- return DM_RESP_BAD_REQUEST;
- }
- locuri = xml_node_get_text(ctx->xml, node);
- if (locuri == NULL) {
- wpa_printf(MSG_ERROR, "No LocURI node text found");
- return DM_RESP_BAD_REQUEST;
- }
- wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
- xml_node_get_text_free(ctx->xml, locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
-
- node = get_node(ctx->xml, add, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Data node found");
- xml_node_get_text_free(ctx->xml, locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- if (pps_fname && os_file_exists(pps_fname)) {
- ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
- if (ret != DM_RESP_OK) {
- xml_node_get_text_free(ctx->xml, locuri);
- return ret;
- }
- ret = 0;
- os_strlcpy(fname, pps_fname, sizeof(fname));
- } else
- ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
- xml_node_get_text_free(ctx->xml, locuri);
- if (ret < 0)
- return ret == -2 ? DM_RESP_ALREADY_EXISTS :
- DM_RESP_COMMAND_FAILED;
-
- if (ctx->no_reconnect == 2) {
- os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
- fname);
- ctx->pps_cred_set = 1;
- return DM_RESP_OK;
- }
-
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, fname);
-
- if (ctx->no_reconnect)
- return DM_RESP_OK;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
- xml_node_t *pps, const char *pps_fname)
-{
- char *locuri, *pos;
- size_t fqdn_len;
- xml_node_t *node, *tnds, *unode, *pps_node, *parent;
- char *data;
- int use_tnds = 0;
-
- locuri = oma_dm_get_target_locuri(ctx, replace);
- if (locuri == NULL)
- return DM_RESP_BAD_REQUEST;
-
- wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL) {
- os_free(locuri);
- return DM_RESP_COMMAND_FAILED;
- }
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node == NULL) {
- wpa_printf(MSG_INFO, "Specified PPS node not found");
- os_free(locuri);
- return DM_RESP_NOT_FOUND;
- }
-
- node = get_node(ctx->xml, replace, "Item/Meta/Type");
- if (node) {
- char *type;
- type = xml_node_get_text(ctx->xml, node);
- if (type == NULL) {
- wpa_printf(MSG_INFO, "Could not find type text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
- use_tnds = node &&
- os_strstr(type, "application/vnd.syncml.dmtnds+xml");
- }
-
- node = get_node(ctx->xml, replace, "Item/Data");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Replace/Item/Data found");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- data = xml_node_get_text(ctx->xml, node);
- if (data == NULL) {
- wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
-
- if (use_tnds) {
- tnds = xml_node_from_buf(ctx->xml, data);
- xml_node_get_text_free(ctx->xml, data);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO,
- "Could not parse Replace/Item/Data text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "Could not parse TNDS text");
- os_free(locuri);
- return DM_RESP_BAD_REQUEST;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- parent = xml_node_get_parent(ctx->xml, pps_node);
- xml_node_detach(ctx->xml, pps_node);
- xml_node_add_child(ctx->xml, parent, unode);
- } else {
- xml_node_set_text(ctx->xml, pps_node, data);
- xml_node_get_text_free(ctx->xml, data);
- }
-
- os_free(locuri);
-
- if (update_pps_file(ctx, pps_fname, pps) < 0)
- return DM_RESP_COMMAND_FAILED;
-
- ctx->pps_updated = 1;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
- xml_node_t *pps, const char *pps_fname, char **value)
-{
- char *locuri, *pos;
- size_t fqdn_len;
- xml_node_t *pps_node;
- const char *name;
-
- *value = NULL;
-
- locuri = oma_dm_get_target_locuri(ctx, get);
- if (locuri == NULL)
- return DM_RESP_BAD_REQUEST;
-
- wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
- if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos = locuri + 8;
-
- if (ctx->fqdn == NULL)
- return DM_RESP_COMMAND_FAILED;
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO,
- "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
-
- pps_node = get_node(ctx->xml, pps, pos);
- if (pps_node == NULL) {
- wpa_printf(MSG_INFO, "Specified PPS node not found");
- os_free(locuri);
- return DM_RESP_NOT_FOUND;
- }
-
- name = xml_node_get_localname(ctx->xml, pps_node);
- wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
- if (os_strcasecmp(name, "Password") == 0) {
- wpa_printf(MSG_INFO, "Do not allow Get for Password node");
- os_free(locuri);
- return DM_RESP_PERMISSION_DENIED;
- }
-
- /*
- * TODO: No support for DMTNDS, so if interior node, reply with a
- * list of children node names in Results element. The child list type is
- * defined in [DMTND].
- */
-
- *value = xml_node_get_text(ctx->xml, pps_node);
- if (*value == NULL)
- return DM_RESP_COMMAND_FAILED;
-
- return DM_RESP_OK;
-}
-
-
-static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
-{
- xml_node_t *cnode;
- char *str;
- int ret;
-
- cnode = get_node(ctx->xml, node, "CmdID");
- if (cnode == NULL)
- return 0;
-
- str = xml_node_get_text(ctx->xml, cnode);
- if (str == NULL)
- return 0;
- ret = atoi(str);
- xml_node_get_text_free(ctx->xml, str);
- return ret;
-}
-
-
-static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
- const char *url, xml_node_t *syncml,
- const char *ext_hdr,
- const char *username, const char *password,
- const char *client_cert,
- const char *client_key)
-{
- xml_node_t *resp;
- char *str, *res;
- char *resp_uri = NULL;
-
- str = xml_node_to_str(ctx->xml, syncml);
- xml_node_free(ctx->xml, syncml);
- if (str == NULL)
- return NULL;
-
- wpa_printf(MSG_INFO, "Send OMA DM Package");
- write_summary(ctx, "Send OMA DM Package");
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
- res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
- ext_hdr, ctx->ca_fname, username, password,
- client_cert, client_key, NULL);
- os_free(str);
- os_free(resp_uri);
- resp_uri = NULL;
-
- if (res == NULL) {
- const char *err = http_get_err(ctx->http);
- if (err) {
- wpa_printf(MSG_INFO, "HTTP error: %s", err);
- write_result(ctx, "HTTP error: %s", err);
- } else {
- write_summary(ctx, "Failed to send OMA DM Package");
- }
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "Server response: %s", res);
-
- wpa_printf(MSG_INFO, "Process OMA DM Package");
- write_summary(ctx, "Process received OMA DM Package");
- resp = xml_node_from_buf(ctx->xml, res);
- os_free(res);
- if (resp == NULL) {
- wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
- return NULL;
- }
-
- debug_dump_node(ctx, "OMA DM Package", resp);
-
- return resp;
-}
-
-
-static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
- xml_node_t *resp, int msgid,
- char **ret_resp_uri,
- xml_node_t *pps, const char *pps_fname)
-{
- xml_node_t *syncml, *syncbody, *hdr, *body, *child;
- const char *name;
- char *resp_uri = NULL;
- int server_msgid = 0;
- int cmdid = 0;
- int server_cmdid;
- int resp_needed = 0;
- char *tmp;
- int final = 0;
- char *locuri;
-
- *ret_resp_uri = NULL;
-
- name = xml_node_get_localname(ctx->xml, resp);
- if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
- wpa_printf(MSG_INFO, "SyncML node not found");
- return NULL;
- }
-
- hdr = get_node(ctx->xml, resp, "SyncHdr");
- body = get_node(ctx->xml, resp, "SyncBody");
- if (hdr == NULL || body == NULL) {
- wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
- return NULL;
- }
-
- xml_node_for_each_child(ctx->xml, child, hdr) {
- xml_node_for_each_check(ctx->xml, child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "SyncHdr %s", name);
- if (os_strcasecmp(name, "RespURI") == 0) {
- tmp = xml_node_get_text(ctx->xml, child);
- if (tmp)
- resp_uri = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- } else if (os_strcasecmp(name, "MsgID") == 0) {
- tmp = xml_node_get_text(ctx->xml, child);
- if (tmp)
- server_msgid = atoi(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- }
- }
-
- wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
- if (resp_uri)
- wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
-
- syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
- if (syncml == NULL) {
- os_free(resp_uri);
- return NULL;
- }
-
- syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
- cmdid++;
- add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
- DM_RESP_AUTH_ACCEPTED, NULL);
-
- xml_node_for_each_child(ctx->xml, child, body) {
- xml_node_for_each_check(ctx->xml, child);
- server_cmdid = oma_dm_get_cmdid(ctx, child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
- server_cmdid, name);
- if (os_strcasecmp(name, "Exec") == 0) {
- int res = oma_dm_exec(ctx, child);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Add") == 0) {
- int res = oma_dm_add(ctx, child, pps, pps_fname);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Replace") == 0) {
- int res;
- res = oma_dm_replace(ctx, child, pps, pps_fname);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- os_free(locuri);
- resp_needed = 1;
- } else if (os_strcasecmp(name, "Status") == 0) {
- /* TODO: Verify success */
- } else if (os_strcasecmp(name, "Get") == 0) {
- int res;
- char *value;
- res = oma_dm_get(ctx, child, pps, pps_fname, &value);
- cmdid++;
- locuri = oma_dm_get_target_locuri(ctx, child);
- if (locuri == NULL)
- res = DM_RESP_BAD_REQUEST;
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, res, locuri);
- if (res == DM_RESP_OK && value) {
- cmdid++;
- add_results(ctx, syncbody, server_msgid,
- server_cmdid, cmdid, locuri, value);
- }
- os_free(locuri);
- xml_node_get_text_free(ctx->xml, value);
- resp_needed = 1;
-#if 0 /* TODO: MUST support */
- } else if (os_strcasecmp(name, "Delete") == 0) {
-#endif
-#if 0 /* TODO: MUST support */
- } else if (os_strcasecmp(name, "Sequence") == 0) {
-#endif
- } else if (os_strcasecmp(name, "Final") == 0) {
- final = 1;
- break;
- } else {
- locuri = oma_dm_get_target_locuri(ctx, child);
- add_status(ctx, syncbody, server_msgid, server_cmdid,
- cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
- locuri);
- os_free(locuri);
- resp_needed = 1;
- }
- }
-
- if (!final) {
- wpa_printf(MSG_INFO, "Final node not found");
- xml_node_free(ctx->xml, syncml);
- os_free(resp_uri);
- return NULL;
- }
-
- if (!resp_needed) {
- wpa_printf(MSG_INFO, "Exchange completed - no response needed");
- xml_node_free(ctx->xml, syncml);
- os_free(resp_uri);
- return NULL;
- }
-
- xml_node_create(ctx->xml, syncbody, NULL, "Final");
-
- debug_dump_node(ctx, "OMA-DM Package 3", syncml);
-
- *ret_resp_uri = resp_uri;
- return syncml;
-}
-
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
- write_summary(ctx, "OMA-DM credential provisioning");
-
- msgid++;
- syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
- if (syncml == NULL)
- return -1;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
- syncml, NULL, NULL, NULL, NULL, NULL);
- if (resp == NULL)
- return -1;
-
- msgid++;
- syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
- NULL, NULL);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
- ctx->no_reconnect = 2;
-
- wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
- write_summary(ctx, "Wait for IP address before starting SIM provisioning");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
- write_summary(ctx, "OMA-DM SIM provisioning");
-
- msgid++;
- syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
- if (syncml == NULL)
- return -1;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
- syncml, NULL, NULL, NULL, NULL, NULL);
- if (resp == NULL)
- return -1;
-
- msgid++;
- syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
- NULL, NULL);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- if (ctx->pps_cred_set) {
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, ctx->pps_fname);
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- write_summary(ctx, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- return -1;
- }
- }
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- wpa_printf(MSG_INFO, "OMA-DM policy update");
- write_summary(ctx, "OMA-DM policy update");
-
- msgid++;
- syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
- if (syncml == NULL)
- return;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
- syncml, NULL, cred_username,
- cred_password, client_cert, client_key);
- if (resp == NULL)
- return;
-
- msgid++;
- syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
- pps, pps_fname);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- if (ctx->pps_updated) {
- wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
- write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
- cmd_set_pps(ctx, pps_fname);
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO,
- "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx,
- "Failed to request wpa_supplicant to reconnect");
- }
- }
-}
-
-
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- xml_node_t *syncml, *resp;
- char *resp_uri = NULL;
- int msgid = 0;
-
- wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
- write_summary(ctx, "OMA-DM subscription remediation");
-
- msgid++;
- syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
- if (syncml == NULL)
- return;
-
- while (syncml) {
- resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
- syncml, NULL, cred_username,
- cred_password, client_cert, client_key);
- if (resp == NULL)
- return;
-
- msgid++;
- syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
- pps, pps_fname);
- xml_node_free(ctx->xml, resp);
- }
-
- os_free(resp_uri);
-
- wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
- write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
- cmd_set_pps(ctx, pps_fname);
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- }
-}
-
-
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *add_fname)
-{
- xml_node_t *pps, *add;
- int res;
-
- ctx->fqdn = os_strdup("wi-fi.org");
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
- pps_fname);
- return;
- }
-
- add = node_from_file(ctx->xml, add_fname);
- if (add == NULL) {
- wpa_printf(MSG_INFO, "Add file %s could not be parsed",
- add_fname);
- xml_node_free(ctx->xml, pps);
- return;
- }
-
- res = oma_dm_add(ctx, add, pps, pps_fname);
- wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
-
- xml_node_free(ctx->xml, pps);
- xml_node_free(ctx->xml, add);
-}
-
-
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *replace_fname)
-{
- xml_node_t *pps, *replace;
- int res;
-
- ctx->fqdn = os_strdup("wi-fi.org");
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
- pps_fname);
- return;
- }
-
- replace = node_from_file(ctx->xml, replace_fname);
- if (replace == NULL) {
- wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
- replace_fname);
- xml_node_free(ctx->xml, pps);
- return;
- }
-
- res = oma_dm_replace(ctx, replace, pps, pps_fname);
- wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
-
- xml_node_free(ctx->xml, pps);
- xml_node_free(ctx->xml, replace);
-}
diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c
index 2ca85f96ce3f..f679df4b18bf 100644
--- a/contrib/wpa/hs20/client/osu_client.c
+++ b/contrib/wpa/hs20/client/osu_client.c
@@ -9,9 +9,6 @@
#include "includes.h"
#include <time.h>
#include <sys/stat.h>
-#ifdef ANDROID
-#include "private/android_filesystem_config.h"
-#endif /* ANDROID */
#include "common.h"
#include "utils/browser.h"
@@ -25,10 +22,9 @@
#include "crypto/sha256.h"
#include "osu_client.h"
-const char *spp_xsd_fname = "spp.xsd";
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...);
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
{
va_list ap;
FILE *f;
@@ -54,7 +50,7 @@ void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
}
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
{
va_list ap;
FILE *f;
@@ -74,231 +70,6 @@ void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
}
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
- xml_node_t *node)
-{
- char *str = xml_node_to_str(ctx->xml, node);
- wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
- free(str);
-}
-
-
-static int valid_fqdn(const char *fqdn)
-{
- const char *pos;
-
- /* TODO: could make this more complete.. */
- if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
- return 0;
- for (pos = fqdn; *pos; pos++) {
- if (*pos >= 'a' && *pos <= 'z')
- continue;
- if (*pos >= 'A' && *pos <= 'Z')
- continue;
- if (*pos >= '0' && *pos <= '9')
- continue;
- if (*pos == '-' || *pos == '.' || *pos == '_')
- continue;
- return 0;
- }
- return 1;
-}
-
-
-static int android_update_permission(const char *path, mode_t mode)
-{
-#ifdef ANDROID
- /* we need to change file/folder permission for Android */
-
- if (!path) {
- wpa_printf(MSG_ERROR, "file path null");
- return -1;
- }
-
- /* Allow processes running with Group ID as AID_WIFI,
- * to read files from SP, SP/<fqdn>, Cert and osu-info directories */
- if (lchown(path, -1, AID_WIFI)) {
- wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s",
- strerror(errno));
- return -1;
- }
-
- if (chmod(path, mode) < 0) {
- wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
- strerror(errno));
- return -1;
- }
-#endif /* ANDROID */
-
- return 0;
-}
-
-
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
-{
- xml_node_t *node;
- char *url, *user = NULL, *pw = NULL;
- char *proto;
- int ret = -1;
-
- proto = xml_node_get_attr_value(ctx->xml, getcert,
- "enrollmentProtocol");
- if (!proto)
- return -1;
- wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
- write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
- if (os_strcasecmp(proto, "EST") != 0) {
- wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
- xml_node_get_attr_value_free(ctx->xml, proto);
- return -1;
- }
- xml_node_get_attr_value_free(ctx->xml, proto);
-
- node = get_node(ctx->xml, getcert, "enrollmentServerURI");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
- xml_node_get_attr_value_free(ctx->xml, proto);
- return -1;
- }
- url = xml_node_get_text(ctx->xml, node);
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Could not get URL text");
- return -1;
- }
- wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
- write_summary(ctx, "enrollmentServerURI: %s", url);
-
- node = get_node(ctx->xml, getcert, "estUserID");
- if (node == NULL && !ctx->client_cert_present) {
- wpa_printf(MSG_INFO, "Could not find estUserID node");
- goto fail;
- }
- if (node) {
- user = xml_node_get_text(ctx->xml, node);
- if (user == NULL) {
- wpa_printf(MSG_INFO, "Could not get estUserID text");
- goto fail;
- }
- wpa_printf(MSG_INFO, "estUserID: %s", user);
- write_summary(ctx, "estUserID: %s", user);
- }
-
- node = get_node(ctx->xml, getcert, "estPassword");
- if (node == NULL && !ctx->client_cert_present) {
- wpa_printf(MSG_INFO, "Could not find estPassword node");
- goto fail;
- }
- if (node) {
- pw = xml_node_get_base64_text(ctx->xml, node, NULL);
- if (pw == NULL) {
- wpa_printf(MSG_INFO, "Could not get estPassword text");
- goto fail;
- }
- wpa_printf(MSG_INFO, "estPassword: %s", pw);
- }
-
- mkdir("Cert", S_IRWXU);
- android_update_permission("Cert", S_IRWXU | S_IRWXG);
-
- if (est_load_cacerts(ctx, url) < 0 ||
- est_build_csr(ctx, url) < 0 ||
- est_simple_enroll(ctx, url, user, pw) < 0)
- goto fail;
-
- ret = 0;
-fail:
- xml_node_get_text_free(ctx->xml, url);
- xml_node_get_text_free(ctx->xml, user);
- xml_node_get_text_free(ctx->xml, pw);
-
- return ret;
-}
-
-
-static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
- const char *fqdn)
-{
- u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
- char *der, *pem;
- size_t der_len, pem_len;
- char *fingerprint;
- char buf[200];
-
- wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
-
- fingerprint = xml_node_get_text(ctx->xml, cert);
- if (fingerprint == NULL)
- return -1;
- if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
- wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
- write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
- xml_node_get_text_free(ctx->xml, fingerprint);
- return -1;
- }
- xml_node_get_text_free(ctx->xml, fingerprint);
-
- der = os_readfile("Cert/est_cert.der", &der_len);
- if (der == NULL) {
- wpa_printf(MSG_INFO, "Could not find client certificate from EST");
- write_result(ctx, "Could not find client certificate from EST");
- return -1;
- }
-
- if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
- os_free(der);
- return -1;
- }
- os_free(der);
-
- if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
- wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
- write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
- unlink("Cert/est_cert.der");
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
- if (rename("Cert/est-cacerts.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
- strerror(errno));
- return -1;
- }
- pem = os_readfile(buf, &pem_len);
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
- if (rename("Cert/est_cert.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
- strerror(errno));
- os_free(pem);
- return -1;
- }
-
- if (pem) {
- FILE *f = fopen(buf, "a");
- if (f) {
- fwrite(pem, pem_len, 1, f);
- fclose(f);
- }
- os_free(pem);
- }
-
- os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
- if (rename("Cert/privkey-plain.pem", buf) < 0) {
- wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
- strerror(errno));
- return -1;
- }
-
- unlink("Cert/est-req.b64");
- unlink("Cert/est-req.pem");
- rmdir("Cert");
-
- return 0;
-}
-
-
#define TMP_CERT_DL_FILE "tmp-cert-download"
static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
@@ -337,12 +108,10 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
xml_node_get_text_free(ctx->xml, hash);
write_summary(ctx, "Download certificate from %s", url);
- ctx->no_osu_cert_validation = 1;
http_ocsp_set(ctx->http, 1);
res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
http_ocsp_set(ctx->http,
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
- ctx->no_osu_cert_validation = 0;
xml_node_get_text_free(ctx->xml, url);
if (res < 0)
return -1;
@@ -392,60 +161,6 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
}
-static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *ca_fname)
-{
- xml_node_t *pps, *node;
- int ret;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return -1;
- }
-
- node = get_child_node(ctx->xml, pps,
- "SubscriptionUpdate/TrustRoot");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
- xml_node_free(ctx->xml, pps);
- return -1;
- }
-
- ret = download_cert(ctx, node, ca_fname);
- xml_node_free(ctx->xml, pps);
-
- return ret;
-}
-
-
-static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *ca_fname)
-{
- xml_node_t *pps, *node;
- int ret;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
- return -1;
- }
-
- node = get_child_node(ctx->xml, pps,
- "Policy/PolicyUpdate/TrustRoot");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
- xml_node_free(ctx->xml, pps);
- return -2;
- }
-
- ret = download_cert(ctx, node, ca_fname);
- xml_node_free(ctx->xml, pps);
-
- return ret;
-}
-
-
static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
const char *ca_fname)
{
@@ -480,298 +195,6 @@ static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
}
-static int download_trust_roots(struct hs20_osu_client *ctx,
- const char *pps_fname)
-{
- char *dir, *pos;
- char fname[300];
- int ret, ret1;
-
- dir = os_strdup(pps_fname);
- if (dir == NULL)
- return -1;
- pos = os_strrchr(dir, '/');
- if (pos == NULL) {
- os_free(dir);
- return -1;
- }
- *pos = '\0';
-
- snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
- ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
- snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
- ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
- if (ret == 0 && ret1 == -1)
- ret = -1;
- snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
- ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
- if (ret == 0 && ret1 == -1)
- ret = -1;
-
- os_free(dir);
-
- return ret;
-}
-
-
-static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
- const char *fqdn)
-{
- size_t match_len, len, i;
- const char *val;
-
- match_len = os_strlen(fqdn);
-
- for (i = 0; i < ctx->server_dnsname_count; i++) {
- wpa_printf(MSG_INFO,
- "Checking suffix match against server dNSName %s",
- ctx->server_dnsname[i]);
- val = ctx->server_dnsname[i];
- len = os_strlen(val);
-
- if (match_len > len)
- continue;
-
- if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
- continue; /* no match */
-
- if (match_len == len)
- return 1; /* exact match */
-
- if (val[len - match_len - 1] == '.')
- return 1; /* full label match completes suffix match */
-
- /* Reject due to incomplete label match */
- }
-
- /* None of the dNSName(s) matched */
- return 0;
-}
-
-
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
- xml_node_t *add_mo, char *fname, size_t fname_len)
-{
- char *str;
- char *fqdn, *pos;
- xml_node_t *tnds, *mo, *cert;
- const char *name;
- int ret;
-
- if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
- uri);
- write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
- uri);
- return -1;
- }
-
- fqdn = strdup(uri + 8);
- if (fqdn == NULL)
- return -1;
- pos = strchr(fqdn, '/');
- if (pos) {
- if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
- wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
- uri);
- write_result(ctx, "Unsupported location for addMO to "
- "add PPS MO (extra directory): '%s'", uri);
- free(fqdn);
- return -1;
- }
- *pos = '\0'; /* remove trailing slash and PPS node name */
- }
- wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
-
- if (!server_dnsname_suffix_match(ctx, fqdn)) {
- wpa_printf(MSG_INFO,
- "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
- fqdn, (int) ctx->server_dnsname_count);
- write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
- fqdn);
- free(fqdn);
- return -1;
- }
-
- if (!valid_fqdn(fqdn)) {
- wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
- write_result(ctx, "Invalid FQDN '%s'", fqdn);
- free(fqdn);
- return -1;
- }
-
- mkdir("SP", S_IRWXU);
- snprintf(fname, fname_len, "SP/%s", fqdn);
- if (mkdir(fname, S_IRWXU) < 0) {
- if (errno != EEXIST) {
- int err = errno;
- wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
- fname, strerror(err));
- free(fqdn);
- return -1;
- }
- }
-
- android_update_permission("SP", S_IRWXU | S_IRWXG);
- android_update_permission(fname, S_IRWXU | S_IRWXG);
-
- snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
-
- if (os_file_exists(fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
- fname);
- write_result(ctx, "PPS file '%s' exists - reject addMO",
- fname);
- free(fqdn);
- return -2;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
-
- str = xml_node_get_text(ctx->xml, add_mo);
- if (str == NULL) {
- wpa_printf(MSG_INFO, "Could not extract MO text");
- free(fqdn);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
-
- tnds = xml_node_from_buf(ctx->xml, str);
- xml_node_get_text_free(ctx->xml, str);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
- free(fqdn);
- return -1;
- }
-
- mo = tnds_to_mo(ctx->xml, tnds);
- if (mo == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
- free(fqdn);
- return -1;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", mo);
-
- name = xml_node_get_localname(ctx->xml, mo);
- if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
- wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
- name);
- free(fqdn);
- return -1;
- }
-
- cert = get_child_node(ctx->xml, mo,
- "Credential/DigitalCertificate/"
- "CertSHA256Fingerprint");
- if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
- xml_node_free(ctx->xml, mo);
- free(fqdn);
- return -1;
- }
- free(fqdn);
-
- if (node_to_file(ctx->xml, fname, mo) < 0) {
- wpa_printf(MSG_INFO, "Could not write MO to file");
- xml_node_free(ctx->xml, mo);
- return -1;
- }
- xml_node_free(ctx->xml, mo);
-
- wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
- write_summary(ctx, "A new PPS MO added as '%s'", fname);
-
- ret = download_trust_roots(ctx, fname);
- if (ret < 0) {
- wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
- write_summary(ctx, "Remove invalid PPS MO file");
- unlink(fname);
- }
-
- return ret;
-}
-
-
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
- xml_node_t *pps)
-{
- char *str;
- FILE *f;
- char backup[300];
-
- if (ctx->client_cert_present) {
- xml_node_t *cert;
- cert = get_child_node(ctx->xml, pps,
- "Credential/DigitalCertificate/"
- "CertSHA256Fingerprint");
- if (cert && os_file_exists("Cert/est_cert.der") &&
- process_est_cert(ctx, cert, ctx->fqdn) < 0) {
- wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
- return -1;
- }
- }
-
- wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
-
- str = xml_node_to_str(ctx->xml, pps);
- if (str == NULL) {
- wpa_printf(MSG_ERROR, "No node found");
- return -1;
- }
- wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
-
- snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
- rename(pps_fname, backup);
- f = fopen(pps_fname, "w");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "Could not write PPS");
- rename(backup, pps_fname);
- free(str);
- return -1;
- }
- fprintf(f, "%s\n", str);
- fclose(f);
-
- free(str);
-
- return 0;
-}
-
-
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
- const char *alt_loc, char **user, char **pw)
-{
- xml_node_t *node;
-
- node = get_child_node(ctx->xml, pps,
- "Credential/UsernamePassword/Username");
- if (node)
- *user = xml_node_get_text(ctx->xml, node);
-
- node = get_child_node(ctx->xml, pps,
- "Credential/UsernamePassword/Password");
- if (node)
- *pw = xml_node_get_base64_text(ctx->xml, node, NULL);
-
- node = get_child_node(ctx->xml, pps, alt_loc);
- if (node) {
- xml_node_t *a;
- a = get_node(ctx->xml, node, "Username");
- if (a) {
- xml_node_get_text_free(ctx->xml, *user);
- *user = xml_node_get_text(ctx->xml, a);
- wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
- }
-
- a = get_node(ctx->xml, node, "Password");
- if (a) {
- free(*pw);
- *pw = xml_node_get_base64_text(ctx->xml, a, NULL);
- wpa_printf(MSG_INFO, "Use OSU password");
- }
- }
-}
-
-
/* Remove old credentials based on HomeSP/FQDN */
static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
{
@@ -1874,14 +1297,13 @@ static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
wpa_printf(MSG_INFO, "credential localname: '%s'", name);
set_pps_credential(ctx, id, child, fqdn);
- ctx->pps_cred_set = 1;
}
xml_node_get_text_free(ctx->xml, update_identifier);
}
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+static void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
{
xml_node_t *pps;
const char *fqdn;
@@ -1988,1167 +1410,20 @@ static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
}
-struct osu_icon {
- int id;
- char lang[4];
- char mime_type[256];
- char filename[256];
-};
-
-struct osu_data {
- char bssid[20];
- char url[256];
- unsigned int methods;
- char osu_ssid[33];
- char osu_ssid2[33];
- char osu_nai[256];
- char osu_nai2[256];
- struct osu_lang_text friendly_name[MAX_OSU_VALS];
- size_t friendly_name_count;
- struct osu_lang_text serv_desc[MAX_OSU_VALS];
- size_t serv_desc_count;
- struct osu_icon icon[MAX_OSU_VALS];
- size_t icon_count;
-};
-
-
-static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
-{
- FILE *f;
- char buf[1000];
- struct osu_data *osu = NULL, *last = NULL;
- size_t osu_count = 0;
- char *pos, *end;
- int res;
-
- f = fopen(fname, "r");
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Could not open %s", fname);
- return NULL;
- }
-
- while (fgets(buf, sizeof(buf), f)) {
- pos = strchr(buf, '\n');
- if (pos)
- *pos = '\0';
-
- if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
- last = realloc(osu, (osu_count + 1) * sizeof(*osu));
- if (last == NULL)
- break;
- osu = last;
- last = &osu[osu_count++];
- memset(last, 0, sizeof(*last));
- res = os_snprintf(last->bssid, sizeof(last->bssid),
- "%s", buf + 13);
- if (os_snprintf_error(sizeof(last->bssid), res))
- break;
- continue;
- }
- if (!last)
- continue;
-
- if (strncmp(buf, "uri=", 4) == 0) {
- res = os_snprintf(last->url, sizeof(last->url),
- "%s", buf + 4);
- if (os_snprintf_error(sizeof(last->url), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "methods=", 8) == 0) {
- last->methods = strtol(buf + 8, NULL, 16);
- continue;
- }
-
- if (strncmp(buf, "osu_ssid=", 9) == 0) {
- res = os_snprintf(last->osu_ssid,
- sizeof(last->osu_ssid),
- "%s", buf + 9);
- if (os_snprintf_error(sizeof(last->osu_ssid), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "osu_ssid2=", 10) == 0) {
- res = os_snprintf(last->osu_ssid2,
- sizeof(last->osu_ssid2),
- "%s", buf + 10);
- if (os_snprintf_error(sizeof(last->osu_ssid2), res))
- break;
- continue;
- }
-
- if (os_strncmp(buf, "osu_nai=", 8) == 0) {
- res = os_snprintf(last->osu_nai, sizeof(last->osu_nai),
- "%s", buf + 8);
- if (os_snprintf_error(sizeof(last->osu_nai), res))
- break;
- continue;
- }
-
- if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
- res = os_snprintf(last->osu_nai2,
- sizeof(last->osu_nai2),
- "%s", buf + 9);
- if (os_snprintf_error(sizeof(last->osu_nai2), res))
- break;
- continue;
- }
-
- if (strncmp(buf, "friendly_name=", 14) == 0) {
- struct osu_lang_text *txt;
- if (last->friendly_name_count == MAX_OSU_VALS)
- continue;
- pos = strchr(buf + 14, ':');
- if (pos == NULL)
- continue;
- *pos++ = '\0';
- txt = &last->friendly_name[last->friendly_name_count++];
- res = os_snprintf(txt->lang, sizeof(txt->lang),
- "%s", buf + 14);
- if (os_snprintf_error(sizeof(txt->lang), res))
- break;
- res = os_snprintf(txt->text, sizeof(txt->text),
- "%s", pos);
- if (os_snprintf_error(sizeof(txt->text), res))
- break;
- }
-
- if (strncmp(buf, "desc=", 5) == 0) {
- struct osu_lang_text *txt;
- if (last->serv_desc_count == MAX_OSU_VALS)
- continue;
- pos = strchr(buf + 5, ':');
- if (pos == NULL)
- continue;
- *pos++ = '\0';
- txt = &last->serv_desc[last->serv_desc_count++];
- res = os_snprintf(txt->lang, sizeof(txt->lang),
- "%s", buf + 5);
- if (os_snprintf_error(sizeof(txt->lang), res))
- break;
- res = os_snprintf(txt->text, sizeof(txt->text),
- "%s", pos);
- if (os_snprintf_error(sizeof(txt->text), res))
- break;
- }
-
- if (strncmp(buf, "icon=", 5) == 0) {
- struct osu_icon *icon;
- if (last->icon_count == MAX_OSU_VALS)
- continue;
- icon = &last->icon[last->icon_count++];
- icon->id = atoi(buf + 5);
- pos = strchr(buf, ':');
- if (pos == NULL)
- continue;
- pos = strchr(pos + 1, ':');
- if (pos == NULL)
- continue;
- pos = strchr(pos + 1, ':');
- if (pos == NULL)
- continue;
- pos++;
- end = strchr(pos, ':');
- if (!end)
- continue;
- *end = '\0';
- res = os_snprintf(icon->lang, sizeof(icon->lang),
- "%s", pos);
- if (os_snprintf_error(sizeof(icon->lang), res))
- break;
- pos = end + 1;
-
- end = strchr(pos, ':');
- if (end)
- *end = '\0';
- res = os_snprintf(icon->mime_type,
- sizeof(icon->mime_type), "%s", pos);
- if (os_snprintf_error(sizeof(icon->mime_type), res))
- break;
- if (!end)
- continue;
- pos = end + 1;
-
- end = strchr(pos, ':');
- if (end)
- *end = '\0';
- res = os_snprintf(icon->filename,
- sizeof(icon->filename), "%s", pos);
- if (os_snprintf_error(sizeof(icon->filename), res))
- break;
- continue;
- }
- }
-
- fclose(f);
-
- *count = osu_count;
- return osu;
-}
-
-
-static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
- const char *ssid, const char *ssid2, const char *url,
- unsigned int methods, int no_prod_assoc,
- const char *osu_nai, const char *osu_nai2)
-{
- int id;
- const char *ifname = ctx->ifname;
- char buf[200];
- struct wpa_ctrl *mon;
- int res;
-
- if (ssid2 && ssid2[0] == '\0')
- ssid2 = NULL;
-
- if (ctx->osu_ssid) {
- if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
- wpa_printf(MSG_DEBUG,
- "Enforced OSU SSID matches ANQP info");
- ssid2 = NULL;
- } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
- wpa_printf(MSG_DEBUG,
- "Enforced OSU SSID matches RSN[OSEN] info");
- ssid = ssid2;
- } else {
- wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
- write_summary(ctx, "Enforced OSU SSID did not match");
- return -1;
- }
- }
-
- id = add_network(ifname);
- if (id < 0)
- return -1;
- if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
- return -1;
- if (ssid2)
- osu_nai = osu_nai2;
- if (osu_nai && os_strlen(osu_nai) > 0) {
- char dir[255], fname[300];
- if (getcwd(dir, sizeof(dir)) == NULL)
- return -1;
- os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
-
- if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
- return -1;
-
- if (set_network(ifname, id, "proto", "OSEN") < 0 ||
- set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
- set_network(ifname, id, "pairwise", "CCMP") < 0 ||
- set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 ||
- set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
- set_network(ifname, id, "ocsp", "2") < 0 ||
- set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
- set_network_quoted(ifname, id, "ca_cert", fname) < 0)
- return -1;
- } else if (ssid2) {
- wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
- write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
- return -1;
- } else {
- if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
- return -1;
- }
-
- mon = open_wpa_mon(ifname);
- if (mon == NULL)
- return -1;
-
- wpa_printf(MSG_INFO, "Associate with OSU SSID");
- write_summary(ctx, "Associate with OSU SSID");
- snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
- if (wpa_command(ifname, buf) < 0)
- return -1;
-
- res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
- buf, sizeof(buf));
-
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "Could not connect to OSU network");
- write_summary(ctx, "Could not connect to OSU network");
- wpa_printf(MSG_INFO, "Remove OSU network connection");
- snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
- wpa_command(ifname, buf);
- return -1;
- }
-
- write_summary(ctx, "Waiting for IP address for subscription registration");
- if (wait_ip_addr(ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
-
- if (no_prod_assoc) {
- if (res < 0)
- return -1;
- wpa_printf(MSG_INFO, "No production connection used for testing purposes");
- write_summary(ctx, "No production connection used for testing purposes");
- return 0;
- }
-
- ctx->no_reconnect = 1;
- if (methods & 0x02) {
- wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
- res = cmd_prov(ctx, url);
- } else if (methods & 0x01) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_oma_dm_prov from osu_connect");
- res = cmd_oma_dm_prov(ctx, url);
- }
-
- wpa_printf(MSG_INFO, "Remove OSU network connection");
- write_summary(ctx, "Remove OSU network connection");
- snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
- wpa_command(ifname, buf);
-
- if (res < 0)
- return -1;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- write_summary(ctx, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
- wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
- write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
- return -1;
- }
-
- return 0;
-}
-
-
-static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
- int connect, int no_prod_assoc,
- const char *friendly_name)
-{
- char fname[255];
- FILE *f;
- struct osu_data *osu = NULL, *last = NULL;
- size_t osu_count = 0, i, j;
- int ret;
-
- write_summary(ctx, "OSU provider selection");
-
- if (dir == NULL) {
- wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
- return -1;
- }
-
- snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
- osu = parse_osu_providers(fname, &osu_count);
- if (osu == NULL) {
- wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
- fname);
- write_result(ctx, "No OSU providers available");
- return -1;
- }
-
- if (friendly_name) {
- for (i = 0; i < osu_count; i++) {
- last = &osu[i];
- for (j = 0; j < last->friendly_name_count; j++) {
- if (os_strcmp(last->friendly_name[j].text,
- friendly_name) == 0)
- break;
- }
- if (j < last->friendly_name_count)
- break;
- }
- if (i == osu_count) {
- wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
- friendly_name);
- write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
- friendly_name);
- free(osu);
- return -1;
- }
-
- wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
- friendly_name);
- write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
- friendly_name);
- ret = i + 1;
- goto selected;
- }
-
- snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
- f = fopen(fname, "w");
- if (f == NULL) {
- wpa_printf(MSG_INFO, "Could not open %s", fname);
- free(osu);
- return -1;
- }
-
- fprintf(f, "<html><head>"
- "<meta http-equiv=\"Content-type\" content=\"text/html; "
- "charset=utf-8\"<title>Select service operator</title>"
- "</head><body><h1>Select service operator</h1>\n");
-
- if (osu_count == 0)
- fprintf(f, "No online signup available\n");
-
- for (i = 0; i < osu_count; i++) {
- last = &osu[i];
-#ifdef ANDROID
- fprintf(f, "<p>\n"
- "<a href=\"http://localhost:12345/osu/%d\">"
- "<table><tr><td>", (int) i + 1);
-#else /* ANDROID */
- fprintf(f, "<p>\n"
- "<a href=\"osu://%d\">"
- "<table><tr><td>", (int) i + 1);
-#endif /* ANDROID */
- for (j = 0; j < last->icon_count; j++) {
- fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
- last->icon[j].id,
- strcasecmp(last->icon[j].mime_type,
- "image/png") == 0 ? "png" : "icon");
- }
- fprintf(f, "<td>");
- for (j = 0; j < last->friendly_name_count; j++) {
- fprintf(f, "<small>[%s]</small> %s<br>\n",
- last->friendly_name[j].lang,
- last->friendly_name[j].text);
- }
- fprintf(f, "<tr><td colspan=2>");
- for (j = 0; j < last->serv_desc_count; j++) {
- fprintf(f, "<small>[%s]</small> %s<br>\n",
- last->serv_desc[j].lang,
- last->serv_desc[j].text);
- }
- fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
- "SSID: %s<br>\n",
- last->bssid, last->osu_ssid);
- if (last->osu_ssid2[0])
- fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2);
- if (last->osu_nai[0])
- fprintf(f, "NAI: %s<br>\n", last->osu_nai);
- if (last->osu_nai2[0])
- fprintf(f, "NAI2: %s<br>\n", last->osu_nai2);
- fprintf(f, "URL: %s<br>\n"
- "methods:%s%s<br>\n"
- "</small></p>\n",
- last->url,
- last->methods & 0x01 ? " OMA-DM" : "",
- last->methods & 0x02 ? " SOAP-XML-SPP" : "");
- }
-
- fprintf(f, "</body></html>\n");
-
- fclose(f);
-
- snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
- write_summary(ctx, "Start web browser with OSU provider selection page");
- ret = hs20_web_browser(fname, 0);
-
-selected:
- if (ret > 0 && (size_t) ret <= osu_count) {
- char *data;
- size_t data_len;
-
- wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
- last = &osu[ret - 1];
- ret = 0;
- wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
- wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
- if (last->osu_ssid2[0])
- wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
- wpa_printf(MSG_INFO, "URL: %s", last->url);
- write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
- ret, last->bssid, last->osu_ssid, last->url);
-
- ctx->friendly_name_count = last->friendly_name_count;
- for (j = 0; j < last->friendly_name_count; j++) {
- wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
- last->friendly_name[j].lang,
- last->friendly_name[j].text);
- os_strlcpy(ctx->friendly_name[j].lang,
- last->friendly_name[j].lang,
- sizeof(ctx->friendly_name[j].lang));
- os_strlcpy(ctx->friendly_name[j].text,
- last->friendly_name[j].text,
- sizeof(ctx->friendly_name[j].text));
- }
-
- ctx->icon_count = last->icon_count;
- for (j = 0; j < last->icon_count; j++) {
- char fname[256];
-
- os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
- dir, last->icon[j].id,
- strcasecmp(last->icon[j].mime_type,
- "image/png") == 0 ?
- "png" : "icon");
- wpa_printf(MSG_INFO, "ICON: %s (%s)",
- fname, last->icon[j].filename);
- os_strlcpy(ctx->icon_filename[j],
- last->icon[j].filename,
- sizeof(ctx->icon_filename[j]));
-
- data = os_readfile(fname, &data_len);
- if (data) {
- sha256_vector(1, (const u8 **) &data, &data_len,
- ctx->icon_hash[j]);
- os_free(data);
- }
- }
-
- if (connect == 2) {
- if (last->methods & 0x02) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_prov from cmd_osu_select");
- ret = cmd_prov(ctx, last->url);
- } else if (last->methods & 0x01) {
- wpa_printf(MSG_DEBUG,
- "Calling cmd_oma_dm_prov from cmd_osu_select");
- ret = cmd_oma_dm_prov(ctx, last->url);
- } else {
- wpa_printf(MSG_DEBUG,
- "No supported OSU provisioning method");
- ret = -1;
- }
- } else if (connect) {
- ret = osu_connect(ctx, last->bssid, last->osu_ssid,
- last->osu_ssid2,
- last->url, last->methods,
- no_prod_assoc, last->osu_nai,
- last->osu_nai2);
- }
- } else
- ret = -1;
-
- free(osu);
-
- return ret;
-}
-
-
-static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
- const char *friendly_name)
-{
- char dir[255];
- char fname[300], buf[400];
- struct wpa_ctrl *mon;
- const char *ifname;
- int res;
-
- ifname = ctx->ifname;
-
- if (getcwd(dir, sizeof(dir)) == NULL)
- return -1;
-
- snprintf(fname, sizeof(fname), "%s/osu-info", dir);
- if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
- errno != EEXIST) {
- wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
- fname, strerror(errno));
- return -1;
- }
-
- android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-
- snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
- if (wpa_command(ifname, buf) < 0) {
- wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
- return -1;
- }
-
- mon = open_wpa_mon(ifname);
- if (mon == NULL)
- return -1;
-
- wpa_printf(MSG_INFO, "Starting OSU fetch");
- write_summary(ctx, "Starting OSU provider information fetch");
- if (wpa_command(ifname, "FETCH_OSU") < 0) {
- wpa_printf(MSG_INFO, "Could not start OSU fetch");
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
- return -1;
- }
- res = get_wpa_cli_event(mon, "OSU provider fetch completed",
- buf, sizeof(buf));
-
- wpa_ctrl_detach(mon);
- wpa_ctrl_close(mon);
-
- if (res < 0) {
- wpa_printf(MSG_INFO, "OSU fetch did not complete");
- write_summary(ctx, "OSU fetch did not complete");
- return -1;
- }
- wpa_printf(MSG_INFO, "OSU provider fetch completed");
-
- return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
-}
-
-
-static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname, const char *ca_fname)
-{
- xml_node_t *pps, *node;
- char pps_fname_buf[300];
- char ca_fname_buf[200];
- char *cred_username = NULL;
- char *cred_password = NULL;
- char *sub_rem_uri = NULL;
- char client_cert_buf[200];
- char *client_cert = NULL;
- char client_key_buf[200];
- char *client_key = NULL;
- int spp;
-
- wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
- address);
-
- if (!pps_fname) {
- char buf[256];
- wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
- if (os_strncmp(address, "fqdn=", 5) == 0) {
- wpa_printf(MSG_INFO, "Use requested FQDN from command line");
- os_snprintf(buf, sizeof(buf), "%s", address + 5);
- address = NULL;
- } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
- sizeof(buf)) < 0) {
- wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
- return -1;
- }
- os_free(ctx->fqdn);
- ctx->fqdn = os_strdup(buf);
- if (ctx->fqdn == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
- buf);
- os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
- "SP/%s/pps.xml", ctx->fqdn);
- pps_fname = pps_fname_buf;
-
- os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
- ctx->fqdn);
- ca_fname = ca_fname_buf;
- }
-
- if (!os_file_exists(pps_fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
- pps_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
- if (ca_fname && !os_file_exists(ca_fname)) {
- wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
- ca_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
- ctx->ca_fname = ca_fname;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read PPS MO");
- return -1;
- }
-
- if (!ctx->fqdn) {
- char *tmp;
- node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
- return -1;
- }
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
- return -1;
- }
- ctx->fqdn = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- if (!ctx->fqdn) {
- wpa_printf(MSG_INFO, "No FQDN known");
- return -1;
- }
- }
-
- node = get_child_node(ctx->xml, pps,
- "SubscriptionUpdate/UpdateMethod");
- if (node) {
- char *tmp;
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
- spp = 0;
- else
- spp = 1;
- } else {
- wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
- spp = 1;
- }
-
- get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
- &cred_username, &cred_password);
- if (cred_username)
- wpa_printf(MSG_INFO, "Using username: %s", cred_username);
- if (cred_password)
- wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
- if (cred_username == NULL && cred_password == NULL &&
- get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
- wpa_printf(MSG_INFO, "Using client certificate");
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- ctx->client_cert_present = 1;
- }
-
- node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
- if (node) {
- sub_rem_uri = xml_node_get_text(ctx->xml, node);
- if (sub_rem_uri &&
- (!address || os_strcmp(address, sub_rem_uri) != 0)) {
- wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
- sub_rem_uri);
- address = sub_rem_uri;
- }
- }
- if (!address) {
- wpa_printf(MSG_INFO, "Server URL not known");
- return -1;
- }
-
- write_summary(ctx, "Wait for IP address for subscriptiom remediation");
- wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
-
- if (spp)
- spp_sub_rem(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
- else
- oma_dm_sub_rem(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
-
- xml_node_get_text_free(ctx->xml, sub_rem_uri);
- xml_node_get_text_free(ctx->xml, cred_username);
- str_clear_free(cred_password);
- xml_node_free(ctx->xml, pps);
- return 0;
-}
-
-
-static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname, const char *ca_fname)
-{
- xml_node_t *pps;
- xml_node_t *node;
- char pps_fname_buf[300];
- char ca_fname_buf[200];
- char *uri = NULL;
- char *cred_username = NULL;
- char *cred_password = NULL;
- char client_cert_buf[200];
- char *client_cert = NULL;
- char client_key_buf[200];
- char *client_key = NULL;
- int spp;
-
- wpa_printf(MSG_INFO, "Policy update requested");
-
- if (!pps_fname) {
- char buf[256];
- int res;
-
- wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
- if (address && os_strncmp(address, "fqdn=", 5) == 0) {
- wpa_printf(MSG_INFO, "Use requested FQDN from command line");
- os_snprintf(buf, sizeof(buf), "%s", address + 5);
- address = NULL;
- } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
- sizeof(buf)) < 0) {
- wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
- return -1;
- }
- os_free(ctx->fqdn);
- ctx->fqdn = os_strdup(buf);
- if (ctx->fqdn == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
- buf);
- os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
- "SP/%s/pps.xml", ctx->fqdn);
- pps_fname = pps_fname_buf;
-
- res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf),
- "SP/%s/ca.pem", buf);
- if (os_snprintf_error(sizeof(ca_fname_buf), res)) {
- os_free(ctx->fqdn);
- ctx->fqdn = NULL;
- return -1;
- }
- ca_fname = ca_fname_buf;
- }
-
- if (!os_file_exists(pps_fname)) {
- wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
- pps_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
-
- if (ca_fname && !os_file_exists(ca_fname)) {
- wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
- ca_fname);
- return -1;
- }
- wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
- ctx->ca_fname = ca_fname;
-
- pps = node_from_file(ctx->xml, pps_fname);
- if (pps == NULL) {
- wpa_printf(MSG_INFO, "Could not read PPS MO");
- return -1;
- }
-
- if (!ctx->fqdn) {
- char *tmp;
- node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
- if (node == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
- return -1;
- }
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp == NULL) {
- wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
- return -1;
- }
- ctx->fqdn = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- if (!ctx->fqdn) {
- wpa_printf(MSG_INFO, "No FQDN known");
- return -1;
- }
- }
-
- node = get_child_node(ctx->xml, pps,
- "Policy/PolicyUpdate/UpdateMethod");
- if (node) {
- char *tmp;
- tmp = xml_node_get_text(ctx->xml, node);
- if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
- spp = 0;
- else
- spp = 1;
- } else {
- wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
- spp = 1;
- }
-
- get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
- &cred_username, &cred_password);
- if (cred_username)
- wpa_printf(MSG_INFO, "Using username: %s", cred_username);
- if (cred_password)
- wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
-
- if (cred_username == NULL && cred_password == NULL &&
- get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
- wpa_printf(MSG_INFO, "Using client certificate");
- os_snprintf(client_cert_buf, sizeof(client_cert_buf),
- "SP/%s/client-cert.pem", ctx->fqdn);
- client_cert = client_cert_buf;
- os_snprintf(client_key_buf, sizeof(client_key_buf),
- "SP/%s/client-key.pem", ctx->fqdn);
- client_key = client_key_buf;
- }
-
- if (!address) {
- node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
- if (node) {
- uri = xml_node_get_text(ctx->xml, node);
- wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
- address = uri;
- }
- }
- if (!address) {
- wpa_printf(MSG_INFO, "Server URL not known");
- return -1;
- }
-
- if (spp)
- spp_pol_upd(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
- else
- oma_dm_pol_upd(ctx, address, pps_fname,
- client_cert, client_key,
- cred_username, cred_password, pps);
-
- xml_node_get_text_free(ctx->xml, uri);
- xml_node_get_text_free(ctx->xml, cred_username);
- str_clear_free(cred_password);
- xml_node_free(ctx->xml, pps);
-
- return 0;
-}
-
-
-static char * get_hostname(const char *url)
-{
- const char *pos, *end, *end2;
- char *ret;
-
- if (url == NULL)
- return NULL;
-
- pos = os_strchr(url, '/');
- if (pos == NULL)
- return NULL;
- pos++;
- if (*pos != '/')
- return NULL;
- pos++;
-
- end = os_strchr(pos, '/');
- end2 = os_strchr(pos, ':');
- if ((end && end2 && end2 < end) || (!end && end2))
- end = end2;
- if (end)
- end--;
- else {
- end = pos;
- while (*end)
- end++;
- if (end > pos)
- end--;
- }
-
- ret = os_malloc(end - pos + 2);
- if (ret == NULL)
- return NULL;
-
- os_memcpy(ret, pos, end - pos + 1);
- ret[end - pos + 1] = '\0';
-
- return ret;
-}
-
-
-static int osu_cert_cb(void *_ctx, struct http_cert *cert)
-{
- struct hs20_osu_client *ctx = _ctx;
- size_t i, j;
- int found;
- char *host = NULL;
-
- wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)",
- !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A",
- ctx->server_url);
-
- if (ctx->no_osu_cert_validation && cert->url)
- host = get_hostname(cert->url);
- else
- host = get_hostname(ctx->server_url);
-
- if (!ctx->no_osu_cert_validation) {
- for (i = 0; i < ctx->server_dnsname_count; i++)
- os_free(ctx->server_dnsname[i]);
- os_free(ctx->server_dnsname);
- ctx->server_dnsname = os_calloc(cert->num_dnsname,
- sizeof(char *));
- ctx->server_dnsname_count = 0;
- }
-
- found = 0;
- for (i = 0; i < cert->num_dnsname; i++) {
- if (!ctx->no_osu_cert_validation && ctx->server_dnsname) {
- ctx->server_dnsname[ctx->server_dnsname_count] =
- os_strdup(cert->dnsname[i]);
- if (ctx->server_dnsname[ctx->server_dnsname_count])
- ctx->server_dnsname_count++;
- }
- if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
- found = 1;
- wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
- }
-
- if (host && !found) {
- wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
- host);
- write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
- host);
- os_free(host);
- return -1;
- }
-
- os_free(host);
-
- for (i = 0; i < cert->num_othername; i++) {
- if (os_strcmp(cert->othername[i].oid,
- "1.3.6.1.4.1.40808.1.1.1") == 0) {
- wpa_hexdump_ascii(MSG_INFO,
- "id-wfa-hotspot-friendlyName",
- cert->othername[i].data,
- cert->othername[i].len);
- }
- }
-
- for (j = 0; !ctx->no_osu_cert_validation &&
- j < ctx->friendly_name_count; j++) {
- int found = 0;
- for (i = 0; i < cert->num_othername; i++) {
- if (os_strcmp(cert->othername[i].oid,
- "1.3.6.1.4.1.40808.1.1.1") != 0)
- continue;
- if (cert->othername[i].len < 3)
- continue;
- if (os_strncasecmp((char *) cert->othername[i].data,
- ctx->friendly_name[j].lang, 3) != 0)
- continue;
- if (os_strncmp((char *) cert->othername[i].data + 3,
- ctx->friendly_name[j].text,
- cert->othername[i].len - 3) == 0) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
- wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
- ctx->friendly_name[j].lang,
- ctx->friendly_name[j].text);
- write_result(ctx, "No friendly name match found for '[%s]%s'",
- ctx->friendly_name[j].lang,
- ctx->friendly_name[j].text);
- return -1;
- }
- }
-
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
-
- wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
- logo->alg_oid, logo->uri);
- wpa_hexdump_ascii(MSG_INFO, "hashValue",
- logo->hash, logo->hash_len);
- }
-
- for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
- int found = 0;
- char *name = ctx->icon_filename[j];
- size_t name_len = os_strlen(name);
-
- wpa_printf(MSG_INFO,
- "[%zu] Looking for icon file name '%s' match",
- j, name);
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
- size_t uri_len = os_strlen(logo->uri);
- char *pos;
-
- wpa_printf(MSG_INFO,
- "[%zu] Comparing to '%s' uri_len=%d name_len=%d",
- i, logo->uri, (int) uri_len, (int) name_len);
- if (uri_len < 1 + name_len) {
- wpa_printf(MSG_INFO, "URI Length is too short");
- continue;
- }
- pos = &logo->uri[uri_len - name_len - 1];
- if (*pos != '/')
- continue;
- pos++;
- if (os_strcmp(pos, name) == 0) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
- wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
- name);
- write_result(ctx,
- "No icon filename match found for '%s'",
- name);
- return -1;
- }
- }
-
- for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
- int found = 0;
-
- for (i = 0; i < cert->num_logo; i++) {
- struct http_logo *logo = &cert->logo[i];
-
- if (logo->hash_len != 32) {
- wpa_printf(MSG_INFO,
- "[%zu][%zu] Icon hash length invalid (should be 32): %d",
- j, i, (int) logo->hash_len);
- continue;
- }
- if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
- found = 1;
- break;
- }
-
- wpa_printf(MSG_DEBUG,
- "[%zu][%zu] Icon hash did not match", j, i);
- wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
- logo->hash, 32);
- wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
- ctx->icon_hash[j], 32);
- }
-
- if (!found) {
- wpa_printf(MSG_INFO,
- "No icon hash match (by hash) found");
- write_result(ctx,
- "No icon hash match (by hash) found");
- return -1;
- }
- }
-
- return 0;
-}
-
-
static int init_ctx(struct hs20_osu_client *ctx)
{
- xml_node_t *devinfo, *devid;
-
os_memset(ctx, 0, sizeof(*ctx));
ctx->ifname = "wlan0";
ctx->xml = xml_node_init_ctx(ctx, NULL);
if (ctx->xml == NULL)
return -1;
- devinfo = node_from_file(ctx->xml, "devinfo.xml");
- if (devinfo) {
- devid = get_node(ctx->xml, devinfo, "DevId");
- if (devid) {
- char *tmp = xml_node_get_text(ctx->xml, devid);
-
- if (tmp) {
- ctx->devid = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
- }
- }
- xml_node_free(ctx->xml, devinfo);
- }
-
ctx->http = http_init_ctx(ctx, ctx->xml);
if (ctx->http == NULL) {
xml_node_deinit_ctx(ctx->xml);
return -1;
}
http_ocsp_set(ctx->http, 2);
- http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
return 0;
}
@@ -3156,17 +1431,8 @@ static int init_ctx(struct hs20_osu_client *ctx)
static void deinit_ctx(struct hs20_osu_client *ctx)
{
- size_t i;
-
http_deinit_ctx(ctx->http);
xml_node_deinit_ctx(ctx->xml);
- os_free(ctx->fqdn);
- os_free(ctx->server_url);
- os_free(ctx->devid);
-
- for (i = 0; i < ctx->server_dnsname_count; i++)
- os_free(ctx->server_dnsname[i]);
- os_free(ctx->server_dnsname);
}
@@ -3209,19 +1475,8 @@ static void usage(void)
"- from_tnds <XML MO in TNDS format> <XML MO>\n"
"- set_pps <PerProviderSubscription XML file name>\n"
"- get_fqdn <PerProviderSubscription XML file name>\n"
- "- pol_upd [Server URL] [PPS] [CA cert]\n"
- "- sub_rem <Server URL> [PPS] [CA cert]\n"
- "- prov <Server URL> [CA cert]\n"
- "- oma_dm_prov <Server URL> [CA cert]\n"
- "- sim_prov <Server URL> [CA cert]\n"
- "- oma_dm_sim_prov <Server URL> [CA cert]\n"
- "- signup [CA cert]\n"
- "- dl_osu_ca <PPS> <CA file>\n"
- "- dl_polupd_ca <PPS> <CA file>\n"
"- dl_aaa_ca <PPS> <CA file>\n"
- "- browser <URL>\n"
- "- parse_cert <X.509 certificate (DER)>\n"
- "- osu_select <OSU info directory> [CA cert]\n");
+ "- browser <URL>\n");
}
@@ -3230,8 +1485,6 @@ int main(int argc, char *argv[])
struct hs20_osu_client ctx;
int c;
int ret = 0;
- int no_prod_assoc = 0;
- const char *friendly_name = NULL;
const char *wpa_debug_file_path = NULL;
extern char *wpas_ctrl_path;
extern int wpa_debug_level;
@@ -3242,7 +1495,7 @@ int main(int argc, char *argv[])
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:");
+ c = getopt(argc, argv, "df:hKqr:s:S:tTw:");
if (c < 0)
break;
switch (c) {
@@ -3256,15 +1509,6 @@ int main(int argc, char *argv[])
case 'K':
wpa_debug_show_keys++;
break;
- case 'N':
- no_prod_assoc = 1;
- break;
- case 'o':
- ctx.osu_ssid = optarg;
- break;
- case 'O':
- friendly_name = optarg;
- break;
case 'q':
wpa_debug_level++;
break;
@@ -3286,9 +1530,6 @@ int main(int argc, char *argv[])
case 'w':
wpas_ctrl_path = optarg;
break;
- case 'x':
- spp_xsd_fname = optarg;
- break;
case 'h':
default:
usage();
@@ -3335,62 +1576,12 @@ int main(int argc, char *argv[])
exit(0);
}
cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "sub_rem") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ret = cmd_sub_rem(&ctx, argv[optind + 1],
- argc > optind + 2 ? argv[optind + 2] : NULL,
- argc > optind + 3 ? argv[optind + 3] : NULL);
- } else if (strcmp(argv[optind], "pol_upd") == 0) {
- ret = cmd_pol_upd(&ctx,
- argc > optind + 1 ? argv[optind + 1] : NULL,
- argc > optind + 2 ? argv[optind + 2] : NULL,
- argc > optind + 3 ? argv[optind + 3] : NULL);
- } else if (strcmp(argv[optind], "prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
- cmd_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "sim_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- cmd_sim_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
} else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
if (argc - optind < 2) {
usage();
exit(0);
}
cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "osu_select") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
- cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
- } else if (strcmp(argv[optind], "signup") == 0) {
- ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
- ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
} else if (strcmp(argv[optind], "set_pps") == 0) {
if (argc - optind < 2) {
usage();
@@ -3403,42 +1594,6 @@ int main(int argc, char *argv[])
exit(0);
}
ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- cmd_oma_dm_prov(&ctx, argv[optind + 1]);
- } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- ctx.ca_fname = argv[optind + 2];
- if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
- write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
- return -1;
- }
- } else if (strcmp(argv[optind], "oma_dm_add") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
- } else if (strcmp(argv[optind], "est_csr") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
- mkdir("Cert", S_IRWXU);
- est_build_csr(&ctx, argv[optind + 1]);
} else if (strcmp(argv[optind], "browser") == 0) {
int ret;
@@ -3451,15 +1606,6 @@ int main(int argc, char *argv[])
argv[optind + 1]);
ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls);
wpa_printf(MSG_INFO, "Web browser result: %d", ret);
- } else if (strcmp(argv[optind], "parse_cert") == 0) {
- if (argc - optind < 2) {
- usage();
- exit(0);
- }
-
- wpa_debug_level = MSG_MSGDUMP;
- http_parse_x509_certificate(ctx.http, argv[optind + 1]);
- wpa_debug_level = MSG_INFO;
} else {
wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
}
diff --git a/contrib/wpa/hs20/client/osu_client.h b/contrib/wpa/hs20/client/osu_client.h
index 9b45b03febe2..3bfbb0dd9e1b 100644
--- a/contrib/wpa/hs20/client/osu_client.h
+++ b/contrib/wpa/hs20/client/osu_client.h
@@ -9,113 +9,16 @@
#ifndef OSU_CLIENT_H
#define OSU_CLIENT_H
-#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
-
-#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
-#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
-#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
-#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
-
-
-#define MAX_OSU_VALS 10
-
-struct osu_lang_text {
- char lang[4];
- char text[253];
-};
-
struct hs20_osu_client {
struct xml_node_ctx *xml;
struct http_ctx *http;
- int no_reconnect;
- char pps_fname[300];
- char *devid;
const char *result_file;
const char *summary_file;
const char *ifname;
- const char *ca_fname;
- int no_osu_cert_validation; /* for EST operations */
- char *fqdn;
- char *server_url;
- struct osu_lang_text friendly_name[MAX_OSU_VALS];
- size_t friendly_name_count;
- size_t icon_count;
- char icon_filename[MAX_OSU_VALS][256];
- u8 icon_hash[MAX_OSU_VALS][32];
- int pps_cred_set;
- int pps_updated;
- int client_cert_present;
- char **server_dnsname;
- size_t server_dnsname_count;
- const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
unsigned long int workarounds;
int ignore_tls; /* whether to ignore TLS validation issues with HTTPS
* server certificate */
};
-
-/* osu_client.c */
-
-void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
-void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
-
-void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
- xml_node_t *node);
-int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
-int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
- xml_node_t *add_mo, char *fname, size_t fname_len);
-void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
- const char *alt_loc, char **user, char **pw);
-int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
- xml_node_t *pps);
-void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-/* spp_client.c */
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-int cmd_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
-
-
-/* oma_dm_client.c */
-
-int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
-int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
-void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps);
-void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname);
-void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *add_fname);
-void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
- const char *replace_fname);
-
-/* est.c */
-
-int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
-int est_build_csr(struct hs20_osu_client *ctx, const char *url);
-int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
- const char *user, const char *pw);
-
#endif /* OSU_CLIENT_H */
diff --git a/contrib/wpa/hs20/client/spp_client.c b/contrib/wpa/hs20/client/spp_client.c
deleted file mode 100644
index 194518e5aba7..000000000000
--- a/contrib/wpa/hs20/client/spp_client.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * Hotspot 2.0 SPP client
- * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <sys/stat.h>
-
-#include "common.h"
-#include "browser.h"
-#include "wpa_ctrl.h"
-#include "wpa_helpers.h"
-#include "xml-utils.h"
-#include "http-utils.h"
-#include "utils/base64.h"
-#include "crypto/crypto.h"
-#include "crypto/sha256.h"
-#include "osu_client.h"
-
-
-extern const char *spp_xsd_fname;
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code);
-static void hs20_policy_update_complete(
- struct hs20_osu_client *ctx, const char *pps_fname);
-
-
-static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
- char *attr_name)
-{
- return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
-}
-
-
-static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
- const char *expected_name)
-{
- struct xml_node_ctx *xctx = ctx->xml;
- const char *name;
- char *err;
- int ret;
-
- if (!xml_node_is_element(xctx, node))
- return -1;
-
- name = xml_node_get_localname(xctx, node);
- if (name == NULL)
- return -1;
-
- if (strcmp(expected_name, name) != 0) {
- wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
- name, expected_name);
- write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
- name, expected_name);
- return -1;
- }
-
- ret = xml_validate(xctx, node, spp_xsd_fname, &err);
- if (ret < 0) {
- wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
- write_summary(ctx, "SPP XML schema validation failed");
- os_free(err);
- }
- return ret;
-}
-
-
-static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
- xml_node_t *parent, const char *urn,
- const char *fname)
-{
- xml_node_t *node;
- xml_node_t *fnode, *tnds;
- char *str;
-
- errno = 0;
- fnode = node_from_file(ctx, fname);
- if (!fnode) {
- wpa_printf(MSG_ERROR,
- "Failed to create XML node from file: %s, possible error: %s",
- fname, strerror(errno));
- return;
- }
- tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
- xml_node_free(ctx, fnode);
- if (!tnds)
- return;
-
- str = xml_node_to_str(ctx, tnds);
- xml_node_free(ctx, tnds);
- if (str == NULL)
- return;
-
- node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
- if (node)
- xml_node_add_attr(ctx, node, ns, "moURN", urn);
- os_free(str);
-}
-
-
-static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
- xml_namespace_t **ret_ns,
- const char *session_id,
- const char *reason)
-{
- xml_namespace_t *ns;
- xml_node_t *spp_node;
-
- write_summary(ctx, "Building sppPostDevData requestReason='%s'",
- reason);
- spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
- "sppPostDevData");
- if (spp_node == NULL)
- return NULL;
- if (ret_ns)
- *ret_ns = ns;
-
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
- xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
- if (session_id)
- xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
- session_id);
- xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
- "http://localhost:12345/");
-
- xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
- "1.0");
- xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
- URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
- URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
-
- add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
- "devinfo.xml");
- add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
- "devdetail.xml");
-
- return spp_node;
-}
-
-
-static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
- xml_node_t *update)
-{
- xml_node_t *node, *parent, *tnds, *unode;
- char *str;
- const char *name;
- char *uri, *pos;
- char *cdata, *cdata_end;
- size_t fqdn_len;
-
- wpa_printf(MSG_INFO, "Processing updateNode");
- debug_dump_node(ctx, "updateNode", update);
-
- uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
- if (uri == NULL) {
- wpa_printf(MSG_INFO, "No managementTreeURI present");
- return -1;
- }
- wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
-
- name = os_strrchr(uri, '/');
- if (name == NULL) {
- wpa_printf(MSG_INFO, "Unexpected URI");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- name++;
- wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
-
- str = xml_node_get_text(ctx->xml, update);
- if (str == NULL) {
- wpa_printf(MSG_INFO, "Could not extract MO text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
- cdata = strstr(str, "<![CDATA[");
- cdata_end = strstr(str, "]]>");
- if (cdata && cdata_end && cdata_end > cdata &&
- cdata < strstr(str, "MgmtTree") &&
- cdata_end > strstr(str, "/MgmtTree")) {
- char *tmp;
- wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
- tmp = strdup(cdata + 9);
- if (tmp) {
- cdata_end = strstr(tmp, "]]>");
- if (cdata_end)
- *cdata_end = '\0';
- wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
- tmp);
- tnds = xml_node_from_buf(ctx->xml, tmp);
- free(tmp);
- } else
- tnds = NULL;
- } else
- tnds = xml_node_from_buf(ctx->xml, str);
- xml_node_get_text_free(ctx->xml, str);
- if (tnds == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- unode = tnds_to_mo(ctx->xml, tnds);
- xml_node_free(ctx->xml, tnds);
- if (unode == NULL) {
- wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- debug_dump_node(ctx, "Parsed TNDS", unode);
-
- if (get_node_uri(ctx->xml, unode, name) == NULL) {
- wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
-
- if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos = uri + 8;
-
- if (ctx->fqdn == NULL) {
- wpa_printf(MSG_INFO, "FQDN not known");
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- fqdn_len = os_strlen(ctx->fqdn);
- if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
- pos[fqdn_len] != '/') {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
- ctx->fqdn);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos += fqdn_len + 1;
-
- if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
- wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
- ctx->fqdn);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- pos += 24;
-
- wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
-
- node = get_node(ctx->xml, pps, pos);
- if (node) {
- parent = xml_node_get_parent(ctx->xml, node);
- xml_node_detach(ctx->xml, node);
- wpa_printf(MSG_INFO, "Replace '%s' node", name);
- } else {
- char *pos2;
- pos2 = os_strrchr(pos, '/');
- if (pos2 == NULL) {
- parent = pps;
- } else {
- *pos2 = '\0';
- parent = get_node(ctx->xml, pps, pos);
- }
- if (parent == NULL) {
- wpa_printf(MSG_INFO, "Could not find parent %s", pos);
- xml_node_free(ctx->xml, unode);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return -1;
- }
- wpa_printf(MSG_INFO, "Add '%s' node", name);
- }
- xml_node_add_child(ctx->xml, parent, unode);
-
- xml_node_get_attr_value_free(ctx->xml, uri);
-
- return 0;
-}
-
-
-static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
- const char *pps_fname, xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
- xml_node_for_each_sibling(ctx->xml, update) {
- xml_node_for_each_check(ctx->xml, update);
- if (process_update_node(ctx, pps, update) < 0)
- return -1;
- }
-
- return update_pps_file(ctx, pps_fname, pps);
-}
-
-
-static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
- const char *pps_fname)
-{
- /*
- * Update wpa_supplicant credentials and reconnect using updated
- * information.
- */
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, pps_fname);
-
- if (ctx->no_reconnect)
- return;
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
- xml_node_t *cmd,
- const char *session_id,
- const char *pps_fname)
-{
- xml_namespace_t *ns;
- xml_node_t *node, *ret_node;
- char *urn;
-
- urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
- if (!urn) {
- wpa_printf(MSG_INFO, "No URN included");
- return NULL;
- }
- wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
- if (strcasecmp(urn, URN_HS20_PPS) != 0) {
- wpa_printf(MSG_INFO, "Unsupported moURN");
- xml_node_get_attr_value_free(ctx->xml, urn);
- return NULL;
- }
- xml_node_get_attr_value_free(ctx->xml, urn);
-
- if (!pps_fname) {
- wpa_printf(MSG_INFO, "PPS file name no known");
- return NULL;
- }
-
- node = build_spp_post_dev_data(ctx, &ns, session_id,
- "MO upload");
- if (node == NULL)
- return NULL;
- add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
-
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
-
- debug_dump_node(ctx, "Received response to MO upload", ret_node);
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
- char *fname, size_t fname_len)
-{
- char *uri, *urn;
- int ret;
-
- debug_dump_node(ctx, "Received addMO", add_mo);
-
- urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
- if (urn == NULL) {
- wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
- return -1;
- }
- wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
- if (strcasecmp(urn, URN_HS20_PPS) != 0) {
- wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
- xml_node_get_attr_value_free(ctx->xml, urn);
- return -1;
- }
- xml_node_get_attr_value_free(ctx->xml, urn);
-
- uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
- if (uri == NULL) {
- wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
- return -1;
- }
- wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
-
- ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
- xml_node_get_attr_value_free(ctx->xml, uri);
- return ret;
-}
-
-
-static int process_spp_user_input_response(struct hs20_osu_client *ctx,
- const char *session_id,
- xml_node_t *add_mo)
-{
- int ret;
- char fname[300];
-
- debug_dump_node(ctx, "addMO", add_mo);
-
- wpa_printf(MSG_INFO, "Subscription registration completed");
-
- if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
- wpa_printf(MSG_INFO, "Could not add MO");
- ret = hs20_spp_update_response(
- ctx, session_id,
- "Error occurred",
- "MO addition or update failed");
- return 0;
- }
-
- ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
- if (ret == 0)
- hs20_sub_rem_complete(ctx, fname);
-
- return 0;
-}
-
-
-static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
- const char *session_id)
-{
- xml_node_t *node, *ret_node;
-
- node = build_spp_post_dev_data(ctx, NULL, session_id,
- "User input completed");
- if (node == NULL)
- return NULL;
-
- ret_node = soap_send_receive(ctx->http, node);
- if (!ret_node) {
- if (soap_reinit_client(ctx->http) < 0)
- return NULL;
- wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
- node = build_spp_post_dev_data(ctx, NULL, session_id,
- "User input completed");
- if (node == NULL)
- return NULL;
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
- wpa_printf(MSG_INFO, "Continue with new connection");
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
- xml_node_t *cmd,
- const char *session_id,
- const char *pps_fname)
-{
- xml_namespace_t *ns;
- xml_node_t *node, *ret_node;
- int res;
-
- wpa_printf(MSG_INFO, "Client certificate enrollment");
-
- res = osu_get_certificate(ctx, cmd);
- if (res < 0)
- wpa_printf(MSG_INFO, "EST simpleEnroll failed");
-
- node = build_spp_post_dev_data(ctx, &ns, session_id,
- res == 0 ?
- "Certificate enrollment completed" :
- "Certificate enrollment failed");
- if (node == NULL)
- return NULL;
-
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return NULL;
-
- debug_dump_node(ctx, "Received response to certificate enrollment "
- "completed", ret_node);
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return NULL;
- }
-
- return ret_node;
-}
-
-
-static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
- const char *session_id, const char *pps_fname,
- xml_node_t *pps, xml_node_t **ret_node)
-{
- xml_node_t *cmd;
- const char *name;
- char *uri;
- char *id = strdup(session_id);
-
- if (id == NULL)
- return -1;
-
- *ret_node = NULL;
-
- debug_dump_node(ctx, "exec", exec);
-
- xml_node_for_each_child(ctx->xml, cmd, exec) {
- xml_node_for_each_check(ctx->xml, cmd);
- break;
- }
- if (!cmd) {
- wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
- cmd);
- free(id);
- return -1;
- }
-
- name = xml_node_get_localname(ctx->xml, cmd);
-
- if (strcasecmp(name, "launchBrowserToURI") == 0) {
- int res;
- uri = xml_node_get_text(ctx->xml, cmd);
- if (!uri) {
- wpa_printf(MSG_INFO, "No URI found");
- free(id);
- return -1;
- }
- wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
- write_summary(ctx, "Launch browser to URI '%s'", uri);
- res = hs20_web_browser(uri, 1);
- xml_node_get_text_free(ctx->xml, uri);
- if (res > 0) {
- wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
- id);
- write_summary(ctx, "User response in browser completed successfully");
- *ret_node = hs20_spp_user_input_completed(ctx, id);
- free(id);
- return *ret_node ? 0 : -1;
- } else {
- wpa_printf(MSG_INFO, "Failed to receive user response");
- write_summary(ctx, "Failed to receive user response");
- hs20_spp_update_response(
- ctx, id, "Error occurred", "Other");
- free(id);
- return -1;
- }
- }
-
- if (strcasecmp(name, "uploadMO") == 0) {
- if (pps_fname == NULL)
- return -1;
- *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
- pps_fname);
- free(id);
- return *ret_node ? 0 : -1;
- }
-
- if (strcasecmp(name, "getCertificate") == 0) {
- *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
- pps_fname);
- free(id);
- return *ret_node ? 0 : -1;
- }
-
- wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
- free(id);
- return -1;
-}
-
-
-enum spp_post_dev_data_use {
- SPP_SUBSCRIPTION_REMEDIATION,
- SPP_POLICY_UPDATE,
- SPP_SUBSCRIPTION_REGISTRATION,
-};
-
-static void process_spp_post_dev_data_response(
- struct hs20_osu_client *ctx,
- enum spp_post_dev_data_use use, xml_node_t *node,
- const char *pps_fname, xml_node_t *pps)
-{
- xml_node_t *child;
- char *status = NULL;
- xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
- char *session_id = NULL;
-
- debug_dump_node(ctx, "sppPostDevDataResponse node", node);
-
- status = get_spp_attr_value(ctx->xml, node, "sppStatus");
- if (status == NULL) {
- wpa_printf(MSG_INFO, "No sppStatus attribute");
- goto out;
- }
- write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
- status);
-
- session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
- if (session_id == NULL) {
- wpa_printf(MSG_INFO, "No sessionID attribute");
- goto out;
- }
-
- wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'",
- status, session_id);
-
- xml_node_for_each_child(ctx->xml, child, node) {
- const char *name;
- xml_node_for_each_check(ctx->xml, child);
- debug_dump_node(ctx, "child", child);
- name = xml_node_get_localname(ctx->xml, child);
- wpa_printf(MSG_INFO, "localname: '%s'", name);
- if (!update && strcasecmp(name, "updateNode") == 0)
- update = child;
- if (!exec && strcasecmp(name, "exec") == 0)
- exec = child;
- if (!add_mo && strcasecmp(name, "addMO") == 0)
- add_mo = child;
- if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
- no_mo = child;
- }
-
- if (use == SPP_SUBSCRIPTION_REMEDIATION &&
- strcasecmp(status,
- "Remediation complete, request sppUpdateResponse") == 0)
- {
- int res, ret;
- if (!update && !no_mo) {
- wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
- goto out;
- }
- wpa_printf(MSG_INFO, "Subscription remediation completed");
- res = update_pps(ctx, update, pps_fname, pps);
- if (res < 0)
- wpa_printf(MSG_INFO, "Failed to update PPS MO");
- ret = hs20_spp_update_response(
- ctx, session_id,
- res < 0 ? "Error occurred" : "OK",
- res < 0 ? "MO addition or update failed" : NULL);
- if (res == 0 && ret == 0)
- hs20_sub_rem_complete(ctx, pps_fname);
- goto out;
- }
-
- if (use == SPP_SUBSCRIPTION_REMEDIATION &&
- strcasecmp(status, "Exchange complete, release TLS connection") ==
- 0) {
- if (!no_mo) {
- wpa_printf(MSG_INFO, "No noMOUpdate element");
- goto out;
- }
- wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
- goto out;
- }
-
- if (use == SPP_POLICY_UPDATE &&
- strcasecmp(status, "Update complete, request sppUpdateResponse") ==
- 0) {
- int res, ret;
- wpa_printf(MSG_INFO, "Policy update received - update PPS");
- res = update_pps(ctx, update, pps_fname, pps);
- ret = hs20_spp_update_response(
- ctx, session_id,
- res < 0 ? "Error occurred" : "OK",
- res < 0 ? "MO addition or update failed" : NULL);
- if (res == 0 && ret == 0)
- hs20_policy_update_complete(ctx, pps_fname);
- goto out;
- }
-
- if (use == SPP_SUBSCRIPTION_REGISTRATION &&
- strcasecmp(status, "Provisioning complete, request "
- "sppUpdateResponse") == 0) {
- if (!add_mo) {
- wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
- goto out;
- }
- process_spp_user_input_response(ctx, session_id, add_mo);
- node = NULL;
- goto out;
- }
-
- if (strcasecmp(status, "No update available at this time") == 0) {
- wpa_printf(MSG_INFO, "No update available at this time");
- goto out;
- }
-
- if (strcasecmp(status, "OK") == 0) {
- int res;
- xml_node_t *ret;
-
- if (!exec) {
- wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
- goto out;
- }
- res = hs20_spp_exec(ctx, exec, session_id,
- pps_fname, pps, &ret);
- /* xml_node_free(ctx->xml, node); */
- node = NULL;
- if (res == 0 && ret)
- process_spp_post_dev_data_response(ctx, use,
- ret, pps_fname, pps);
- goto out;
- }
-
- if (strcasecmp(status, "Error occurred") == 0) {
- xml_node_t *err;
- char *code = NULL;
- err = get_node(ctx->xml, node, "sppError");
- if (err)
- code = xml_node_get_attr_value(ctx->xml, err,
- "errorCode");
- wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
- code ? code : "N/A");
- xml_node_get_attr_value_free(ctx->xml, code);
- goto out;
- }
-
- wpa_printf(MSG_INFO,
- "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
- status);
-out:
- xml_node_get_attr_value_free(ctx->xml, status);
- xml_node_get_attr_value_free(ctx->xml, session_id);
- xml_node_free(ctx->xml, node);
-}
-
-
-static int spp_post_dev_data(struct hs20_osu_client *ctx,
- enum spp_post_dev_data_use use,
- const char *reason,
- const char *pps_fname, xml_node_t *pps)
-{
- xml_node_t *payload;
- xml_node_t *ret_node;
-
- payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
- if (payload == NULL)
- return -1;
-
- ret_node = soap_send_receive(ctx->http, payload);
- if (!ret_node) {
- const char *err = http_get_err(ctx->http);
- if (err) {
- wpa_printf(MSG_INFO, "HTTP error: %s", err);
- write_result(ctx, "HTTP error: %s", err);
- } else {
- write_summary(ctx, "Failed to send SOAP message");
- }
- return -1;
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return -1;
- }
-
- process_spp_post_dev_data_response(ctx, use, ret_node,
- pps_fname, pps);
- return 0;
-}
-
-
-void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "SPP subscription remediation");
- write_summary(ctx, "SPP subscription remediation");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(address);
-
- if (soap_init_client(ctx->http, address, ctx->ca_fname,
- cred_username, cred_password, client_cert,
- client_key) == 0) {
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
- "Subscription remediation", pps_fname, pps);
- }
-}
-
-
-static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
- const char *pps_fname)
-{
- wpa_printf(MSG_INFO, "Policy update completed");
-
- /*
- * Update wpa_supplicant credentials and reconnect using updated
- * information.
- */
- wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
- cmd_set_pps(ctx, pps_fname);
-
- wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
- if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
- wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
-}
-
-
-static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
- xml_node_t *node)
-{
- char *status, *session_id;
-
- debug_dump_node(ctx, "sppExchangeComplete", node);
-
- status = get_spp_attr_value(ctx->xml, node, "sppStatus");
- if (status == NULL) {
- wpa_printf(MSG_INFO, "No sppStatus attribute");
- return -1;
- }
- write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
- status);
-
- session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
- if (session_id == NULL) {
- wpa_printf(MSG_INFO, "No sessionID attribute");
- xml_node_get_attr_value_free(ctx->xml, status);
- return -1;
- }
-
- wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'",
- status, session_id);
- xml_node_get_attr_value_free(ctx->xml, session_id);
-
- if (strcasecmp(status, "Exchange complete, release TLS connection") ==
- 0) {
- xml_node_get_attr_value_free(ctx->xml, status);
- return 0;
- }
-
- wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
- write_summary(ctx, "Unexpected sppStatus '%s'", status);
- xml_node_get_attr_value_free(ctx->xml, status);
- return -1;
-}
-
-
-static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code)
-{
- xml_namespace_t *ns;
- xml_node_t *spp_node, *node;
-
- spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
- "sppUpdateResponse");
- if (spp_node == NULL)
- return NULL;
-
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
- xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
- xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
-
- if (error_code) {
- node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
- if (node)
- xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
- error_code);
- }
-
- return spp_node;
-}
-
-
-static int hs20_spp_update_response(struct hs20_osu_client *ctx,
- const char *session_id,
- const char *spp_status,
- const char *error_code)
-{
- xml_node_t *node, *ret_node;
- int ret;
-
- write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
- spp_status, error_code);
- node = build_spp_update_response(ctx, session_id, spp_status,
- error_code);
- if (node == NULL)
- return -1;
- ret_node = soap_send_receive(ctx->http, node);
- if (!ret_node) {
- if (soap_reinit_client(ctx->http) < 0)
- return -1;
- wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
- node = build_spp_update_response(ctx, session_id, spp_status,
- error_code);
- if (node == NULL)
- return -1;
- ret_node = soap_send_receive(ctx->http, node);
- if (ret_node == NULL)
- return -1;
- wpa_printf(MSG_INFO, "Continue with new connection");
- }
-
- if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
- wpa_printf(MSG_INFO, "SPP validation failed");
- xml_node_free(ctx->xml, ret_node);
- return -1;
- }
-
- ret = process_spp_exchange_complete(ctx, ret_node);
- xml_node_free(ctx->xml, ret_node);
- return ret;
-}
-
-
-void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
- const char *pps_fname,
- const char *client_cert, const char *client_key,
- const char *cred_username, const char *cred_password,
- xml_node_t *pps)
-{
- wpa_printf(MSG_INFO, "SPP policy update");
- write_summary(ctx, "SPP policy update");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(address);
-
- if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
- cred_password, client_cert, client_key) == 0) {
- spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
- pps_fname, pps);
- }
-}
-
-
-int cmd_prov(struct hs20_osu_client *ctx, const char *url)
-{
- unlink("Cert/est_cert.der");
- unlink("Cert/est_cert.pem");
-
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO,
- "Credential provisioning requested - URL: %s ca_fname: %s",
- url, ctx->ca_fname ? ctx->ca_fname : "N/A");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
-
- if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
- return -1;
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
- "Subscription registration", NULL, NULL);
-
- return ctx->pps_cred_set ? 0 : -1;
-}
-
-
-int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
-{
- if (url == NULL) {
- wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
- return -1;
- }
-
- wpa_printf(MSG_INFO, "SIM provisioning requested");
-
- os_free(ctx->server_url);
- ctx->server_url = os_strdup(url);
-
- wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
-
- if (wait_ip_addr(ctx->ifname, 15) < 0) {
- wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
- }
-
- if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
- NULL) < 0)
- return -1;
- spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
- "Subscription provisioning", NULL, NULL);
-
- return ctx->pps_cred_set ? 0 : -1;
-}
diff --git a/contrib/wpa/src/ap/Makefile b/contrib/wpa/src/ap/Makefile
index a1e9b7c44d2f..a7513ecfbe8a 100644
--- a/contrib/wpa/src/ap/Makefile
+++ b/contrib/wpa/src/ap/Makefile
@@ -3,6 +3,9 @@ CFLAGS += -DNEED_AP_MLME
CFLAGS += -DCONFIG_ETH_P_OUI
CFLAGS += -DCONFIG_HS20
CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AX
+CFLAGS += -DCONFIG_IEEE80211BE
CFLAGS += -DCONFIG_IEEE80211R
CFLAGS += -DCONFIG_IEEE80211R_AP
CFLAGS += -DCONFIG_WPS
@@ -32,10 +35,13 @@ LIB_OBJS= \
hw_features.o \
ieee802_11_auth.o \
ieee802_11.o \
+ ieee802_11_eht.o \
+ ieee802_11_he.o \
ieee802_11_ht.o \
ieee802_11_shared.o \
ieee802_11_vht.o \
ieee802_1x.o \
+ interference.o \
neighbor_db.o \
ndisc_snoop.o \
p2p_hostapd.o \
diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c
index f5b36d327a74..1966d8ec6c1a 100644
--- a/contrib/wpa/src/ap/acs.c
+++ b/contrib/wpa/src/ap/acs.c
@@ -2,6 +2,7 @@
* ACS - Automatic Channel Selection module
* Copyright (c) 2011, Atheros Communications
* Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -54,7 +55,7 @@
* Todo / Ideas
* ------------
* - implement other interference computation methods
- * - BSS/RSSI based
+ * - RSSI based
* - spectral scan based
* (should be possibly to hook this up with current ACS scans)
* - add wpa_supplicant support (for P2P)
@@ -557,6 +558,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
static int acs_usable_chan(struct hostapd_channel_data *chan)
{
+ if (chan->interference_bss_based)
+ return 1;
+
return !dl_list_empty(&chan->survey_list) &&
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
acs_survey_list_is_sufficient(chan);
@@ -828,7 +832,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
struct hostapd_channel_data **ideal_chan,
long double *ideal_factor)
{
- struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+ struct hostapd_channel_data *chan, *adj_chan = NULL,
+ *chan2 = NULL, *best;
long double factor;
int i, j;
int bw320_offset = 0, ideal_bw320_offset = 0;
@@ -847,49 +852,11 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
chan = &mode->channels[i];
- /* Since in the current ACS implementation the first channel is
- * always a primary channel, skip channels not available as
- * primary until more sophisticated channel selection is
- * implemented.
- *
- * If this implementation is changed to allow any channel in
- * the bandwidth to be the primary one, the last parameter to
- * acs_update_puncturing_bitmap() should be changed to the index
- * of the primary channel
- */
- if (!chan_pri_allowed(chan))
- continue;
-
- if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
- iface->conf->acs_exclude_dfs)
- continue;
-
- if (!is_in_chanlist(iface, chan))
- continue;
-
- if (!is_in_freqlist(iface, chan))
- continue;
-
- if (chan->max_tx_power < iface->conf->min_tx_power)
- continue;
-
- if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
- iface->conf->country[2] == 0x4f)
- continue;
-
- if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
- wpa_printf(MSG_DEBUG,
- "ACS: Channel %d: BW %u is not supported",
- chan->chan, bw);
- continue;
- }
-
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
- if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
- ((iface->conf->ieee80211n &&
- iface->conf->secondary_channel) ||
- is_6ghz_freq(chan->freq)) &&
+ if (bw == 40 &&
+ mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211n &&
!acs_usable_bw_chan(chan, ACS_BW40)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
@@ -900,18 +867,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
(iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
iface->conf->ieee80211be)) {
- if (hostapd_get_oper_chwidth(iface->conf) ==
- CONF_OPER_CHWIDTH_80MHZ &&
- !acs_usable_bw_chan(chan, ACS_BW80)) {
+ if (bw == 80 && !acs_usable_bw_chan(chan, ACS_BW80)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
chan->chan);
continue;
}
- if (hostapd_get_oper_chwidth(iface->conf) ==
- CONF_OPER_CHWIDTH_160MHZ &&
- !acs_usable_bw_chan(chan, ACS_BW160)) {
+ if (bw == 160 && !acs_usable_bw_chan(chan, ACS_BW160)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
chan->chan);
@@ -921,43 +884,67 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
iface->conf->ieee80211be) {
- if (hostapd_get_oper_chwidth(iface->conf) ==
- CONF_OPER_CHWIDTH_320MHZ &&
- !acs_usable_bw320_chan(iface, chan, &bw320_offset))
+ if (bw == 320 &&
+ !acs_usable_bw320_chan(iface, chan,
+ &bw320_offset)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth",
+ chan->chan);
continue;
+ }
}
factor = 0;
best = NULL;
- if (acs_usable_chan(chan)) {
- factor = chan->interference_factor;
- total_weight = 1;
- best = chan;
- }
- for (j = 1; j < n_chans; j++) {
- adj_chan = acs_find_chan(iface, chan->freq +
- j * secondary_channel * 20);
- if (!adj_chan)
+ for (j = 0; j < n_chans; j++) {
+ chan2 = acs_find_chan(iface, chan->freq +
+ j * secondary_channel * 20);
+ if (!chan2)
break;
- if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+ if (!chan_bw_allowed(chan2, bw, secondary_channel != -1,
+ j == 0)) {
wpa_printf(MSG_DEBUG,
- "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
- chan->chan, adj_chan->chan, bw);
+ "ACS: Channel %d: BW %u is not supported",
+ chan2->chan, bw);
break;
}
- if (!acs_usable_chan(adj_chan))
+ if ((chan2->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs)
+ break;
+
+ if (chan2->max_tx_power < iface->conf->min_tx_power)
+ break;
+
+ if ((chan2->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ break;
+
+ if (!acs_usable_chan(chan2))
continue;
- factor += adj_chan->interference_factor;
+ factor += chan2->interference_factor;
total_weight += 1;
+ if (!chan_pri_allowed(chan2))
+ continue;
+
+ if (!is_in_chanlist(iface, chan2))
+ continue;
+
+ if (!is_in_freqlist(iface, chan2))
+ continue;
+
+ if (iface->conf->acs_exclude_6ghz_non_psc &&
+ !is_6ghz_psc_frequency(chan2->freq))
+ continue;
+
/* find the best channel in this segment */
- if (!best || adj_chan->interference_factor <
+ if (!best || chan2->interference_factor <
best->interference_factor)
- best = adj_chan;
+ best = chan2;
}
if (j != n_chans) {
@@ -966,18 +953,25 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
continue;
}
+ if (!best) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: No valid channel found in the segment starting with channel %d",
+ chan->chan);
+ continue;
+ }
+
/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
* crowded primary channel if one was found in the segment */
if (iface->current_mode &&
iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
- best && chan != best) {
+ chan != best) {
wpa_printf(MSG_DEBUG,
"ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
best->chan, chan->chan,
chan->interference_factor,
best->interference_factor);
#ifdef CONFIG_IEEE80211BE
- index_primary = (chan->freq - best->freq) / 20;
+ index_primary = (best->freq - chan->freq) / 20;
#endif /* CONFIG_IEEE80211BE */
chan = best;
}
@@ -1254,13 +1248,53 @@ static int acs_study_survey_based(struct hostapd_iface *iface)
}
+static int acs_study_bss_based(struct hostapd_iface *iface)
+{
+ struct wpa_scan_results *scan_res;
+ int j;
+
+ wpa_printf(MSG_DEBUG, "ACS: Trying BSS-based ACS");
+
+ scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+ if (!scan_res) {
+ wpa_printf(MSG_INFO, "ACS: Scan request failed");
+ hostapd_setup_interface_complete(iface, 1);
+ return -1;
+ }
+
+ for (j = 0; j < iface->current_mode->num_channels; j++) {
+ struct hostapd_channel_data *chan;
+ unsigned int bss_on_ch = 0;
+ size_t i;
+
+ chan = &iface->current_mode->channels[j];
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+
+ if (bss->freq == chan->freq)
+ bss_on_ch++;
+ }
+
+ wpa_printf(MSG_MSGDUMP,
+ "ACS: Interference on ch %d (%d MHz): %d",
+ chan->chan, chan->freq, bss_on_ch);
+ chan->interference_factor = bss_on_ch;
+ chan->interference_bss_based = true;
+ }
+
+ wpa_scan_results_free(scan_res);
+ return 0;
+}
+
+
static int acs_study_options(struct hostapd_iface *iface)
{
if (acs_study_survey_based(iface) == 0)
return 0;
- /* TODO: If no surveys are available/sufficient this is a good
- * place to fallback to BSS-based ACS */
+ wpa_printf(MSG_INFO, "ACS: Survey based ACS failed");
+ if (acs_study_bss_based(iface) == 0)
+ return 0;
return -1;
}
diff --git a/contrib/wpa/src/ap/afc.c b/contrib/wpa/src/ap/afc.c
new file mode 100644
index 000000000000..fed4d940ee3d
--- /dev/null
+++ b/contrib/wpa/src/ap/afc.c
@@ -0,0 +1,1123 @@
+/*
+ * Automated Frequency Coordination
+ * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/un.h>
+#include <time.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/json.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.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 int hostapd_afc_reset_channels(struct hostapd_iface *iface)
+{
+ int ret;
+
+ ret = hostapd_get_hw_features(iface);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "AFC: Failed to reset channel flags");
+ return ret;
+ }
+
+ ret = hostapd_set_current_hw_info(iface, iface->freq);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "AFC: Failed to set current hardware info");
+
+ return ret;
+}
+
+
+static struct wpabuf *
+hostapd_afc_build_location_request(struct hostapd_iface *iface)
+{
+ struct wpabuf *location_obj, *ellipse_obj = NULL;
+ struct hostapd_config *iconf = iface->conf;
+ bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
+ size_t location_len = 1024, ellipse_len = 128;
+ unsigned int i;
+
+ location_obj = wpabuf_alloc(location_len);
+ if (!location_obj)
+ return NULL;
+
+ json_start_object(location_obj, "location");
+ if (iconf->afc.location.type != AFC_LINEAR_POLYGON) {
+ struct afc_linear_polygon *lp =
+ iconf->afc.location.linear_polygon_data;
+
+ if (!lp)
+ goto error;
+
+ ellipse_obj = wpabuf_alloc(ellipse_len);
+ if (!ellipse_obj)
+ goto error;
+
+ json_start_object(ellipse_obj, "center");
+
+ json_add_double(ellipse_obj, "longitude", lp->longitude);
+ json_value_sep(ellipse_obj);
+
+ json_add_double(ellipse_obj, "latitude", lp->latitude);
+ json_end_object(ellipse_obj);
+ json_value_sep(ellipse_obj);
+ }
+
+ switch (iconf->afc.location.type) {
+ case AFC_LINEAR_POLYGON:
+ json_start_object(location_obj, "linearPolygon");
+ json_start_array(location_obj, "outerBoundary");
+ for (i = 0; i < iconf->afc.location.n_linear_polygon_data; i++)
+ {
+ struct afc_linear_polygon *lp =
+ &iconf->afc.location.linear_polygon_data[i];
+
+ json_start_object(location_obj, NULL);
+
+ json_add_double(location_obj, "longitude",
+ lp->longitude);
+ json_value_sep(location_obj);
+
+ json_add_double(location_obj, "latitude",
+ lp->latitude);
+
+ json_end_object(location_obj);
+ if (i < iconf->afc.location.n_linear_polygon_data - 1)
+ json_value_sep(location_obj);
+ }
+ json_end_array(location_obj);
+
+ /* linearPolygon */
+ json_end_object(location_obj);
+ json_value_sep(location_obj);
+ break;
+ case AFC_RADIAL_POLYGON:
+ json_start_object(location_obj, "radialPolygon");
+ wpabuf_put_buf(location_obj, ellipse_obj);
+
+ json_start_array(location_obj, "outerBoundary");
+ for (i = 0; i < iconf->afc.location.n_radial_polygon_data; i++)
+ {
+ struct afc_radial_polygon *rp =
+ &iconf->afc.location.radial_polygon_data[i];
+
+ json_start_object(location_obj, NULL);
+ json_add_double(location_obj, "length", rp->length);
+ json_value_sep(location_obj);
+
+ json_add_double(location_obj, "angle", rp->angle);
+ json_end_object(location_obj);
+ if (i < iconf->afc.location.n_radial_polygon_data - 1)
+ json_value_sep(location_obj);
+ }
+ json_end_array(location_obj);
+
+ /* radialPolygon */
+ json_end_object(location_obj);
+ json_value_sep(location_obj);
+ break;
+ case AFC_ELLIPSE:
+ default:
+ json_start_object(location_obj, "ellipse");
+ wpabuf_put_buf(location_obj, ellipse_obj);
+
+ json_add_int(location_obj, "majorAxis",
+ iconf->afc.location.major_axis);
+ json_value_sep(location_obj);
+ json_add_int(location_obj, "minorAxis",
+ iconf->afc.location.minor_axis);
+ json_value_sep(location_obj);
+ json_add_int(location_obj, "orientation",
+ iconf->afc.location.orientation);
+ /* ellipse */
+ json_end_object(location_obj);
+ json_value_sep(location_obj);
+ break;
+ }
+
+ json_start_object(location_obj, "elevation");
+
+ json_add_double(location_obj, "height", iconf->afc.location.height);
+ json_value_sep(location_obj);
+ if (iconf->afc.location.height_type) {
+ json_add_string(location_obj, "heightType",
+ iconf->afc.location.height_type);
+ json_value_sep(location_obj);
+ }
+ json_add_int(location_obj, "verticalUncertainty",
+ iconf->afc.location.vertical_tolerance);
+
+ /* elevation */
+ json_end_object(location_obj);
+ json_value_sep(location_obj);
+
+ json_add_int(location_obj, "indoorDeployment", is_ap_indoor);
+
+ /* location */
+ json_end_object(location_obj);
+ json_value_sep(location_obj);
+
+ wpabuf_free(ellipse_obj);
+ return location_obj;
+
+error:
+ wpabuf_free(location_obj);
+ wpabuf_free(ellipse_obj);
+
+ return NULL;
+}
+
+
+static struct wpabuf * hostapd_afc_get_opclass_chan_list(u8 op_class)
+{
+ struct wpabuf *chan_list_obj;
+ const struct oper_class_map *oper_class;
+ int chan_offset, chan;
+ size_t ch_len = 512;
+
+ oper_class = get_oper_class(NULL, op_class);
+ if (!oper_class)
+ return NULL;
+
+ chan_list_obj = wpabuf_alloc(ch_len);
+ if (!chan_list_obj)
+ return NULL;
+ json_start_array(chan_list_obj, "channelCfi");
+
+ switch (op_class) {
+ case 132: /* 40 MHz */
+ chan_offset = 2;
+ break;
+ case 133: /* 80 MHz */
+ chan_offset = 6;
+ break;
+ case 134: /* 160 MHz */
+ chan_offset = 14;
+ break;
+ case 137: /* 320 MHz */
+ /*
+ * global_op_class already use the central channels for
+ * 320 MHz, so fallthrough and use 0 for chan_offset.
+ */
+ default:
+ chan_offset = 0;
+ break;
+ }
+
+ for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
+ chan += oper_class->inc) {
+ char ch_str[32];
+
+ if (chan + chan_offset > oper_class->max_chan)
+ break;
+
+ os_snprintf(ch_str, sizeof(ch_str), "%s%d",
+ chan != oper_class->min_chan ? ", " : "",
+ chan + chan_offset);
+ wpabuf_put_str(chan_list_obj, ch_str);
+ }
+ json_end_array(chan_list_obj);
+
+ return chan_list_obj;
+}
+
+
+static struct wpabuf *
+hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
+{
+ struct wpabuf *op_class_list_obj;
+ struct hostapd_config *iconf = iface->conf;
+ unsigned int i, num;
+
+ op_class_list_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
+ if (!op_class_list_obj)
+ return NULL;
+
+ json_start_array(op_class_list_obj, "inquiredChannels");
+ num = int_array_len(iconf->afc.op_class);
+ for (i = 0; i < num; i++) {
+ struct wpabuf *chan_list_obj = NULL;
+ u8 op_class = iconf->afc.op_class[i];
+
+ if (!is_6ghz_op_class(op_class))
+ continue;
+
+ json_start_object(op_class_list_obj, NULL);
+
+ json_add_int(op_class_list_obj, "globalOperatingClass",
+ op_class);
+ json_value_sep(op_class_list_obj);
+
+ chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
+ if (!chan_list_obj)
+ goto error;
+
+ wpabuf_put_buf(op_class_list_obj, chan_list_obj);
+ json_end_object(op_class_list_obj);
+
+ if (i < num - 1)
+ json_value_sep(op_class_list_obj);
+
+ wpabuf_free(chan_list_obj);
+ chan_list_obj = NULL;
+ }
+ json_end_array(op_class_list_obj);
+
+ return op_class_list_obj;
+
+error:
+ wpabuf_free(op_class_list_obj);
+ return NULL;
+}
+
+
+static struct wpabuf *
+hostapd_afc_build_request(struct hostapd_iface *iface)
+{
+ struct wpabuf *req_obj, *location_obj, *op_class_list_obj = NULL;
+ struct hostapd_config *iconf = iface->conf;
+ unsigned int i;
+ char request_id_str[16];
+
+ req_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
+ if (!req_obj)
+ return NULL;
+
+ json_start_object(req_obj, NULL);
+
+ if (iconf->afc.request.version) {
+ json_add_string(req_obj, "version", iconf->afc.request.version);
+ json_value_sep(req_obj);
+ }
+
+ json_start_array(req_obj, "availableSpectrumInquiryRequests");
+ json_start_object(req_obj, NULL);
+
+ iface->afc.request_id++;
+ os_snprintf(request_id_str, sizeof(request_id_str), "%u",
+ iface->afc.request_id);
+ json_add_string(req_obj, "requestId", request_id_str);
+ json_value_sep(req_obj);
+
+ json_start_object(req_obj, "deviceDescriptor");
+ if (iconf->afc.request.sn) {
+ json_add_string(req_obj, "serialNumber", iconf->afc.request.sn);
+ json_value_sep(req_obj);
+ }
+
+ json_start_array(req_obj, "certificationId");
+
+ for (i = 0; i < iconf->afc.n_cert_ids; i++) {
+ json_start_object(req_obj, NULL);
+ json_add_string(req_obj, "rulesetId",
+ iconf->afc.cert_ids[i].ruleset);
+ json_value_sep(req_obj);
+
+ json_add_string(req_obj, "id", iconf->afc.cert_ids[i].id);
+ json_end_object(req_obj);
+
+ if (i < iconf->afc.n_cert_ids - 1)
+ json_value_sep(req_obj);
+ }
+ /* certificationId */
+ json_end_array(req_obj);
+
+ /* deviceDescriptor */
+ json_end_object(req_obj);
+ json_value_sep(req_obj);
+
+ location_obj = hostapd_afc_build_location_request(iface);
+ if (!location_obj)
+ goto error;
+
+ wpabuf_put_buf(req_obj, location_obj);
+
+ if (iconf->afc.freqs.num) {
+ json_start_array(req_obj, "inquiredFrequencyRange");
+ for (i = 0; i < iconf->afc.freqs.num; i++) {
+ struct wpa_freq_range *fr = &iconf->afc.freqs.range[i];
+
+ json_start_object(req_obj, NULL);
+
+ json_add_int(req_obj, "lowFrequency", fr->min);
+ json_value_sep(req_obj);
+ json_add_int(req_obj, "highFrequency", fr->max);
+
+ json_end_object(req_obj);
+ if (i < iconf->afc.freqs.num - 1)
+ json_value_sep(req_obj);
+ }
+ json_end_array(req_obj);
+ json_value_sep(req_obj);
+ }
+
+ op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
+ if (!op_class_list_obj)
+ goto error;
+
+ wpabuf_put_buf(req_obj, op_class_list_obj);
+
+ if (iconf->afc.min_power) {
+ json_value_sep(req_obj);
+ json_add_int(req_obj, "minDesiredPower", iconf->afc.min_power);
+ }
+
+ /* availableSpectrumInquiryRequests */
+ json_end_object(req_obj);
+ json_end_array(req_obj);
+
+ json_end_object(req_obj);
+ wpa_hexdump_ascii_buf(MSG_MSGDUMP, "AFC: Pending request", req_obj);
+
+ wpabuf_free(location_obj);
+ wpabuf_free(op_class_list_obj);
+
+ return req_obj;
+
+error:
+ wpabuf_free(req_obj);
+ wpabuf_free(location_obj);
+ wpabuf_free(op_class_list_obj);
+
+ return NULL;
+}
+
+
+static int
+hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
+ struct json_token *freq_info)
+{
+ struct afc_freq_range_elem *f = NULL, *tmp;
+ struct json_token *freq_obj;
+ int count = 0;
+
+ for (freq_obj = freq_info->child; freq_obj;
+ freq_obj = freq_obj->sibling) {
+ struct json_token *freq_range_obj, *token;
+ int low_freq, high_freq;
+ double max_psd;
+
+ freq_range_obj = json_get_member(freq_obj, "frequencyRange");
+ if (!freq_range_obj || freq_range_obj->type != JSON_OBJECT)
+ continue;
+
+ token = json_get_member(freq_range_obj, "lowFrequency");
+ if (!token || token->type != JSON_NUMBER)
+ continue;
+ low_freq = token->number;
+
+ token = json_get_member(freq_range_obj, "highFrequency");
+ if (!token || token->type != JSON_NUMBER)
+ continue;
+ high_freq = token->number;
+
+ token = json_get_member(freq_obj, "maxPsd");
+ if (!token)
+ token = json_get_member(freq_obj, "maxPSD");
+ if (!token || (token->type != JSON_DOUBLE &&
+ token->type != JSON_NUMBER))
+ continue;
+ max_psd = token->type == JSON_DOUBLE ?
+ token->dnumber : (double) token->number;
+
+ tmp = os_realloc_array(f, count + 1, sizeof(*f));
+ if (!tmp) {
+ os_free(f);
+ return -ENOMEM;
+ }
+ f = tmp;
+
+ f[count].low_freq = low_freq;
+ f[count].high_freq = high_freq;
+ f[count++].max_psd = max_psd;
+ }
+ os_free(iface->afc.freq_range);
+ 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, double power)
+{
+ int op_class_pwr_index, num_low_subchan, channel;
+ int count = *chan_list_size;
+ struct afc_chan_info_elem *c = *chan_list, *tmp;
+
+ switch (op_class) {
+ case 132: /* 40 MHz */
+ op_class_pwr_index = 1;
+ num_low_subchan = 2;
+ break;
+ case 133: /* 80 MHz */
+ op_class_pwr_index = 2;
+ num_low_subchan = 6;
+ break;
+ case 134: /* 160 MHz */
+ op_class_pwr_index = 3;
+ num_low_subchan = 14;
+ break;
+ case 137: /* 320 MHz */
+ op_class_pwr_index = 4;
+ num_low_subchan = 30;
+ break;
+ default:
+ op_class_pwr_index = 0;
+ num_low_subchan = 0;
+ break;
+ }
+
+ for (channel = center_chan - num_low_subchan;
+ channel <= center_chan + num_low_subchan; channel += 4) {
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (c[i].chan == channel)
+ break;
+ }
+
+ if (i == count) {
+ tmp = os_realloc_array(c, count + 1, sizeof(*c));
+ if (!tmp) {
+ os_free(c);
+ *chan_list = NULL;
+ *chan_list_size = 0;
+ return -ENOMEM;
+ }
+ c = tmp;
+
+ c[count].chan = channel;
+ os_memset(c[count].power, 0, sizeof(c[count].power));
+ count++;
+ }
+ c[i].power[op_class_pwr_index] = power;
+ }
+
+ *chan_list_size = count;
+ *chan_list = c;
+
+ return 0;
+}
+
+
+static int
+hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
+ struct json_token *chan_info)
+{
+ struct afc_chan_info_elem *c = NULL;
+ struct json_token *chan_obj;
+ int count = 0;
+
+ for (chan_obj = chan_info->child; chan_obj;
+ chan_obj = chan_obj->sibling) {
+ struct json_token *chan_cfi, *max_eirp;
+ struct json_token *token, *chan_arr, *power_arr;
+ int op_class;
+
+ token = json_get_member(chan_obj, "globalOperatingClass");
+ if (!token || token->type != JSON_NUMBER)
+ continue;
+ op_class = token->number;
+
+ chan_cfi = json_get_member(chan_obj, "channelCfi");
+ if (!chan_cfi || chan_cfi->type != JSON_ARRAY)
+ continue;
+
+ max_eirp = json_get_member(chan_obj, "maxEirp");
+ if (!max_eirp || max_eirp->type != JSON_ARRAY)
+ continue;
+
+ chan_arr = chan_cfi->child;
+ power_arr = max_eirp->child;
+ while (chan_arr && power_arr) {
+ int channel, ret;
+ double power;
+
+ if (chan_arr->type != JSON_NUMBER ||
+ (power_arr->type != JSON_DOUBLE &&
+ power_arr->type != JSON_NUMBER)) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Failed to parse array at opclass %d",
+ op_class);
+ break;
+ }
+
+ channel = chan_arr->number;
+ power = power_arr->type == JSON_DOUBLE ?
+ power_arr->dnumber : (double) power_arr->number;
+
+ ret = hostad_afc_update_chan_info(&c, &count, op_class,
+ channel, power);
+ if (ret) {
+ iface->afc.chan_info_list = NULL;
+ iface->afc.num_chan_info = 0;
+ return ret;
+ }
+ chan_arr = chan_arr->sibling;
+ power_arr = power_arr->sibling;
+ }
+ }
+ os_free(iface->afc.chan_info_list);
+ iface->afc.chan_info_list = c;
+ iface->afc.num_chan_info = count;
+
+ return 0;
+}
+
+
+static int hostad_afc_get_timeout(struct json_token *obj)
+{
+ int year, month, day, hour, min, sec;
+ os_time_t t;
+ struct os_time now;
+
+ if (sscanf(obj->string, "%d-%d-%dT%d:%d:%dZ",
+ &year, &month, &day, &hour, &min, &sec) != 6)
+ return HOSTAPD_AFC_TIMEOUT;
+
+ if (os_mktime(year, month, day, hour, min, sec, &t) < 0)
+ return HOSTAPD_AFC_TIMEOUT;
+
+ os_get_time(&now);
+
+ return t < now.sec ?
+ HOSTAPD_AFC_RETRY_TIMEOUT : (t - now.sec) * 80 / 100;
+}
+
+
+static int
+hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply, size_t len)
+{
+ struct hostapd_config *iconf = iface->conf;
+ int request_timeout = -1, ret = -EINVAL;
+ struct json_token *root = NULL, *reply_obj, *token;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "AFC: Received reply", reply, len);
+
+ iface->afc_response = os_malloc(len + 1);
+ if (!iface->afc_response) {
+ wpa_printf(MSG_ERROR,
+ "AFC: Failed to alloc memory for storing reply");
+ return -ENOMEM;
+ }
+ os_memcpy(iface->afc_response, reply, len);
+ iface->afc_response[len] = '\0';
+
+ root = json_parse(reply, len);
+ if (!root) {
+ wpa_printf(MSG_ERROR, "AFC: Failed to parse reply payload");
+ goto fail;
+ }
+
+ token = json_get_member(root, "version");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_ERROR, "AFC: Missing version in reply");
+ goto fail;
+ }
+ if (iconf->afc.request.version &&
+ os_strcmp(iconf->afc.request.version, token->string) != 0) {
+ wpa_printf(MSG_ERROR, "AFC: Mismatch in reply version");
+ goto fail;
+ }
+
+ reply_obj = json_get_member(root, "availableSpectrumInquiryResponses");
+ if (!reply_obj || reply_obj->type != JSON_ARRAY) {
+ wpa_printf(MSG_ERROR,
+ "AFC: Missing availableSpectrumInquiry in reply");
+ goto fail;
+ }
+
+ for (token = reply_obj->child; token; token = token->sibling) {
+ struct json_token *reply_elem, *response;
+ int status = -EINVAL, timeout;
+ unsigned int j;
+ char request_id_str[16];
+
+ reply_elem = json_get_member(token, "requestId");
+ if (!reply_elem || reply_elem->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Missing requestId in reply element");
+ continue;
+ }
+
+ snprintf(request_id_str, sizeof(request_id_str), "%u",
+ iface->afc.request_id);
+ if (os_strcmp(request_id_str, reply_elem->string) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: RequestId mismatch in reply element");
+ continue;
+ }
+
+ reply_elem = json_get_member(token, "rulesetId");
+ if (!reply_elem || reply_elem->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Missing rulesetId in reply element");
+ continue;
+ }
+
+ for (j = 0; j < iconf->afc.n_cert_ids; j++) {
+ if (os_strcmp(iconf->afc.cert_ids[j].ruleset,
+ reply_elem->string) == 0)
+ break;
+ }
+
+ if (j == iconf->afc.n_cert_ids) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: RulesetId mismatch in reply element");
+ continue;
+ }
+
+ response = json_get_member(token, "response");
+ if (!response || response->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Missing response field in reply element");
+ continue;
+ }
+
+ reply_elem = json_get_member(response, "shortDescription");
+ if (reply_elem && reply_elem->type == JSON_STRING)
+ wpa_printf(MSG_DEBUG, "AFC: Reply element: %s",
+ reply_elem->string);
+
+ reply_elem = json_get_member(response, "responseCode");
+ if (reply_elem && reply_elem->type == JSON_NUMBER)
+ status = reply_elem->number;
+
+ if (status < 0) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Reply invalid responseCode: %d",
+ status);
+ continue;
+ }
+ reply_elem = json_get_member(token, "availableFrequencyInfo");
+ if (reply_elem && reply_elem->type == JSON_ARRAY &&
+ hostad_afc_parse_available_freq_info(iface, reply_elem)) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Failed to parse availableFrequencyInfo");
+ continue;
+ }
+
+ reply_elem = json_get_member(token, "availableChannelInfo");
+ if (reply_elem && reply_elem->type == JSON_ARRAY &&
+ hostad_afc_parse_available_chan_info(iface, reply_elem)) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Failed to parse availableChannelInfo");
+ continue;
+ }
+
+ reply_elem = json_get_member(token, "availabilityExpireTime");
+ if (!reply_elem || reply_elem->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG,
+ "AFC: Missing expire time, use default timeout");
+ continue;
+ }
+
+ timeout = hostad_afc_get_timeout(reply_elem);
+ 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;
+
+fail:
+ json_free(root);
+ return ret;
+}
+
+
+static int hostapd_afc_send_receive(struct hostapd_iface *iface)
+{
+ struct hostapd_config *iconf = iface->conf;
+ struct wpabuf *request_obj = NULL;
+ struct timeval sock_timeout = {
+ .tv_sec = 10,
+ };
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+#ifdef __FreeBSD__
+ .sun_len = sizeof(addr),
+#endif /* __FreeBSD__ */
+ };
+ char *buf = NULL;
+ int sockfd, ret;
+ fd_set read_set;
+
+ if (iface->afc.data_valid) {
+ /* AFC data already downloaded from the server */
+ return 0;
+ }
+
+ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ if (!iconf->afc.socket) {
+ wpa_printf(MSG_ERROR, "AFC: Missing socket string");
+ return -EINVAL;
+ }
+
+ if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
+ wpa_printf(MSG_ERROR, "AFC: Malformed 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, "AFC: Failed creating socket");
+ return sockfd;
+ }
+
+ if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "AFC: Failed connecting socket");
+ ret = -EIO;
+ goto close_sock;
+ }
+
+ request_obj = hostapd_afc_build_request(iface);
+ if (!request_obj) {
+ ret = -EINVAL;
+ goto close_sock;
+ }
+
+ ret = send(sockfd, wpabuf_head(request_obj), wpabuf_len(request_obj),
+ 0);
+ if (ret < 0 || (size_t) ret != wpabuf_len(request_obj)) {
+ wpa_printf(MSG_ERROR, "AFC: Failed sending 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, "AFC: select() failed on 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)
+ ret = -EIO;
+ if (ret < 0)
+ goto close_sock;
+
+ ret = hostapd_afc_parse_reply(iface, buf, ret);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "AFC: Failed parsing reply: %d", ret);
+ goto close_sock;
+ }
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_RECEIVED);
+close_sock:
+ os_free(buf);
+ wpabuf_free(iface->afc_request);
+ iface->afc_request = 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 channel, chan_num;
+
+ oper_class = get_oper_class(NULL, iface->conf->op_class);
+ if (!oper_class)
+ return false;
+
+ switch (iface->conf->op_class) {
+ case 132: /* 40 MHz */
+ chan_num = 2;
+ break;
+ case 133: /* 80 MHz */
+ chan_num = 4;
+ break;
+ case 134: /* 160 MHz */
+ chan_num = 8;
+ break;
+ case 137: /* 320 MHz */
+ chan_num = 16;
+ break;
+ default:
+ chan_num = 1;
+ break;
+ }
+
+ for (channel = oper_class->min_chan; channel <= oper_class->max_chan;
+ channel += oper_class->inc) {
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ struct hostapd_channel_data *chan = NULL;
+ int i, start_ch = channel;
+
+ if (iface->conf->op_class == 137)
+ start_ch = channel - 30;
+
+ for (i = 0; i < chan_num; i++) {
+ chan = hw_get_channel_chan(mode, start_ch + i * 4,
+ NULL);
+
+ if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+ break;
+ }
+
+ if (i == chan_num)
+ 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;
+
+ iface->afc.request_id = os_random();
+ ret = hostapd_afc_send_receive(iface);
+ if (ret < 0) {
+ /*
+ * If the connection to the AFCD failed, reschedule 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;
+
+ ret = hostapd_is_usable_chans(iface);
+ if (ret != 1) {
+ /* Trigger an ACS freq scan */
+ iconf->channel = 0;
+ iface->freq = 0;
+ hostapd_set_and_check_bw320_offset(iface->conf, 0);
+
+ if (!ret && acs_init(iface) != HOSTAPD_CHAN_ACS) {
+ wpa_printf(MSG_ERROR, "AFC: 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)
+{
+ wpabuf_free(iface->afc_request);
+ os_free(iface->afc_response);
+ os_free(iface->afc.chan_info_list);
+ os_free(iface->afc.freq_range);
+
+ iface->afc_request = NULL;
+ iface->afc_response = NULL;
+
+ 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_afc_reset_channels(iface)) {
+ restart_iface = false;
+ goto restart_interface;
+ }
+
+ if (hostapd_is_usable_chans(iface) > 0) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
+ 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;
+ hostapd_set_and_check_bw320_offset(iface->conf, 0);
+ } else {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
+ }
+
+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_send_request(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
+ eloop_register_timeout(0, 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 = NULL;
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ mode->is_6ghz)
+ break;
+ }
+
+ if (i == iface->num_hw_features ||
+ !he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
+ !iface->afc.data_valid)
+ return;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ unsigned 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,
+ "AFC: Disabling freq=%d MHz (not allowed by AFC)",
+ chan->freq);
+ }
+}
+
+
+int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
+ int *power)
+{
+ unsigned int i;
+
+ if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
+ !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 = (int) (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) {
+ unsigned int j;
+
+ *power = 0;
+ for (j = 0; j < ARRAY_SIZE(c->power); j++)
+ *power = MAX(*power,
+ (int) (2 * c->power[j]));
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/contrib/wpa/src/ap/airtime_policy.c b/contrib/wpa/src/ap/airtime_policy.c
index 68443115f16b..003c4c1256d9 100644
--- a/contrib/wpa/src/ap/airtime_policy.c
+++ b/contrib/wpa/src/ap/airtime_policy.c
@@ -119,7 +119,12 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
static unsigned int get_airtime_quantum(unsigned int max_wt)
{
- unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
+ unsigned int quantum;
+
+ if (max_wt == 0)
+ return AIRTIME_QUANTUM_MAX;
+
+ quantum = AIRTIME_QUANTUM_TARGET / max_wt;
if (quantum < AIRTIME_QUANTUM_MIN)
quantum = AIRTIME_QUANTUM_MIN;
diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c
index 565b587166da..c5a5f3a917cf 100644
--- a/contrib/wpa/src/ap/ap_config.c
+++ b/contrib/wpa/src/ap/ap_config.c
@@ -170,6 +170,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
bss->test_assoc_comeback_type = -1;
+ bss->association_response_status_code = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_PASN
@@ -177,6 +178,13 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->pasn_comeback_after = 10;
bss->pasn_noauth = 1;
#endif /* CONFIG_PASN */
+ bss->urnm_mfpr_x20 = -1;
+ bss->urnm_mfpr = -1;
+#ifdef CONFIG_ENC_ASSOC
+ bss->assoc_frame_encryption = 0;
+ bss->pmksa_caching_privacy = 0;
+ bss->eap_using_authentication_frames = 0;
+#endif /* CONFIG_ENC_ASSOC */
}
@@ -306,6 +314,8 @@ struct hostapd_config * hostapd_config_defaults(void)
hostapd_set_and_check_bw320_offset(conf, 0);
+ conf->disable_mcs15_rx = true;
+
return conf;
}
@@ -491,34 +501,48 @@ int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
#ifdef CONFIG_SAE
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
+ int *groups = conf->sae_groups;
+ int default_groups[] = { 19, 0, 0 };
if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
!hostapd_sae_pw_id_in_use(conf) &&
- !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2) &&
!hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
- !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+ !wpa_key_mgmt_sae(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
return 0; /* PT not needed */
+ if (!groups) {
+ groups = default_groups;
+ if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
+ default_groups[1] = 20;
+ }
+
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
if (ssid->wpa_passphrase) {
- ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
- ssid->ssid_len,
+ ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
(const u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase),
- NULL);
+ NULL, 0);
if (!ssid->pt)
return -1;
}
for (pw = conf->sae_passwords; pw; pw = pw->next) {
sae_deinit_pt(pw->pt);
- pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
- ssid->ssid_len,
+ pw->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
(const u8 *) pw->password,
os_strlen(pw->password),
- pw->identifier);
+ (const u8 *) pw->identifier,
+ pw->identifier ?
+ os_strlen(pw->identifier) : 0);
if (!pw->pt)
return -1;
}
@@ -779,6 +803,8 @@ static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
#ifdef CONFIG_SAE_PK
sae_deinit_pk(tmp->pk);
#endif /* CONFIG_SAE_PK */
+ os_free(tmp->success_mac);
+ os_free(tmp->fail_mac);
os_free(tmp);
}
}
@@ -865,6 +891,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->radius_das_shared_secret);
hostapd_config_free_vlan(conf);
os_free(conf->time_zone);
+ os_free(conf->supported_rates);
+ os_free(conf->basic_rates);
#ifdef CONFIG_IEEE80211R_AP
hostapd_config_clear_rxkhs(conf);
@@ -917,31 +945,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->hs20_wan_metrics);
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
- os_free(conf->hs20_icons);
- if (conf->hs20_osu_providers) {
- for (i = 0; i < conf->hs20_osu_providers_count; i++) {
- struct hs20_osu_provider *p;
- size_t j;
- p = &conf->hs20_osu_providers[i];
- os_free(p->friendly_name);
- os_free(p->server_uri);
- os_free(p->method_list);
- for (j = 0; j < p->icons_count; j++)
- os_free(p->icons[j]);
- os_free(p->icons);
- os_free(p->osu_nai);
- os_free(p->osu_nai2);
- os_free(p->service_desc);
- }
- os_free(conf->hs20_osu_providers);
- }
- if (conf->hs20_operator_icon) {
- for (i = 0; i < conf->hs20_operator_icon_count; i++)
- os_free(conf->hs20_operator_icon[i]);
- os_free(conf->hs20_operator_icon);
- }
- os_free(conf->subscr_remediation_url);
- os_free(conf->hs20_sim_provisioning_url);
os_free(conf->t_c_filename);
os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
@@ -960,6 +963,11 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(conf->own_ie_override);
+ wpabuf_free(conf->rsne_override);
+ wpabuf_free(conf->rsnoe_override);
+ wpabuf_free(conf->rsno2e_override);
+ wpabuf_free(conf->rsnxe_override);
+ wpabuf_free(conf->rsnxoe_override);
wpabuf_free(conf->sae_commit_override);
wpabuf_free(conf->rsne_override_eapol);
wpabuf_free(conf->rsnxe_override_eapol);
@@ -1008,12 +1016,33 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
#ifdef CONFIG_PASN
os_free(conf->pasn_groups);
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(conf->pasn_test_groups);
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
+ wpabuf_clear_free(conf->sae_pw_id_key);
+
os_free(conf);
}
+#ifdef CONFIG_AFC
+void hostapd_config_free_afc_cert_ids(struct hostapd_config *conf)
+{
+ unsigned int i;
+
+ for (i = 0; i < conf->afc.n_cert_ids; i++) {
+ os_free(conf->afc.cert_ids[i].ruleset);
+ os_free(conf->afc.cert_ids[i].id);
+ }
+ os_free(conf->afc.cert_ids);
+ conf->afc.cert_ids = NULL;
+ conf->afc.n_cert_ids = 0;
+}
+#endif /* CONFIG_AFC */
+
+
/**
* hostapd_config_free - Free hostapd configuration
* @conf: Configuration data from hostapd_config_read().
@@ -1028,8 +1057,6 @@ void hostapd_config_free(struct hostapd_config *conf)
for (i = 0; i < conf->num_bss; i++)
hostapd_config_free_bss(conf->bss[i]);
os_free(conf->bss);
- os_free(conf->supported_rates);
- os_free(conf->basic_rates);
os_free(conf->acs_ch_list.range);
os_free(conf->acs_freq_list.range);
os_free(conf->driver_params);
@@ -1038,6 +1065,18 @@ 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.sn);
+ hostapd_config_free_afc_cert_ids(conf);
+ 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.freqs.range);
+ os_free(conf->afc.op_class);
+#endif /* CONFIG_AFC */
os_free(conf);
}
@@ -1079,21 +1118,6 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
}
-int hostapd_rate_found(int *list, int rate)
-{
- int i;
-
- if (list == NULL)
- return 0;
-
- for (i = 0; list[i] >= 0; i++)
- if (list[i] == rate)
- return 1;
-
- return 0;
-}
-
-
int hostapd_vlan_valid(struct hostapd_vlan *vlan,
struct vlan_description *vlan_desc)
{
@@ -1204,7 +1228,7 @@ static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
#endif /* CONFIG_SAE_PK */
-static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
+bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
{
if (bss->wpa != WPA_PROTO_RSN) {
wpa_printf(MSG_ERROR,
@@ -1324,6 +1348,22 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
}
}
+ if (full_config) {
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (conf->bss[i] != bss &&
+ os_strcmp(conf->bss[i]->iface, bss->iface) == 0) {
+ wpa_printf(MSG_ERROR,
+ "Duplicate interface '%s' for BSSID "
+ MACSTR " and " MACSTR, bss->iface,
+ MAC2STR(conf->bss[i]->bssid),
+ MAC2STR(bss->bssid));
+ return -1;
+ }
+ }
+ }
+
#ifdef CONFIG_IEEE80211R_AP
if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
(bss->nas_identifier == NULL ||
@@ -1491,9 +1531,31 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
#ifdef CONFIG_IEEE80211BE
if (full_config && !bss->disable_11be && bss->disable_11ax) {
bss->disable_11be = true;
+ bss->mld_ap = 0;
wpa_printf(MSG_INFO,
"Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
}
+
+ if (full_config && conf->ieee80211be && !bss->disable_11be &&
+ !bss->beacon_prot && ap_pmf_enabled(bss)) {
+ bss->beacon_prot = 1;
+ wpa_printf(MSG_INFO,
+ "Enabling beacon protection as IEEE 802.11be is enabled for this BSS");
+ }
+
+ if (full_config && (!conf->ieee80211be || bss->disable_11be) &&
+ bss->mld_ap) {
+ wpa_printf(MSG_INFO,
+ "Cannot enable mld_ap when IEEE 802.11be is disabled");
+ return -1;
+ }
+
+ if (full_config && (!conf->ieee80211be || bss->disable_11be) &&
+ bss->bss_require_eht) {
+ bss->bss_require_eht = false;
+ wpa_printf(MSG_INFO,
+ "Disabling bss_require_eht as IEEE 802.11be is disabled for this BSS");
+ }
#endif /* CONFIG_IEEE80211BE */
if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
@@ -1502,6 +1564,13 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
return -1;
}
+ /* Do not advertise SPP A-MSDU support if not using CCMP/GCMP */
+ if (full_config && bss->spp_amsdu &&
+ !(bss->wpa &&
+ bss->rsn_pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+ WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+ bss->spp_amsdu = false;
+
return 0;
}
@@ -1668,11 +1737,6 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss,
if (full_config)
bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
#endif /* CONFIG_WEP */
- } else if (bss->osen) {
- bss->ssid.security_policy = SECURITY_OSEN;
- bss->wpa_group = WPA_CIPHER_CCMP;
- bss->wpa_pairwise = 0;
- bss->rsn_pairwise = WPA_CIPHER_CCMP;
} else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
if (full_config) {
diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h
index ced2181ab8ae..726262c8170b 100644
--- a/contrib/wpa/src/ap/ap_config.h
+++ b/contrib/wpa/src/ap/ap_config.h
@@ -93,7 +93,6 @@ typedef enum hostap_security_policy {
SECURITY_IEEE_802_1X = 2,
SECURITY_WPA_PSK = 3,
SECURITY_WPA = 4,
- SECURITY_OSEN = 5
} secpolicy;
struct hostapd_ssid {
@@ -189,7 +188,6 @@ struct hostapd_eap_user {
unsigned int wildcard_prefix:1;
unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */
- unsigned int remediation:1;
unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
struct hostapd_radius_attr *accept_attr;
@@ -260,6 +258,10 @@ struct sae_password_entry {
int vlan_id;
struct sae_pt *pt;
struct sae_pk *pk;
+ u8 *success_mac;
+ unsigned int num_success_mac, next_success_mac;
+ u8 *fail_mac;
+ unsigned int num_fail_mac, next_fail_mac;
};
struct dpp_controller_conf {
@@ -293,6 +295,9 @@ struct hostapd_bss_config {
int max_num_sta; /* maximum number of STAs in station table */
+ enum beacon_rate_type rate_type;
+ unsigned int beacon_rate;
+
int dtim_period;
unsigned int bss_load_update_period;
unsigned int chan_util_avg_period;
@@ -325,6 +330,9 @@ struct hostapd_bss_config {
struct hostapd_ssid ssid;
+ int *supported_rates;
+ int *basic_rates;
+
char *eap_req_id_text; /* optional displayable message sent with
* EAP Request-Identity */
size_t eap_req_id_text_len;
@@ -358,7 +366,11 @@ struct hostapd_bss_config {
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
int extended_key_id;
int wpa_key_mgmt;
+ int rsn_override_key_mgmt;
+ int rsn_override_key_mgmt_2;
enum mfp_options ieee80211w;
+ enum mfp_options rsn_override_mfp;
+ enum mfp_options rsn_override_mfp_2;
int group_mgmt_cipher;
int beacon_prot;
/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
@@ -387,9 +399,14 @@ struct hostapd_bss_config {
u32 wpa_pairwise_update_count;
int wpa_disable_eapol_key_retries;
int rsn_pairwise;
+ int rsn_override_pairwise;
+ int rsn_override_pairwise_2;
int rsn_preauth;
char *rsn_preauth_interfaces;
+ int rsn_override_omit_rsnxe;
+ int rsn_override_mlo_compat;
+
#ifdef CONFIG_IEEE80211R_AP
/* IEEE 802.11r - Fast BSS Transition */
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
@@ -443,7 +460,6 @@ struct hostapd_bss_config {
int pac_key_lifetime;
int pac_key_refresh_time;
int eap_teap_auth;
- int eap_teap_pac_no_inner;
int eap_teap_separate_result;
int eap_teap_id;
int eap_teap_method_sequence;
@@ -458,6 +474,7 @@ struct hostapd_bss_config {
char *radius_server_clients;
int radius_server_auth_port;
int radius_server_acct_port;
+ int radius_server_acct_log;
int radius_server_ipv6;
int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
@@ -554,6 +571,10 @@ struct hostapd_bss_config {
bool disable_11ax;
bool disable_11be;
+#ifdef CONFIG_IEEE80211BE
+ bool bss_require_eht;
+#endif /* CONFIG_IEEE80211BE */
+
/* IEEE 802.11v */
int time_advertisement;
char *time_zone;
@@ -613,7 +634,6 @@ struct hostapd_bss_config {
u8 qos_map_set[16 + 2 * 21];
unsigned int qos_map_set_len;
- int osen;
int proxy_arp;
int na_mcast_to_ucast;
@@ -629,37 +649,7 @@ struct hostapd_bss_config {
size_t hs20_connection_capability_len;
u8 *hs20_operating_class;
u8 hs20_operating_class_len;
- struct hs20_icon {
- u16 width;
- u16 height;
- char language[3];
- char type[256];
- char name[256];
- char file[256];
- } *hs20_icons;
- size_t hs20_icons_count;
- u8 osu_ssid[SSID_MAX_LEN];
- size_t osu_ssid_len;
- struct hs20_osu_provider {
- unsigned int friendly_name_count;
- struct hostapd_lang_string *friendly_name;
- char *server_uri;
- int *method_list;
- char **icons;
- size_t icons_count;
- char *osu_nai;
- char *osu_nai2;
- unsigned int service_desc_count;
- struct hostapd_lang_string *service_desc;
- } *hs20_osu_providers, *last_osu;
- size_t hs20_osu_providers_count;
- size_t hs20_osu_providers_nai_count;
- char **hs20_operator_icon;
- size_t hs20_operator_icon_count;
unsigned int hs20_deauth_req_timeout;
- char *subscr_remediation_url;
- u8 subscr_remediation_method;
- char *hs20_sim_provisioning_url;
char *t_c_filename;
u32 t_c_timestamp;
char *t_c_server_url;
@@ -679,8 +669,18 @@ struct hostapd_bss_config {
int sae_require_mfp;
int sae_confirm_immediate;
enum sae_pwe sae_pwe;
+ /* Accept an SAE association even when the STA advertises SAE H2E
+ * support in the RSNXE but authenticated using hunting-and-pecking.
+ * This is a workaround for WPA3-Personal Compatibility Mode and
+ * RSNXE-override interoperability (e.g., some MLO STAs that add only
+ * the RSNXE Override element and do not use H2E on 2.4/5 GHz) */
+ bool sae_accept_h2e_without_use;
int *sae_groups;
struct sae_password_entry *sae_passwords;
+ int sae_password_psk;
+ int sae_track_password;
+ struct wpabuf *sae_pw_id_key;
+ unsigned int sae_pw_id_num;
char *wowlan_triggers; /* Wake-on-WLAN triggers */
@@ -688,6 +688,11 @@ struct hostapd_bss_config {
u8 bss_load_test[5];
u8 bss_load_test_set;
struct wpabuf *own_ie_override;
+ struct wpabuf *rsne_override;
+ struct wpabuf *rsnoe_override;
+ struct wpabuf *rsno2e_override;
+ struct wpabuf *rsnxe_override;
+ struct wpabuf *rsnxoe_override;
int sae_reflection_attack;
int sae_commit_status;
int sae_pk_omit;
@@ -712,8 +717,10 @@ struct hostapd_bss_config {
struct wpabuf *eapol_m1_elements;
struct wpabuf *eapol_m3_elements;
bool eapol_m3_no_encrypt;
+ bool eapol_key_reserved_random;
int test_assoc_comeback_type;
struct wpabuf *presp_elements;
+ int association_response_status_code;
#ifdef CONFIG_IEEE80211BE
u16 eht_oper_puncturing_override;
@@ -906,6 +913,14 @@ struct hostapd_bss_config {
int macsec_csindex;
/**
+ * macsec_icv_indicator - Always include ICV Indicator
+ * (for compatibility with older MACsec switches)
+ *
+ * Range: 0-1 (default: 0)
+ */
+ int macsec_icv_indicator;
+
+ /**
* mka_ckn - MKA pre-shared CKN
*/
#define MACSEC_CKN_MAX_LEN 32
@@ -932,6 +947,11 @@ struct hostapd_bss_config {
/* Whether to allow PASN-UNAUTH */
int pasn_noauth;
+#ifdef CONFIG_ENC_ASSOC
+ /* Whether to allow unauthenticated EPPKE (EPPKE without base AKM) */
+ bool eppke_unauth;
+#endif /* CONFIG_ENC_ASSOC */
+
#ifdef CONFIG_TESTING_OPTIONS
/*
* Normally, KDK should be derived if and only if both sides support
@@ -941,6 +961,12 @@ struct hostapd_bss_config {
/* If set, corrupt the MIC in the 2nd Authentication frame of PASN */
int pasn_corrupt_mic;
+
+ /*
+ * If set, override Supported Groups element in the 2nd Authentication
+ * frame of PASN for group negotiation testing.
+ */
+ int *pasn_test_groups;
#endif /* CONFIG_TESTING_OPTIONS */
int *pasn_groups;
@@ -952,6 +978,9 @@ struct hostapd_bss_config {
u16 pasn_comeback_after;
#endif /* CONFIG_PASN */
+ int urnm_mfpr_x20;
+ int urnm_mfpr;
+
unsigned int unsol_bcast_probe_resp_interval;
u8 ext_capa_mask[EXT_CAPA_MAX_LEN];
@@ -959,9 +988,9 @@ struct hostapd_bss_config {
u8 rnr;
char *config_id;
- bool xrates_supported;
bool ssid_protection;
+ bool known_sta_identification;
#ifdef CONFIG_IEEE80211BE
/* The AP is part of an AP MLD */
@@ -981,6 +1010,14 @@ struct hostapd_bss_config {
bool mld_indicate_disabled;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
+ int mbssid_index;
+
+ bool spp_amsdu;
+#ifdef CONFIG_ENC_ASSOC
+ unsigned int assoc_frame_encryption:1;
+ unsigned int pmksa_caching_privacy:1;
+ unsigned int eap_using_authentication_frames:1;
+#endif /* CONFIG_ENC_ASSOC */
};
/**
@@ -1057,11 +1094,6 @@ struct hostapd_config {
SHORT_PREAMBLE = 1
} preamble;
- int *supported_rates;
- int *basic_rates;
- unsigned int beacon_rate;
- enum beacon_rate_type rate_type;
-
const struct wpa_driver_ops *driver;
char *driver_params;
@@ -1139,6 +1171,7 @@ struct hostapd_config {
double ignore_reassoc_probability;
double corrupt_gtk_rekey_mic_probability;
int ecsa_ie_only;
+ int csa_ie_only;
bool delay_eapol_tx;
#endif /* CONFIG_TESTING_OPTIONS */
@@ -1220,6 +1253,7 @@ struct hostapd_config {
u8 punct_acs_threshold;
u8 eht_default_pe_duration;
u8 eht_bw320_offset;
+ bool require_eht;
#endif /* CONFIG_IEEE80211BE */
/* EHT enable/disable config from CHAN_SWITCH */
@@ -1232,9 +1266,60 @@ struct hostapd_config {
MBSSID_ENABLED = 1,
ENHANCED_MBSSID_ENABLED = 2,
} mbssid;
+ unsigned int mbssid_max;
/* Whether to enable TWT responder in HT and VHT modes */
bool ht_vht_twt_responder;
+
+ bool channel_usage;
+ bool peer_to_peer_twt;
+
+ /* Set I2R LMR policy to allow LMR response from ISTA */
+ bool i2r_lmr_policy;
+
+ /* Disable MCS15 Subfield in EHT operation element */
+ bool disable_mcs15_rx;
+
+#ifdef CONFIG_AFC
+ struct {
+ char *socket;
+ struct {
+ char *version;
+ char *sn;
+ } request;
+ unsigned int n_cert_ids;
+ struct afc_cert_id {
+ char *ruleset;
+ char *id;
+ } *cert_ids;
+ struct {
+ enum afc_location_type {
+ AFC_ELLIPSE,
+ AFC_LINEAR_POLYGON,
+ AFC_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;
+ struct wpa_freq_range_list freqs;
+ int *op_class; /* int array */
+ int min_power;
+ } afc;
+#endif /* CONFIG_AFC */
};
@@ -1373,7 +1458,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, struct vlan_description *vlan_id);
-int hostapd_rate_found(int *list, int rate);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk, int *vlan_id);
@@ -1397,5 +1481,7 @@ int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
int vlan_id, const u8 *addr);
void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
const u8 *addr);
+bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss);
+void hostapd_config_free_afc_cert_ids(struct hostapd_config *conf);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c
index c47349110e5f..4e4c637d5903 100644
--- a/contrib/wpa/src/ap/ap_drv_ops.c
+++ b/contrib/wpa/src/ap/ap_drv_ops.c
@@ -21,6 +21,7 @@
#include "p2p_hostapd.h"
#include "hs20.h"
#include "wpa_auth.h"
+#include "hw_features.h"
#include "ap_drv_ops.h"
@@ -39,6 +40,8 @@ u32 hostapd_sta_flags_to_drv(u32 flags)
res |= WPA_STA_AUTHENTICATED;
if (flags & WLAN_STA_ASSOC)
res |= WPA_STA_ASSOCIATED;
+ if (flags & WLAN_STA_SPP_AMSDU)
+ res |= WPA_STA_SPP_AMSDU;
return res;
}
@@ -116,9 +119,11 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
goto fail;
#endif /* CONFIG_FILS */
- pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
- if (add_buf_data(&assocresp, buf, pos - buf) < 0)
- goto fail;
+ if (!hapd->conf->rsn_override_omit_rsnxe) {
+ pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
+ if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+ goto fail;
+ }
if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
@@ -181,11 +186,6 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
add_buf_data(&proberesp, buf, pos - buf) < 0)
goto fail;
-
- pos = hostapd_eid_osen(hapd, buf);
- if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
- add_buf_data(&proberesp, buf, pos - buf) < 0)
- goto fail;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
@@ -370,7 +370,14 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
{
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
- return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+ const u8 *addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ addr = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, addr,
NULL, NULL, force_ifname, if_addr, NULL, 0);
}
@@ -472,7 +479,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
- int set, const u8 *link_addr, bool mld_link_sta)
+ int set, const u8 *link_addr, bool mld_link_sta,
+ u16 eml_cap, bool epp_sta)
{
struct hostapd_sta_add_params params;
@@ -502,6 +510,9 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.support_p2p_ps = supp_p2p_ps;
params.set = set;
params.mld_link_id = -1;
+#ifdef CONFIG_ENC_ASSOC
+ params.epp_sta = epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_IEEE80211BE
/*
@@ -512,6 +523,9 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.mld_link_id = hapd->mld_link_id;
params.mld_link_addr = link_addr;
params.mld_link_sta = mld_link_sta;
+ /* Copy EML capabilities of ML STA */
+ if (link_addr)
+ params.eml_cap = eml_cap;
}
#endif /* CONFIG_IEEE80211BE */
@@ -519,16 +533,6 @@ int hostapd_sta_add(struct hostapd_data *hapd,
}
-int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
- u8 *tspec_ie, size_t tspec_ielen)
-{
- if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
- return 0;
- return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
- tspec_ielen);
-}
-
-
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
{
if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
@@ -754,15 +758,7 @@ hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
!hapd->drv_priv)
return NULL;
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
- flags, dfs_domain);
-}
-
-
-int hostapd_driver_commit(struct hostapd_data *hapd)
-{
- if (hapd->driver == NULL || hapd->driver->commit == NULL)
- return 0;
- return hapd->driver->commit(hapd->drv_priv);
+ flags, dfs_domain, NULL, 0);
}
@@ -781,6 +777,37 @@ bool hostapd_drv_nl80211(struct hostapd_data *hapd)
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params)
{
+ params->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ params->link_id = hapd->mld_link_id;
+
+ if (!hapd->iface->scan_cb && hapd->conf->mld_ap &&
+ hapd->iface->interfaces) {
+ /* Other links may be waiting for scan results */
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h_iface =
+ hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd;
+
+ if (!h_iface || h_iface == hapd->iface ||
+ h_iface->num_bss == 0)
+ continue;
+
+ h_hapd = h_iface->bss[0];
+
+ if (hostapd_is_ml_partner(hapd, h_hapd) &&
+ h_hapd->iface->state == HAPD_IFACE_ACS) {
+ wpa_printf(MSG_INFO,
+ "ACS in progress in a partner link - try to scan later");
+ return -EBUSY;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (hapd->driver && hapd->driver->scan2)
return hapd->driver->scan2(hapd->drv_priv, params);
return -1;
@@ -889,20 +916,23 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
const u8 *addr, int reason)
{
const u8 *own_addr = hapd->own_addr;
+ int link_id = -1;
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap) {
struct sta_info *sta = ap_get_sta(hapd, addr);
- if (ap_sta_is_mld(hapd, sta))
+ if (ap_sta_is_mld(hapd, sta)) {
own_addr = hapd->mld->mld_addr;
+ link_id = hapd->mld_link_id;
+ }
}
#endif /* CONFIG_IEEE80211BE */
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
return 0;
return hapd->driver->sta_disassoc(hapd->drv_priv, own_addr, addr,
- reason);
+ reason, link_id);
}
@@ -916,9 +946,50 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
}
+#ifdef CONFIG_IEEE80211BE
+static bool hostapd_is_action_frame_link_agnostic(u8 category, u8 sub_category)
+{
+ /* As per IEEE Std 802.11be-2024, 35.3.14 (MLD individually addressed
+ * Management frame delivery), between an AP MLD and a non-AP MLD, the
+ * following individually addressed MMPDUs shall be intended for an MLD.
+ */
+ switch (category) {
+ case WLAN_ACTION_BLOCK_ACK:
+ case WLAN_ACTION_FT:
+ case WLAN_ACTION_SA_QUERY:
+ case WLAN_ACTION_WNM:
+ switch (sub_category) {
+ case WNM_BSS_TRANS_MGMT_REQ:
+ case WNM_BSS_TRANS_MGMT_RESP:
+ case WNM_SLEEP_MODE_REQ:
+ case WNM_SLEEP_MODE_RESP:
+ return true;
+ default:
+ return false;
+ }
+ case WLAN_ACTION_ROBUST_AV_STREAMING:
+ switch (sub_category) {
+ case ROBUST_AV_SCS_REQ:
+ case ROBUST_AV_SCS_RESP:
+ case ROBUST_AV_MSCS_REQ:
+ case ROBUST_AV_MSCS_RESP:
+ return true;
+ default:
+ return false;
+ }
+ /* TODO: Handle EHT/EPCS related action frames once the support is
+ * added. */
+ default:
+ return false;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
unsigned int wait, const u8 *dst,
- const u8 *data, size_t len, bool addr3_ap)
+ const u8 *data, size_t len, bool addr3_ap,
+ const u8 *forced_a3)
{
const u8 *own_addr = hapd->own_addr;
const u8 *bssid;
@@ -926,12 +997,15 @@ static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
struct sta_info *sta;
+ int link_id = -1;
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
return 0;
bssid = hapd->own_addr;
- if (!addr3_ap && !is_multicast_ether_addr(dst) &&
- len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+ if (forced_a3) {
+ bssid = forced_a3;
+ } else if (!addr3_ap && !is_multicast_ether_addr(dst) &&
+ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
/*
* Public Action frames to a STA that is not a member of the BSS
* shall use wildcard BSSID value.
@@ -956,11 +1030,15 @@ static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
own_addr = hapd->mld->mld_addr;
bssid = own_addr;
}
+
+ if (!hostapd_is_action_frame_link_agnostic(data[0], data[1]))
+ link_id = hapd->mld_link_id;
#endif /* CONFIG_IEEE80211BE */
}
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- own_addr, bssid, data, len, 0);
+ own_addr, bssid, data, len, 0,
+ link_id);
}
@@ -968,7 +1046,8 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
unsigned int wait, const u8 *dst, const u8 *data,
size_t len)
{
- return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false);
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+ NULL);
}
@@ -977,7 +1056,19 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
unsigned int wait, const u8 *dst,
const u8 *data, size_t len)
{
- return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true,
+ NULL);
+}
+
+
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *a3,
+ const u8 *data, size_t len)
+{
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+ a3);
}
@@ -1018,6 +1109,12 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
}
data.radar_background = radar_background;
+ data.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ data.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
if (!res) {
if (radar_background)
@@ -1049,6 +1146,10 @@ void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
{
int i;
bool is_no_ir = false;
+ bool allow_6g_acs = is_6ghz_op_class(hapd->iconf->op_class) &&
+ hostapd_config_check_bss_6g(hapd->conf) &&
+ (hapd->iface->conf->ieee80211ax ||
+ hapd->iface->conf->ieee80211be);
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
@@ -1069,8 +1170,7 @@ void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
if (is_6ghz_freq(chan->freq) &&
((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
!is_6ghz_psc_frequency(chan->freq)) ||
- (!hapd->iface->conf->ieee80211ax &&
- !hapd->iface->conf->ieee80211be)))
+ !allow_6g_acs))
continue;
if ((!(chan->flag & HOSTAPD_CHAN_DISABLED) || allow_disabled) &&
!(hapd->iface->conf->acs_exclude_dfs &&
@@ -1133,8 +1233,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
params.hw_mode = hapd->iface->conf->hw_mode;
params.link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
- !hapd->conf->disable_11be)
+ if (hapd->conf->mld_ap)
params.link_id = hapd->mld_link_id;
#endif /* CONFIG_IEEE80211BE */
@@ -1250,3 +1349,60 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
}
#endif /* CONFIG_PASN */
+
+
+struct hostapd_multi_hw_info *
+hostapd_get_multi_hw_info(struct hostapd_data *hapd,
+ unsigned int *num_multi_hws)
+{
+ if (!hapd->driver || !hapd->driver->get_multi_hw_info)
+ return NULL;
+
+ return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws);
+}
+
+
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+ struct wpa_pmkid_params *params)
+{
+ if (!hapd->driver || !hapd->driver->add_pmkid || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->add_pmkid(hapd->drv_priv, params);
+}
+
+
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid, int akmp)
+{
+ struct wpa_pmkid_params params;
+
+ os_memset(&params, 0, sizeof(params));
+ params.bssid = bssid;
+ params.pmkid = pmkid;
+ params.pmk = pmk;
+ params.pmk_len = pmk_len;
+
+ return hostapd_drv_add_pmkid(hapd, &params);
+}
+
+
+static int hostapd_drv_remove_pmkid(struct hostapd_data *hapd,
+ struct wpa_pmkid_params *params)
+{
+ if (!hapd->driver || !hapd->driver->remove_pmkid || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->remove_pmkid(hapd->drv_priv, params);
+}
+
+
+int hostapd_remove_pmkid(struct hostapd_data *hapd, const u8 *sta_addr,
+ const u8 *pmkid)
+{
+ struct wpa_pmkid_params params;
+
+ os_memset(&params, 0, sizeof(params));
+ params.bssid = sta_addr;
+ params.pmkid = pmkid;
+
+ return hostapd_drv_remove_pmkid(hapd, &params);
+}
diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h
index d7e79c840cb8..66913ed0fca7 100644
--- a/contrib/wpa/src/ap/ap_drv_ops.h
+++ b/contrib/wpa/src/ap/ap_drv_ops.h
@@ -49,7 +49,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
- int set, const u8 *link_addr, bool mld_link_sta);
+ int set, const u8 *link_addr, bool mld_link_sta,
+ u16 eml_cap, bool epp_sta);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
@@ -86,7 +87,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags, u8 *dfs_domain);
-int hostapd_driver_commit(struct hostapd_data *hapd);
int hostapd_drv_none(struct hostapd_data *hapd);
bool hostapd_drv_nl80211(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
@@ -116,6 +116,11 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
unsigned int freq,
unsigned int wait, const u8 *dst,
const u8 *data, size_t len);
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *a3,
+ const u8 *data, size_t len);
static inline void
hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
{
@@ -130,8 +135,6 @@ int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
u16 seq, u16 status, const u8 *ie, size_t len);
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
int reassoc, u16 status, const u8 *ie, size_t len);
-int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
- u8 *tspec_ie, size_t tspec_ielen);
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
enum hostapd_hw_mode mode, int freq,
int channel, int ht_enabled, int vht_enabled,
@@ -267,14 +270,6 @@ static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
}
-static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
- int auth_algs)
-{
- if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
- return 0;
- return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
-}
-
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
const u8 *own_addr, const u8 *addr,
int qos)
@@ -393,7 +388,7 @@ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
{
- int link_id = -1;
+ int link_id = -1, ret;
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
return 0;
@@ -401,7 +396,13 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
if (hapd->conf->mld_ap)
link_id = hapd->mld_link_id;
#endif /* CONFIG_IEEE80211BE */
- return hapd->driver->stop_ap(hapd->drv_priv, link_id);
+
+ ret = hapd->driver->stop_ap(hapd->drv_priv, link_id);
+ if (ret)
+ return ret;
+
+ hapd->beacon_set_done = 0;
+ return 0;
}
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
@@ -478,4 +479,15 @@ static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211BE */
+struct hostapd_multi_hw_info *
+hostapd_get_multi_hw_info(struct hostapd_data *hapd,
+ unsigned int *num_multi_hws);
+
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+ struct wpa_pmkid_params *params);
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid, int akmp);
+int hostapd_remove_pmkid(struct hostapd_data *hapd, const u8 *sta_addr,
+ const u8 *pmkid);
+
#endif /* AP_DRV_OPS */
diff --git a/contrib/wpa/src/ap/ap_mlme.c b/contrib/wpa/src/ap/ap_mlme.c
index 309e69a3f95d..608b99213cd1 100644
--- a/contrib/wpa/src/ap/ap_mlme.c
+++ b/contrib/wpa/src/ap/ap_mlme.c
@@ -112,7 +112,9 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->auth_alg != WLAN_AUTH_FT &&
sta->auth_alg != WLAN_AUTH_FILS_SK &&
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
- sta->auth_alg != WLAN_AUTH_FILS_PK)
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ sta->auth_alg != WLAN_AUTH_EPPKE &&
+ sta->auth_alg != WLAN_AUTH_802_1X)
mlme_deletekeys_request(hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
}
@@ -140,7 +142,9 @@ void mlme_reassociate_indication(struct hostapd_data *hapd,
if (sta->auth_alg != WLAN_AUTH_FT &&
sta->auth_alg != WLAN_AUTH_FILS_SK &&
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
- sta->auth_alg != WLAN_AUTH_FILS_PK)
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ sta->auth_alg != WLAN_AUTH_EPPKE &&
+ sta->auth_alg != WLAN_AUTH_802_1X)
mlme_deletekeys_request(hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
}
diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c
index 837b6909bde5..3e8031810f45 100644
--- a/contrib/wpa/src/ap/authsrv.c
+++ b/contrib/wpa/src/ap/authsrv.c
@@ -14,6 +14,7 @@
#include "eap_server/eap.h"
#include "eap_server/eap_sim_db.h"
#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius.h"
#include "radius/radius_server.h"
#include "hostapd.h"
#include "ap_config.h"
@@ -89,7 +90,6 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
- user->remediation = eap_user->remediation;
user->accept_attr = eap_user->accept_attr;
user->t_c_timestamp = eap_user->t_c_timestamp;
rv = 0;
@@ -102,6 +102,114 @@ out:
}
+/**
+ * hostapd_radius_log_acct_req - Callback for logging received RADIUS
+ * accounting requests
+ * @ctx: Context (struct hostapd_data)
+ * @msg: Received RADIUS accounting request
+ * @status_type: Status type from the message (parsed Acct-Status-Type
+ * attribute)
+ * Returns: 0 on success, -1 on failure
+ */
+static int hostapd_radius_log_acct_req(void *ctx, struct radius_msg *msg,
+ u32 status_type)
+{
+ char nas_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+ char session_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+ char username[RADIUS_MAX_ATTR_LEN + 1] = "";
+ char calling_station_id[3 * ETH_ALEN] = "";
+ u32 session_time = 0, terminate_cause = 0,
+ bytes_in = 0, bytes_out = 0,
+ packets_in = 0, packets_out = 0,
+ gigawords_in = 0, gigawords_out = 0;
+ unsigned long long total_bytes_in = 0, total_bytes_out = 0;
+
+ /* Parse NAS identification (required by RFC 2866, section 4.1) */
+ if (radius_msg_get_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) nas_id,
+ sizeof(nas_id) - 1))
+ nas_id[0] = '\0';
+
+ /* Process Accounting-On and Accounting-Off messages separately */
+ if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON ||
+ status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
+ wpa_printf(MSG_INFO, "RADIUS ACCT: NAS='%s' status='%s'",
+ nas_id,
+ status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON
+ ? "Accounting-On" : "Accounting-Off");
+ return 0;
+ }
+
+ /* Parse session ID (required by RFC 2866, section 5.5) */
+ if (radius_msg_get_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) session_id,
+ sizeof(session_id) - 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS ACCT: request doesn't include session ID");
+ return -1;
+ }
+
+ /* Parse user name */
+ radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) username,
+ sizeof(username) - 1);
+
+ /* Parse device identifier */
+ radius_msg_get_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) calling_station_id,
+ sizeof(calling_station_id) - 1);
+
+ switch (status_type) {
+ case RADIUS_ACCT_STATUS_TYPE_START:
+ wpa_printf(MSG_INFO,
+ "RADIUS ACCT: NAS='%s' session='%s' status='Accounting-Start' station='%s' username='%s'",
+ nas_id, session_id, calling_station_id, username);
+ break;
+ case RADIUS_ACCT_STATUS_TYPE_STOP:
+ case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
+ /* Parse counters */
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+ &session_time);
+ radius_msg_get_attr_int32(msg,
+ RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ &terminate_cause);
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS,
+ &bytes_in);
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+ &bytes_out);
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS,
+ &packets_in);
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+ &packets_out);
+ radius_msg_get_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+ &gigawords_in);
+ radius_msg_get_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+ &gigawords_out);
+
+ /* RFC 2869, section 5.1 and 5.2 */
+ total_bytes_in = ((u64) gigawords_in << 32) + bytes_in;
+ total_bytes_out = ((u64) gigawords_out << 32) + bytes_out;
+
+ wpa_printf(MSG_INFO,
+ "RADIUS ACCT: NAS='%s' session='%s' status='%s' station='%s' username='%s' session_time=%u term_cause=%u pck_in=%u pck_out=%u bytes_in=%llu bytes_out=%llu",
+ nas_id, session_id,
+ status_type == RADIUS_ACCT_STATUS_TYPE_STOP ?
+ "Accounting-Stop" : "Accounting-Interim-Update",
+ calling_station_id, username, session_time,
+ terminate_cause, packets_in, packets_out,
+ total_bytes_in, total_bytes_out);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "RADIUS ACCT: Unknown request status type %u",
+ status_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
{
struct radius_server_conf srv;
@@ -129,6 +237,8 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
srv.conf_ctx = hapd;
srv.ipv6 = conf->radius_server_ipv6;
srv.get_eap_user = hostapd_radius_get_eap_user;
+ if (conf->radius_server_acct_log)
+ srv.acct_req_cb = hostapd_radius_log_acct_req;
srv.eap_req_id_text = conf->eap_req_id_text;
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
srv.sqlite_file = conf->eap_user_sqlite;
@@ -136,9 +246,6 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
srv.dump_msk_file = conf->dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
#ifdef CONFIG_HS20
- srv.subscr_remediation_url = conf->subscr_remediation_url;
- srv.subscr_remediation_method = conf->subscr_remediation_method;
- srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
srv.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */
srv.erp_domain = conf->erp_domain;
@@ -224,7 +331,6 @@ static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd)
cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
- cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
cfg->eap_teap_id = hapd->conf->eap_teap_id;
cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c
index cec0c9829fd9..1a0ac922e9e9 100644
--- a/contrib/wpa/src/ap/beacon.c
+++ b/contrib/wpa/src/ap/beacon.c
@@ -3,6 +3,7 @@
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -262,32 +263,47 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
{
u8 *pos = eid;
u8 *end = eid + max_len;
+ u8 op_class;
+ bool force_global;
if (!hapd->iconf->ieee80211d || max_len < 6 ||
hapd->iface->current_mode == NULL)
return eid;
+ op_class = hostapd_get_oper_class_of_bss(hapd);
+
*pos++ = WLAN_EID_COUNTRY;
pos++; /* length will be set later */
os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
pos += 3;
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ /* The 6 GHz band uses global operating classes */
+ force_global = is_6ghz_op_class(op_class);
+
+#ifdef CONFIG_MBO
+ /* Wi-Fi Agile Muiltiband AP is required to use a global operating
+ * class. */
+ if (hapd->conf->mbo_enabled)
+ force_global = true;
+#endif /* CONFIG_MBO */
+
+ if (force_global) {
/* Force the third octet of the country string to indicate
* Global Operating Class (Table E-4) */
eid[4] = 0x04;
+ }
+ if (is_6ghz_op_class(op_class)) {
/* Operating Triplet field */
/* Operating Extension Identifier (>= 201 to indicate this is
* not a Subband Triplet field) */
*pos++ = 201;
/* Operating Class */
- *pos++ = hapd->iconf->op_class;
+ *pos++ = op_class;
/* Coverage Class */
*pos++ = 0;
/* Subband Triplets are required only for the 20 MHz case */
- if (hapd->iconf->op_class == 131 ||
- hapd->iconf->op_class == 136)
+ if (op_class == 131 || op_class == 136)
pos = hostapd_fill_subband_triplets(hapd, pos, end);
} else {
pos = hostapd_fill_subband_triplets(hapd, pos, end);
@@ -390,11 +406,40 @@ static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
}
-static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
+static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsne_override_2(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsnxe_override(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
{
const u8 *ie;
- ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
+ ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
if (!ie || 2U + ie[1] > len)
return pos;
@@ -403,6 +448,39 @@ static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
}
+static size_t hostapd_get_rsne_override_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsne_override_2_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
{
#ifdef CONFIG_TESTING_OPTIONS
@@ -428,6 +506,11 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
return eid;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->csa_ie_only)
+ return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+
*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
*eid++ = 4;
*eid++ = hapd->cs_block_tx;
@@ -439,17 +522,56 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
}
+static u8 * hostapd_eid_max_cs_time(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_IEEE80211BE
+ u32 switch_time;
+
+ /* Add Max Channel Switch Time element only if this AP is affiliated
+ * with an AP MLD and channel switch is in process. */
+ if (!hapd->conf->mld_ap || !hapd->cs_freq_params.channel)
+ return eid;
+
+ /* Switch time is basically time between CSA count 1 and CSA count
+ * 0 (1 beacon interval) + time for interface restart + time to
+ * send a Beacon frame in the new channel (1 beacon interval).
+ *
+ * TODO: Use dynamic interface restart time. For now, assume 1 sec.
+ */
+ switch_time = USEC_TO_TU(1000 * 1000) + 2 * hapd->iconf->beacon_int;
+
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 4;
+ *eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME;
+ WPA_PUT_LE24(eid, switch_time);
+ eid += 3;
+#endif /* CONFIG_IEEE80211BE */
+
+ return eid;
+}
+
+
static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
{
u8 op_class, channel;
+ enum oper_chan_width chwidth;
+ int secondary_channel;
+ u8 seg0, seg1;
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
!hapd->iface->freq)
return eid;
+ hostapd_get_oper_chan_info_of_bss(hapd, &chwidth, &seg0, &seg1);
+ secondary_channel = hapd->iconf->secondary_channel;
+
+ if (seg0 == hapd->iconf->channel &&
+ chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ secondary_channel = 0;
+
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
- hapd->iconf->secondary_channel,
- hostapd_get_oper_chwidth(hapd->iconf),
+ secondary_channel,
+ chwidth,
&op_class, &channel) ==
NUM_HOSTAPD_MODES)
return eid;
@@ -476,7 +598,6 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
size_t len, rnr_len = 0;
u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
- size_t i;
if (!iface->mbssid_max_interfaces ||
iface->num_bss > iface->mbssid_max_interfaces ||
@@ -484,14 +605,6 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
!iface->ema_max_periodicity))
goto fail;
- /* Make sure bss->xrates_supported is set for all BSSs to know whether
- * it need to be non-inherited. */
- for (i = 0; i < iface->num_bss; i++) {
- u8 buf[100];
-
- hostapd_eid_ext_supp_rates(iface->bss[i], buf);
- }
-
tx_bss = hostapd_mbssid_get_tx_bss(hapd);
len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
NULL, 0, &rnr_len);
@@ -521,18 +634,24 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
elem_count, elem_offset, NULL, 0, rnr_elem,
&rnr_elem_count, rnr_elem_offset, rnr_len);
- params->mbssid_tx_iface = tx_bss->conf->iface;
- params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
- params->mbssid_elem = elem;
- params->mbssid_elem_len = end - elem;
- params->mbssid_elem_count = elem_count;
- params->mbssid_elem_offset = elem_offset;
- params->rnr_elem = rnr_elem;
- params->rnr_elem_len = rnr_len;
- params->rnr_elem_count = rnr_elem_count;
- params->rnr_elem_offset = rnr_elem_offset;
+ params->mbssid.mbssid_tx_iface = tx_bss->conf->iface;
+ params->mbssid.mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+ params->mbssid.mbssid_elem = elem;
+ params->mbssid.mbssid_elem_len = end - elem;
+ params->mbssid.mbssid_elem_count = elem_count;
+ params->mbssid.mbssid_elem_offset = elem_offset;
+ params->mbssid.rnr_elem = rnr_elem;
+ params->mbssid.rnr_elem_len = rnr_len;
+ params->mbssid.rnr_elem_count = rnr_elem_count;
+ params->mbssid.rnr_elem_offset = rnr_elem_offset;
if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
- params->ema = true;
+ params->mbssid.ema = true;
+
+ params->mbssid.mbssid_tx_iface_linkid = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (tx_bss->conf->mld_ap)
+ params->mbssid.mbssid_tx_iface_linkid = tx_bss->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
return 0;
@@ -568,7 +687,7 @@ static size_t he_elem_len(struct hostapd_data *hapd)
size_t len = 0;
#ifdef CONFIG_IEEE80211AX
- if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ if (!hostapd_is_he_enabled(hapd))
return len;
len += 3 + sizeof(struct ieee80211_he_capabilities) +
@@ -601,7 +720,6 @@ struct probe_resp_params {
bool is_p2p;
/* Generated IEs will be included inside an ML element */
- bool is_ml_sta_info;
struct hostapd_data *mld_ap;
struct mld_info *mld_info;
@@ -623,7 +741,7 @@ static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
#ifdef CONFIG_IEEE80211BE
if (!params)
return;
- ap_sta_free_sta_profile(params->mld_info);
+
os_free(params->mld_info);
params->mld_info = NULL;
#endif /* CONFIG_IEEE80211BE */
@@ -633,8 +751,12 @@ static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
struct probe_resp_params *params)
{
+ struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap :
+ hapd;
size_t buflen = 0;
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
@@ -661,31 +783,51 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
buflen += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
+
buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
buflen += 3 + sizeof(struct ieee80211_eht_operation);
if (hapd->iconf->punct_bitmap)
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
- if (!params->is_ml_sta_info && hapd->conf->mld_ap) {
- struct hostapd_data *ml_elem_ap =
- params->mld_ap ? params->mld_ap : hapd;
+ if (params->mld_ap && params->mld_ap->conf->mld_ap) {
+ buflen += hostapd_eid_eht_ml_beacon_len(
+ params->mld_ap, params->mld_info,
+ !!params->mld_ap);
+
+ if (hapd->conf->mld_ap)
+ buflen += hostapd_eid_eht_ml_beacon_len(
+ hapd, NULL, false);
+ /* For Max Channel Switch Time element during channel
+ * switch */
+ buflen += 6;
+ } else if (hapd->conf->mld_ap) {
buflen += hostapd_eid_eht_ml_beacon_len(
- ml_elem_ap, params->mld_info, !!params->mld_ap);
+ hapd, params->mld_info, false);
+
+ /* For Max Channel Switch Time element during channel
+ * switch */
+ buflen += 6;
}
+
+ if (hapd->conf->mld_ap)
+ buflen += hostapd_eid_eht_ml_tid_to_link_map_len(
+ hapd);
}
#endif /* CONFIG_IEEE80211BE */
- buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+ buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP,
+ NULL,
params->known_bss,
params->known_bss_len, NULL);
- if (!params->is_ml_sta_info)
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP,
- true);
+ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
+ buflen += hostapd_get_rsne_override_len(hapd);
+ buflen += hostapd_get_rsne_override_2_len(hapd);
+ buflen += hostapd_get_rsnxe_override_len(hapd);
return buflen;
}
@@ -695,18 +837,19 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
struct probe_resp_params *params,
u8 *pos, size_t len)
{
+ struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap :
+ hapd;
u8 *csa_pos;
u8 *epos;
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
epos = pos + len;
- if (!params->is_ml_sta_info) {
- *pos++ = WLAN_EID_SSID;
- *pos++ = hapd->conf->ssid.ssid_len;
- os_memcpy(pos, hapd->conf->ssid.ssid,
- hapd->conf->ssid.ssid_len);
- pos += hapd->conf->ssid.ssid_len;
- }
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
@@ -719,18 +862,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
- /*
- * CSA IE
- * TODO: This should be included inside the ML sta profile
- */
- if (!params->is_ml_sta_info) {
- csa_pos = hostapd_eid_csa(hapd, pos);
- if (csa_pos != pos)
- params->csa_pos = csa_pos - 1;
- else
- params->csa_pos = NULL;
- pos = csa_pos;
- }
+ /* CSA element */
+ csa_pos = hostapd_eid_csa(hapd, pos);
+ if (csa_pos != pos)
+ params->csa_pos = csa_pos - 1;
+ else
+ params->csa_pos = NULL;
+ pos = csa_pos;
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -740,24 +878,20 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
- pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+ pos = hostapd_eid_mbssid(hapd_probed, pos, epos,
+ WLAN_FC_STYPE_PROBE_RESP, 0,
NULL, params->known_bss, params->known_bss_len,
NULL, NULL, NULL, 0);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
- /*
- * eCSA IE
- * TODO: This should be included inside the ML sta profile
- */
- if (!params->is_ml_sta_info) {
- csa_pos = hostapd_eid_ecsa(hapd, pos);
- if (csa_pos != pos)
- params->ecsa_pos = csa_pos - 1;
- else
- params->ecsa_pos = NULL;
- pos = csa_pos;
- }
+ /* eCSA element */
+ csa_pos = hostapd_eid_ecsa(hapd, pos);
+ if (csa_pos != pos)
+ params->ecsa_pos = csa_pos - 1;
+ else
+ params->ecsa_pos = NULL;
+ pos = csa_pos;
pos = hostapd_eid_supported_op_classes(hapd, pos);
pos = hostapd_eid_ht_capabilities(hapd, pos);
@@ -785,7 +919,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
- if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ if (hostapd_is_vht_enabled(hapd) &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
pos = hostapd_eid_vht_operation(hapd, pos);
@@ -794,21 +928,23 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+ if (hostapd_is_he_enabled(hapd) &&
is_6ghz_op_class(hapd->iconf->op_class))
pos = hostapd_eid_txpower_envelope(hapd, pos);
#endif /* CONFIG_IEEE80211AX */
- pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ pos = hostapd_eid_chsw_wrapper(hapd, pos);
- if (!params->is_ml_sta_info)
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
- true);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
+
+ /* Max Channel Switch Time element */
+ pos = hostapd_eid_max_cs_time(hapd, pos);
+
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
u8 *cca_pos;
pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
@@ -829,17 +965,27 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
- struct hostapd_data *ml_elem_ap =
- params->mld_ap ? params->mld_ap : hapd;
-
- if (ml_elem_ap->conf->mld_ap)
+ if (hostapd_is_eht_enabled(hapd)) {
+ if (params->mld_ap && params->mld_ap->conf->mld_ap) {
pos = hostapd_eid_eht_ml_beacon(
- ml_elem_ap, params->mld_info,
+ params->mld_ap, params->mld_info,
pos, !!params->mld_ap);
+ if (hapd->conf->mld_ap)
+ pos = hostapd_eid_eht_ml_beacon(
+ hapd, NULL, pos, false);
+
+ } else if (hapd->conf->mld_ap) {
+ pos = hostapd_eid_eht_ml_beacon(hapd,
+ params->mld_info,
+ pos, false);
+ }
+
pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_eht_operation(hapd, pos);
+
+ if (hapd->conf->mld_ap)
+ pos = hostapd_eid_eht_ml_tid_to_link_map(hapd, pos);
}
#endif /* CONFIG_IEEE80211BE */
@@ -848,9 +994,8 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
- /* WPA / OSEN */
+ /* WPA */
pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
- pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
@@ -885,6 +1030,10 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
+ pos = hostapd_get_rsne_override(hapd, pos, epos - pos);
+ pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos);
+ pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos);
+
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
@@ -906,6 +1055,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
struct probe_resp_params *params)
{
+ struct hostapd_data *hapd_probed = hapd;
u8 *pos;
size_t buflen;
@@ -913,7 +1063,7 @@ static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
#define MAX_PROBERESP_LEN 768
buflen = MAX_PROBERESP_LEN;
- buflen += hostapd_probe_resp_elems_len(hapd, params);
+ buflen += hostapd_probe_resp_elems_len(hapd_probed, params);
params->resp = os_zalloc(buflen);
if (!params->resp) {
params->resp_len = 0;
@@ -943,7 +1093,7 @@ static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
params->resp->u.probe_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
- pos = hostapd_probe_resp_fill_elems(hapd, params,
+ pos = hostapd_probe_resp_fill_elems(hapd_probed, params,
params->resp->u.probe_resp.variable,
buflen);
@@ -957,7 +1107,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
int mld_id, u16 links)
{
- struct probe_resp_params sta_info_params;
struct hostapd_data *link;
params->mld_ap = NULL;
@@ -969,21 +1118,27 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
"MLD: Got ML probe request with AP MLD ID %d for links %04x",
mld_id, links);
+ /*
+ * Set mld_ap if the ML probe request explicitly requested a specific
+ * AP MLD ID.
+ */
+ if (mld_id > 0) {
+ if (hapd == hostapd_mbssid_get_tx_bss(hapd)) {
+ hapd = hostapd_get_mbssid_bss_by_idx(hapd, mld_id);
+ if (!hapd) {
+ wpa_printf(MSG_INFO,
+ "Ignore Probe Request from " MACSTR
+ " since no matched non-TX BSS found for MBSSID Index %d",
+ MAC2STR(mgmt->sa), mld_id);
+ goto fail;
+ }
+ }
+ params->mld_ap = hapd;
+ }
+
for_each_mld_link(link, hapd) {
struct mld_link_info *link_info;
- size_t buflen;
u8 mld_link_id = link->mld_link_id;
- u8 *epos;
- u8 buf[EHT_ML_MAX_STA_PROF_LEN];
-
- /*
- * Set mld_ap iff the ML probe request explicitly
- * requested a specific MLD ID. In that case, the targeted
- * AP may have been a nontransmitted BSSID on the same
- * interface.
- */
- if (mld_id != -1 && link->iface == hapd->iface)
- params->mld_ap = link;
/* Never duplicate main Probe Response frame body */
if (link == hapd)
@@ -994,52 +1149,15 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
continue;
link_info = &params->mld_info->links[mld_link_id];
-
- sta_info_params.req = params->req;
- sta_info_params.is_p2p = false;
- sta_info_params.is_ml_sta_info = true;
- sta_info_params.mld_ap = NULL;
- sta_info_params.mld_info = NULL;
-
- buflen = MAX_PROBERESP_LEN;
- buflen += hostapd_probe_resp_elems_len(link, &sta_info_params);
-
- if (buflen > EHT_ML_MAX_STA_PROF_LEN) {
- wpa_printf(MSG_DEBUG,
- "MLD: Not including link %d in ML probe response (%zu bytes is too long)",
- mld_link_id, buflen);
- goto fail;
- }
-
- /*
- * NOTE: This does not properly handle inheritance and
- * various other things.
- */
- link_info->valid = true;
- epos = buf;
-
- /* Capabilities is the only fixed parameter */
- WPA_PUT_LE16(epos, hostapd_own_capab_info(hapd));
- epos += 2;
-
- epos = hostapd_probe_resp_fill_elems(
- link, &sta_info_params, epos,
- EHT_ML_MAX_STA_PROF_LEN - 2);
- link_info->resp_sta_profile_len = epos - buf;
- os_free(link_info->resp_sta_profile);
- link_info->resp_sta_profile = os_memdup(
- buf, link_info->resp_sta_profile_len);
- if (!link_info->resp_sta_profile)
- link_info->resp_sta_profile_len = 0;
- os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN);
+ os_memcpy(link_info, &hapd->partner_links[mld_link_id],
+ sizeof(hapd->partner_links[mld_link_id]));
wpa_printf(MSG_DEBUG,
- "MLD: ML probe response includes link sta info for %d: %u bytes (estimate %zu)",
- mld_link_id, link_info->resp_sta_profile_len,
- buflen);
+ "MLD: ML probe response includes link STA info for %d: %u bytes",
+ mld_link_id, link_info->resp_sta_profile_len);
}
- if (mld_id != -1 && !params->mld_ap) {
+ if (mld_id > 0 && !params->mld_ap) {
wpa_printf(MSG_DEBUG,
"MLD: No nontransmitted BSSID for MLD ID %d",
mld_id);
@@ -1305,6 +1423,7 @@ static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
for_each_element_id(sub, 0, pos, len) {
const struct ieee80211_eht_per_sta_profile *sta;
u16 sta_control;
+ u8 link_id;
if (*links == 0xffff)
*links = 0;
@@ -1324,7 +1443,9 @@ static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
* partial profile was requested.
*/
sta_control = le_to_host16(sta->sta_control);
- *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
+ link_id = sta_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id < MAX_NUM_MLD_LINKS)
+ *links |= BIT(link_id);
}
if (!for_each_element_completed(sub, pos, len)) {
@@ -1353,6 +1474,7 @@ void handle_probe_req(struct hostapd_data *hapd,
size_t csa_offs_len;
struct radius_sta rad_info;
struct probe_resp_params params;
+ char *hex = NULL;
#ifdef CONFIG_IEEE80211BE
int mld_id;
u16 links;
@@ -1483,6 +1605,27 @@ void handle_probe_req(struct hostapd_data *hapd,
res = ssid_match(hapd, elems.ssid, elems.ssid_len,
elems.ssid_list, elems.ssid_list_len,
elems.short_ssid_list, elems.short_ssid_list_len);
+ if (res == NO_SSID_MATCH && hapd->iconf->mbssid) {
+ /* If the Probe Request frame is directed to the TX BSS in an
+ * MBSSID set, check whether the SSID is matching any of the
+ * non-TX BSSs' SSID. */
+ if (!(mgmt->da[0] & 0x01 || mgmt->bssid[0] & 0x01)) {
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ if (hapd->iface->bss[i] == hapd)
+ continue;
+
+ res = ssid_match(hapd->iface->bss[i],
+ elems.ssid, elems.ssid_len,
+ elems.ssid_list,
+ elems.ssid_list_len,
+ elems.short_ssid_list,
+ elems.short_ssid_list_len);
+ if (res != NO_SSID_MATCH)
+ break;
+ }
+ }
+ }
+
if (res == NO_SSID_MATCH) {
if (!(mgmt->da[0] & 0x01)) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
@@ -1585,8 +1728,20 @@ void handle_probe_req(struct hostapd_data *hapd,
if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
return;
+ if (hapd->conf->notify_mgmt_frames) {
+ size_t hex_len;
+
+ hex_len = len * 2 + 1;
+ hex = os_malloc(hex_len);
+ if (hex)
+ wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len);
+ }
+
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
- " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+ " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal,
+ hex ? " buf=" : "", hex ? hex : "");
+
+ os_free(hex);
os_memset(&params, 0, sizeof(params));
@@ -1603,7 +1758,6 @@ void handle_probe_req(struct hostapd_data *hapd,
params.is_p2p = !!elems.p2p;
params.known_bss = elems.mbssid_known_bss;
params.known_bss_len = elems.mbssid_known_bss_len;
- params.is_ml_sta_info = false;
hostapd_gen_probe_resp(hapd, &params);
@@ -1630,7 +1784,8 @@ void handle_probe_req(struct hostapd_data *hapd,
params.ecsa_pos - (u8 *) params.resp;
}
- ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack,
+ ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd),
+ params.resp, params.resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
@@ -1684,7 +1839,6 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
params.is_p2p = false;
params.known_bss = NULL;
params.known_bss_len = 0;
- params.is_ml_sta_info = false;
params.mld_ap = NULL;
params.mld_info = NULL;
@@ -1728,7 +1882,6 @@ u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
probe_params.is_p2p = false;
probe_params.known_bss = NULL;
probe_params.known_bss_len = 0;
- probe_params.is_ml_sta_info = false;
probe_params.mld_ap = NULL;
probe_params.mld_info = NULL;
@@ -1754,21 +1907,21 @@ void sta_track_del(struct hostapd_sta_info *info)
static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd)
{
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be)
+ if (hostapd_is_eht_enabled(hapd))
return FD_CAP_PHY_INDEX_EHT;
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
+ if (hostapd_is_he_enabled(hapd))
return FD_CAP_PHY_INDEX_HE;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
- if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
+ if (hostapd_is_vht_enabled(hapd))
return FD_CAP_PHY_INDEX_VHT;
#endif /* CONFIG_IEEE80211AC */
- if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
+ if (hostapd_is_ht_enabled(hapd))
return FD_CAP_PHY_INDEX_HT;
return 0;
@@ -2106,7 +2259,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
-#define BEACON_TAIL_BUF_SIZE 512
+#define BEACON_TAIL_BUF_SIZE 1500
head = os_zalloc(BEACON_HEAD_BUF_SIZE);
tail_len = BEACON_TAIL_BUF_SIZE;
#ifdef CONFIG_WPS
@@ -2134,7 +2287,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tail_len += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
tail_len += 3 + sizeof(struct ieee80211_eht_operation);
if (hapd->iconf->punct_bitmap)
@@ -2145,8 +2298,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
* long based on the common info and number of per
* station profiles. For now use 256.
*/
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
tail_len += 256;
+ tail_len +=
+ hostapd_eid_eht_ml_tid_to_link_map_len(hapd);
+
+ /* for Max Channel Switch Time element during channel
+ * switch */
+ tail_len += 6;
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -2157,13 +2317,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
tail_len += hostapd_eid_dpp_cc_len(hapd);
+ tail_len += hostapd_get_rsne_override_len(hapd);
+ tail_len += hostapd_get_rsne_override_2_len(hapd);
+ tail_len += hostapd_get_rsnxe_override_len(hapd);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
- os_free(head);
- os_free(tail);
- return -1;
+ goto error;
}
tailend = tail + tail_len;
@@ -2239,17 +2400,19 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
- if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+ /*
+ * Include the Multiple BSSID element whenever MBSSID is enabled. The
+ * element may include zero or more nontransmitted BSSID profiles.
+ */
+ if (hapd->iconf->mbssid) {
if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
- os_free(head);
- os_free(tail);
wpa_printf(MSG_ERROR,
"MBSSID: Failed to set beacon data");
- return -1;
+ goto error;
}
complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
(hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
- params->mbssid_elem_count == 1);
+ params->mbssid.mbssid_elem_count == 1);
}
tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
@@ -2273,7 +2436,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
- if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ if (hostapd_is_vht_enabled(hapd) &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
tailpos = hostapd_eid_vht_operation(hapd, tailpos);
@@ -2282,21 +2445,25 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+ if (hostapd_is_he_enabled(hapd) &&
is_6ghz_op_class(hapd->iconf->op_class))
tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
#endif /* CONFIG_IEEE80211AX */
- tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+ tailpos = hostapd_eid_chsw_wrapper(hapd, tailpos);
tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+
+ /* Max Channel Switch Time element */
+ tailpos = hostapd_eid_max_cs_time(hapd, tailpos);
+
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
- params->mbssid_elem_count);
+ params->mbssid.mbssid_elem_count);
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
u8 *cca_pos;
tailpos = hostapd_eid_he_capab(hapd, tailpos,
@@ -2316,13 +2483,16 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
if (hapd->conf->mld_ap)
tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
tailpos, false);
tailpos = hostapd_eid_eht_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+ if (hapd->conf->mld_ap)
+ tailpos = hostapd_eid_eht_ml_tid_to_link_map(hapd,
+ tailpos);
}
#endif /* CONFIG_IEEE80211BE */
@@ -2331,9 +2501,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
- /* WPA / OSEN */
+ /* WPA */
tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
- tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
@@ -2368,6 +2537,13 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tail + tail_len - tailpos);
tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsne_override(hapd, tailpos,
+ tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsne_override_2(hapd, tailpos,
+ tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsnxe_override(hapd, tailpos,
+ tail + tail_len - tailpos);
+
if (hapd->conf->vendor_elements) {
os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
@@ -2400,25 +2576,27 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
/* If SAE offload is enabled, provide password to lower layer for
* SAE authentication and PMK generation.
*/
- if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &&
(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
if (hostapd_sae_pk_in_use(hapd->conf)) {
wpa_printf(MSG_ERROR,
"SAE PK not supported with SAE offload");
- return -1;
+ goto error;
}
if (hostapd_sae_pw_id_in_use(hapd->conf)) {
wpa_printf(MSG_ERROR,
"SAE Password Identifiers not supported with SAE offload");
- return -1;
+ goto error;
}
- params->sae_password = sae_get_password(hapd, NULL, NULL, NULL,
- NULL, NULL);
+ params->sae_password = sae_get_password(hapd, NULL, NULL, 0,
+ NULL, NULL, NULL);
if (!params->sae_password) {
wpa_printf(MSG_ERROR, "SAE password not configured for offload");
- return -1;
+ goto error;
}
}
#endif /* CONFIG_SAE */
@@ -2431,9 +2609,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
params->proberesp_len = resp_len;
params->dtim_period = hapd->conf->dtim_period;
params->beacon_int = hapd->iconf->beacon_int;
- params->basic_rates = hapd->iface->basic_rates;
- params->beacon_rate = hapd->iconf->beacon_rate;
- params->rate_type = hapd->iconf->rate_type;
+ params->basic_rates = hapd->basic_rates;
+ params->beacon_rate = hapd->conf->beacon_rate;
+ params->rate_type = hapd->conf->rate_type;
params->ssid = hapd->conf->ssid.ssid;
params->ssid_len = hapd->conf->ssid.ssid_len;
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
@@ -2445,7 +2623,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
else if (hapd->conf->wpa & WPA_PROTO_WPA)
params->pairwise_ciphers = hapd->conf->wpa_pairwise;
params->group_cipher = hapd->conf->wpa_group;
- params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params->key_mgmt_suites = hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2;
params->auth_algs = hapd->conf->auth_algs;
params->wpa_version = hapd->conf->wpa;
params->privacy = hapd->conf->wpa;
@@ -2478,7 +2658,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
else
params->short_slot_time = -1;
- if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+ if (!hostapd_is_ht_enabled(hapd))
params->ht_opmode = -1;
else
params->ht_opmode = hapd->iface->ht_op_mode;
@@ -2494,10 +2674,6 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
params->disable_dgaf = hapd->conf->disable_dgaf;
- if (hapd->conf->osen) {
- params->privacy = 1;
- params->osen = 1;
- }
#endif /* CONFIG_HS20 */
params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
params->pbss = hapd->conf->pbss;
@@ -2514,14 +2690,21 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
- !hapd->conf->disable_11be) {
+ if (hapd->conf->mld_ap) {
params->mld_ap = true;
params->mld_link_id = hapd->mld_link_id;
}
#endif /* CONFIG_IEEE80211BE */
return 0;
+
+#if defined(CONFIG_SAE) || defined(NEED_AP_MLME)
+error:
+ os_free(head);
+ os_free(tail);
+ os_free(resp);
+ return -1;
+#endif /* CONFIG_SAE || NEED_AP_MLME */
}
@@ -2533,14 +2716,14 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
params->head = NULL;
os_free(params->proberesp);
params->proberesp = NULL;
- os_free(params->mbssid_elem);
- params->mbssid_elem = NULL;
- os_free(params->mbssid_elem_offset);
- params->mbssid_elem_offset = NULL;
- os_free(params->rnr_elem);
- params->rnr_elem = NULL;
- os_free(params->rnr_elem_offset);
- params->rnr_elem_offset = NULL;
+ os_free(params->mbssid.mbssid_elem);
+ params->mbssid.mbssid_elem = NULL;
+ os_free(params->mbssid.mbssid_elem_offset);
+ params->mbssid.mbssid_elem_offset = NULL;
+ os_free(params->mbssid.rnr_elem);
+ params->mbssid.rnr_elem = NULL;
+ os_free(params->mbssid.rnr_elem_offset);
+ params->mbssid.rnr_elem_offset = NULL;
#ifdef CONFIG_FILS
os_free(params->fd_frame_tmpl);
params->fd_frame_tmpl = NULL;
@@ -2576,6 +2759,14 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
return -1;
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->cca_in_progress) {
+ wpa_printf(MSG_ERROR,
+ "Cannot set beacons during CCA period");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
hapd->beacon_set_done = 1;
if (ieee802_11_build_ap_params(hapd, &params) < 0)
@@ -2679,12 +2870,452 @@ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
}
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_get_probe_resp_tmpl(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ bool is_ml_sta_info)
+{
+ os_memset(params, 0, sizeof(*params));
+ hostapd_gen_probe_resp(hapd, params);
+ if (!params->resp)
+ return -1;
+
+ /* The caller takes care of freeing params->resp. */
+ return 0;
+}
+
+
+static bool is_restricted_eid_in_sta_profile(u8 eid, bool tx_vap)
+{
+ switch (eid) {
+ case WLAN_EID_TIM:
+ case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+ case WLAN_EID_MULTIPLE_BSSID:
+ case WLAN_EID_REDUCED_NEIGHBOR_REPORT:
+ case WLAN_EID_NEIGHBOR_REPORT:
+ return true;
+ case WLAN_EID_SSID:
+ /* SSID is not restricted for non-transmitted BSSID */
+ return tx_vap;
+ default:
+ return false;
+ }
+}
+
+
+static bool is_restricted_ext_eid_in_sta_profile(u8 ext_id)
+{
+ switch (ext_id) {
+ case WLAN_EID_EXT_MULTI_LINK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+/* Create the link STA profiles based on inheritance from the reporting
+ * profile.
+ *
+ * NOTE: The same function is used for length calculation as well as filling
+ * data in the given buffer. This avoids risk of not updating the length
+ * function but filling function or vice versa.
+ */
+static size_t hostapd_add_sta_profile(struct ieee80211_mgmt *link_fdata,
+ size_t link_data_len,
+ struct ieee80211_mgmt *own_fdata,
+ size_t own_data_len,
+ u8 *sta_profile, bool tx_vap)
+{
+ const struct element *link_elem;
+ size_t sta_profile_len = 0;
+ const u8 *link_elem_data;
+ u8 link_ele_len;
+ u8 *link_data;
+ const struct element *own_elem;
+ u8 link_eid, own_eid, own_ele_len;
+ const u8 *own_elem_data;
+ u8 *own_data;
+ bool is_ext;
+ bool ie_found;
+ u8 non_inherit_ele_ext_list[256] = { 0 };
+ u8 non_inherit_ele_ext_list_len = 0;
+ u8 non_inherit_ele_list[256] = { 0 };
+ u8 non_inherit_ele_list_len = 0;
+ u8 num_link_elem_vendor_ies = 0, num_own_elem_vendor_ies = 0;
+ bool add_vendor_ies = false, is_identical_vendor_ies = true;
+ /* The bitmap of parsed EIDs. There are 256 EIDs and ext EIDs, so 32
+ * bytes to store the bitmaps. */
+ u8 parsed_eid_bmap[32] = { 0 }, parsed_ext_eid_bmap[32] = { 0 };
+ /* extra len used in the logic includes the element id and len */
+ u8 extra_len = 2;
+
+ /* Include len for capab info */
+ sta_profile_len += sizeof(le16);
+ if (sta_profile) {
+ os_memcpy(sta_profile, &link_fdata->u.probe_resp.capab_info,
+ sizeof(le16));
+ sta_profile += sizeof(le16);
+ }
+
+ own_data = own_fdata->u.probe_resp.variable;
+ link_data = link_fdata->u.probe_resp.variable;
+
+ /* The below logic takes the reporting BSS data and reported BSS data
+ * and performs intersection to build the STA profile of the reported
+ * BSS. Certain elements are not added to the STA profile as
+ * recommended in standard. Matching element information in the
+ * reporting BSS profile are ignored in the STA profile. Remaining
+ * elements pertaining to the STA profile are appended at the end. */
+ for_each_element(own_elem, own_data, own_data_len) {
+ is_ext = false;
+ ie_found = false;
+
+ /* Pick one of own elements and get its EID and length */
+ own_elem_data = own_elem->data;
+ own_ele_len = own_elem->datalen;
+
+ if (own_elem->id == WLAN_EID_EXTENSION) {
+ is_ext = true;
+ own_eid = *(own_elem_data);
+ if (is_restricted_ext_eid_in_sta_profile(own_eid))
+ continue;
+ } else {
+ own_eid = own_elem->id;
+ if (is_restricted_eid_in_sta_profile(own_eid, tx_vap))
+ continue;
+ }
+
+ for_each_element(link_elem, link_data, link_data_len) {
+ /* If the element type mismatches, do not consider
+ * this link element for comparison. */
+ if ((link_elem->id == WLAN_EID_EXTENSION &&
+ !is_ext) ||
+ (is_ext && link_elem->id != WLAN_EID_EXTENSION))
+ continue;
+
+ /* Comparison can be done so get the link element and
+ * its EID and length. */
+ link_elem_data = link_elem->data;
+ link_ele_len = link_elem->datalen;
+
+ if (link_elem->id == WLAN_EID_EXTENSION)
+ link_eid = *(link_elem_data);
+ else
+ link_eid = link_elem->id;
+
+ /* Ignore if EID does not match */
+ if (own_eid != link_eid)
+ continue;
+
+ ie_found = true;
+
+ /* Ignore if the contents is identical. */
+ if (own_ele_len == link_ele_len &&
+ os_memcmp(own_elem->data, link_elem->data,
+ own_ele_len) == 0) {
+ if (own_eid == WLAN_EID_VENDOR_SPECIFIC) {
+ is_identical_vendor_ies = true;
+ num_own_elem_vendor_ies++;
+ }
+
+ /* Update the parsed EIDs bitmap */
+ if (is_ext)
+ parsed_ext_eid_bmap[own_eid / 8] |=
+ BIT(own_eid % 8);
+ else
+ parsed_eid_bmap[own_eid / 8] |=
+ BIT(own_eid % 8);
+ break;
+ }
+
+ /* No need to include this non-matching Vendor Specific
+ * element explicitly at this point. */
+ if (own_eid == WLAN_EID_VENDOR_SPECIFIC) {
+ is_identical_vendor_ies = false;
+ continue;
+ }
+
+ /* This element is present in the reported profile
+ * as well as present in the reporting profile.
+ * However, there is a mismatch in the contents and
+ * hence, include this in the per STA profile. */
+ sta_profile_len += link_ele_len + extra_len;
+ if (sta_profile) {
+ os_memcpy(sta_profile,
+ link_elem->data - extra_len,
+ link_ele_len + extra_len);
+ sta_profile += link_ele_len + extra_len;
+ }
+
+ /* Update the parsed EIDs bitmap */
+ if (is_ext)
+ parsed_ext_eid_bmap[own_eid / 8] |=
+ BIT(own_eid % 8);
+ else
+ parsed_eid_bmap[own_eid / 8] |=
+ BIT(own_eid % 8);
+ break;
+ }
+
+ /* We found at least one Vendor Specific element in reporting
+ * link which is not same (or present) in the reported link. We
+ * need to include all Vendor Specific elements from the
+ * reported link. */
+ if (!is_identical_vendor_ies)
+ add_vendor_ies = true;
+
+ /* This is a unique element in the reporting profile which is
+ * not present in the reported profile. Update the
+ * non-inheritance list. */
+ if (!ie_found) {
+ u8 idx;
+
+ if (is_ext) {
+ idx = non_inherit_ele_ext_list_len++;
+ non_inherit_ele_ext_list[idx] = own_eid;
+ } else {
+ idx = non_inherit_ele_list_len++;
+ non_inherit_ele_list[idx] = own_eid;
+ }
+ }
+ }
+
+ /* Parse the remaining elements in the reported profile */
+ for_each_element(link_elem, link_data, link_data_len) {
+ link_elem_data = link_elem->data;
+ link_ele_len = link_elem->datalen;
+
+ /* No need to check this Vendor Specific element at this point.
+ * Just take the count and continue. */
+ if (link_elem->id == WLAN_EID_VENDOR_SPECIFIC) {
+ num_link_elem_vendor_ies++;
+ continue;
+ }
+
+ if (link_elem->id == WLAN_EID_EXTENSION) {
+ link_eid = *(link_elem_data);
+
+ if ((parsed_ext_eid_bmap[link_eid / 8] &
+ BIT(link_eid % 8)) ||
+ is_restricted_ext_eid_in_sta_profile(link_eid))
+ continue;
+ } else {
+ link_eid = link_elem->id;
+
+ if ((parsed_eid_bmap[link_eid / 8] &
+ BIT(link_eid % 8)) ||
+ is_restricted_eid_in_sta_profile(link_eid, tx_vap))
+ continue;
+ }
+
+ sta_profile_len += link_ele_len + extra_len;
+ if (sta_profile) {
+ os_memcpy(sta_profile, link_elem_data - extra_len,
+ link_ele_len + extra_len);
+ sta_profile += link_ele_len + extra_len;
+ }
+ }
+
+ /* Handle Vendor Specific elements
+ * Add all the Vendor Specific elements of the reported link if
+ * a. There is at least one non-matching Vendor Specific element, or
+ * b. The number of Vendor Specific elements in reporting and reported
+ * link is not same. */
+ if (add_vendor_ies ||
+ num_own_elem_vendor_ies != num_link_elem_vendor_ies) {
+ for_each_element(link_elem, link_data, link_data_len) {
+ link_elem_data = link_elem->data;
+ link_ele_len = link_elem->datalen;
+
+ if (link_elem->id != WLAN_EID_VENDOR_SPECIFIC)
+ continue;
+
+ sta_profile_len += link_ele_len + extra_len;
+ if (sta_profile) {
+ os_memcpy(sta_profile,
+ link_elem_data - extra_len,
+ link_ele_len + extra_len);
+ sta_profile += link_ele_len + extra_len;
+ }
+ }
+ }
+
+ /* Handle non-inheritance
+ * Non-Inheritance element:
+ * Element ID Ext: 1 octet
+ * Length: 1 octet
+ * Ext tag number: 1 octet
+ * Length of Elements ID list: 1 octet
+ * Elements ID list: variable
+ * Length of Elements ID Extension list: 1 octet
+ * Elements ID extensions list: variable
+ */
+ if (non_inherit_ele_list_len || non_inherit_ele_ext_list_len)
+ sta_profile_len += 3 + 2 + non_inherit_ele_list_len +
+ non_inherit_ele_ext_list_len;
+
+ if (sta_profile &&
+ (non_inherit_ele_list_len || non_inherit_ele_ext_list_len)) {
+ *sta_profile++ = WLAN_EID_EXTENSION;
+ *sta_profile++ = non_inherit_ele_list_len +
+ non_inherit_ele_ext_list_len + 3;
+ *sta_profile++ = WLAN_EID_EXT_NON_INHERITANCE;
+ *sta_profile++ = non_inherit_ele_list_len;
+ os_memcpy(sta_profile, non_inherit_ele_list,
+ non_inherit_ele_list_len);
+ sta_profile += non_inherit_ele_list_len;
+ *sta_profile++ = non_inherit_ele_ext_list_len;
+ os_memcpy(sta_profile, non_inherit_ele_ext_list,
+ non_inherit_ele_ext_list_len);
+ sta_profile += non_inherit_ele_ext_list_len;
+ }
+
+ return sta_profile_len;
+}
+
+
+static u8 * hostapd_gen_sta_profile(struct ieee80211_mgmt *link_data,
+ size_t link_data_len,
+ struct ieee80211_mgmt *own_data,
+ size_t own_data_len,
+ size_t *sta_profile_len, bool tx_vap)
+{
+ u8 *sta_profile;
+
+ /* Get the length first */
+ *sta_profile_len = hostapd_add_sta_profile(link_data, link_data_len,
+ own_data, own_data_len,
+ NULL, tx_vap);
+ if (!(*sta_profile_len) || *sta_profile_len > EHT_ML_MAX_STA_PROF_LEN)
+ return NULL;
+
+ sta_profile = os_zalloc(*sta_profile_len);
+ if (!sta_profile)
+ return NULL;
+
+ /* Now fill in the data */
+ hostapd_add_sta_profile(link_data, link_data_len, own_data,
+ own_data_len, sta_profile, tx_vap);
+
+ /* The caller takes care of freeing the returned sta_profile */
+ return sta_profile;
+}
+
+
+static void hostapd_gen_per_sta_profiles(struct hostapd_data *hapd)
+{
+ bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd);
+ size_t link_data_len, sta_profile_len;
+ size_t own_data_len, fixed;
+ struct probe_resp_params link_params;
+ struct probe_resp_params own_params;
+ struct ieee80211_mgmt *link_data;
+ struct ieee80211_mgmt *own_data;
+ struct mld_link_info *link_info;
+ struct hostapd_data *link_bss;
+ u8 link_id, *sta_profile;
+
+ if (!hapd->conf->mld_ap || !hapd->started)
+ return;
+
+ wpa_printf(MSG_DEBUG, "MLD: Generating per STA profiles for MLD %s",
+ hapd->conf->iface);
+
+ wpa_printf(MSG_DEBUG, "MLD: Reporting link %d", hapd->mld_link_id);
+
+ /* Generate a Probe Response template for self */
+ if (hostapd_get_probe_resp_tmpl(hapd, &own_params, false)) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Error in building per STA profiles");
+ return;
+ }
+
+ own_data = own_params.resp;
+ own_data_len = own_params.resp_len;
+
+ /* Consider the length of the variable fields */
+ fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ if (own_data_len < fixed)
+ goto fail;
+ own_data_len -= fixed;
+
+ for_each_mld_link(link_bss, hapd) {
+ if (link_bss == hapd || !link_bss->started)
+ continue;
+
+ link_id = link_bss->mld_link_id;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ sta_profile = NULL;
+ sta_profile_len = 0;
+
+ /* Generate a Probe Response frame template for partner link */
+ if (hostapd_get_probe_resp_tmpl(link_bss, &link_params, true)) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Could not get link STA probe response template for link %d",
+ link_id);
+ continue;
+ }
+
+ link_data = link_params.resp;
+ link_data_len = link_params.resp_len;
+
+ /* Consider length of the variable fields */
+ fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ if (link_data_len < fixed)
+ continue;
+ link_data_len -= fixed;
+
+ sta_profile = hostapd_gen_sta_profile(link_data, link_data_len,
+ own_data, own_data_len,
+ &sta_profile_len, tx_vap);
+ if (!sta_profile) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Could not generate link STA profile for link %d",
+ link_id);
+ continue;
+ }
+
+ link_info = &hapd->partner_links[link_id];
+ link_info->valid = true;
+
+ os_free(link_info->resp_sta_profile);
+ link_info->resp_sta_profile_len = sta_profile_len;
+
+ link_info->resp_sta_profile = os_memdup(sta_profile,
+ sta_profile_len);
+ if (!link_info->resp_sta_profile)
+ link_info->resp_sta_profile_len = 0;
+
+ os_memcpy(link_info->local_addr, link_bss->own_addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Reported link STA info for %d: %u bytes",
+ link_id, link_info->resp_sta_profile_len);
+
+ os_free(sta_profile);
+ os_free(link_params.resp);
+ }
+
+fail:
+ os_free(own_params.resp);
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
int ieee802_11_set_beacon(struct hostapd_data *hapd)
{
struct hostapd_iface *iface = hapd->iface;
int ret;
size_t i, j;
bool is_6g, hapd_mld = false;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *link_bss;
+#endif /* CONFIG_IEEE80211BE */
ret = __ieee802_11_set_beacon(hapd);
if (ret != 0)
@@ -2720,11 +3351,21 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
continue;
#endif /* CONFIG_IEEE80211BE */
- if (other->bss[i] && other->bss[i]->started)
+ if (other->bss[i] && other->bss[i]->started &&
+ other->bss[i]->beacon_set_done)
__ieee802_11_set_beacon(other->bss[i]);
}
}
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd_mld)
+ return 0;
+
+ /* Generate per STA profiles for each affiliated APs */
+ for_each_mld_link(link_bss, hapd)
+ hostapd_gen_per_sta_profiles(link_bss);
+#endif /* CONFIG_IEEE80211BE */
+
return 0;
}
diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c
index d4d73de19d19..90a0a49363e3 100644
--- a/contrib/wpa/src/ap/ctrl_iface_ap.c
+++ b/contrib/wpa/src/ap/ctrl_iface_ap.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
+#include "common/hw_features_common.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "fst/fst_ctrl_iface.h"
#include "hostapd.h"
@@ -475,6 +476,10 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211BE
if (sta->mld_info.mld_sta) {
+ u16 mld_sta_capa = sta->mld_info.common_info.mld_capa;
+ u8 max_simul_links = mld_sta_capa &
+ EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+
for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) {
if (!sta->mld_info.links[i].valid)
continue;
@@ -485,6 +490,11 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_simul_links=%d\n", max_simul_links);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
}
#endif /* CONFIG_IEEE80211BE */
@@ -661,15 +671,18 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P_MANAGER */
- if (os_strstr(txtaddr, " tx=0"))
+ sta = ap_get_sta(hapd, addr);
+ if (os_strstr(txtaddr, " tx=0")) {
hostapd_drv_sta_remove(hapd, addr);
- else
+ if (sta)
+ ap_free_sta(hapd, sta);
+ } else {
hostapd_drv_sta_deauth(hapd, addr, reason);
- sta = ap_get_sta(hapd, addr);
- if (sta)
- ap_sta_deauthenticate(hapd, sta, reason);
- else if (addr[0] == 0xff)
- hostapd_free_stas(hapd);
+ if (sta)
+ ap_sta_deauthenticate(hapd, sta, reason);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+ }
return 0;
}
@@ -723,15 +736,18 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P_MANAGER */
- if (os_strstr(txtaddr, " tx=0"))
+ sta = ap_get_sta(hapd, addr);
+ if (os_strstr(txtaddr, " tx=0")) {
hostapd_drv_sta_remove(hapd, addr);
- else
+ if (sta)
+ ap_free_sta(hapd, sta);
+ } else {
hostapd_drv_sta_disassoc(hapd, addr, reason);
- sta = ap_get_sta(hapd, addr);
- if (sta)
- ap_sta_disassociate(hapd, sta, reason);
- else if (addr[0] == 0xff)
- hostapd_free_stas(hapd);
+ if (sta)
+ ap_sta_disassociate(hapd, sta, reason);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+ }
return 0;
}
@@ -874,15 +890,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
iface->conf->channel,
iface->conf->enable_edmg,
iface->conf->edmg_channel,
- iface->conf->ieee80211n && !hapd->conf->disable_11n ?
+ hostapd_is_ht_enabled(hapd) ?
iface->conf->secondary_channel : 0,
- iface->conf->ieee80211n && !hapd->conf->disable_11n,
- iface->conf->ieee80211ac &&
- !hapd->conf->disable_11ac,
- iface->conf->ieee80211ax &&
- !hapd->conf->disable_11ax,
- iface->conf->ieee80211be &&
- !hapd->conf->disable_11be,
+ hostapd_is_ht_enabled(hapd),
+ hostapd_is_vht_enabled(hapd),
+ hostapd_is_he_enabled(hapd),
+ hostapd_is_eht_enabled(hapd),
iface->conf->beacon_int,
hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
@@ -890,7 +903,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
#ifdef CONFIG_IEEE80211BE
- if (iface->conf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
ret = os_snprintf(buf + len, buflen - len,
"eht_oper_chwidth=%d\n"
"eht_oper_centr_freq_seg0_idx=%d\n",
@@ -911,6 +924,15 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
}
+ if (hapd->iconf->punct_bitmap) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "punct_bitmap=0x%x\n",
+ hapd->iconf->punct_bitmap);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
if (hapd->conf->mld_ap) {
struct hostapd_data *link_bss;
@@ -945,12 +967,21 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
return len;
len += ret;
}
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "ap_mld_type=%s\n",
+ (hapd->iface->mld_mld_capa &
+ EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK)
+ ? "NSTR" : "STR");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
}
}
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
- if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
ret = os_snprintf(buf + len, buflen - len,
"he_oper_chwidth=%d\n"
"he_oper_centr_freq_seg0_idx=%d\n"
@@ -974,7 +1005,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
}
#endif /* CONFIG_IEEE80211AX */
- if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
+ if (hostapd_is_vht_enabled(hapd)) {
ret = os_snprintf(buf + len, buflen - len,
"vht_oper_chwidth=%d\n"
"vht_oper_centr_freq_seg0_idx=%d\n"
@@ -989,7 +1020,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
}
- if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+ if (hostapd_is_vht_enabled(hapd) && mode) {
u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
@@ -1002,7 +1033,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
}
- if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+ if (hostapd_is_ht_enabled(hapd)) {
ret = os_snprintf(buf + len, buflen - len,
"ht_caps_info=%04x\n",
hapd->iconf->ht_capab);
@@ -1011,21 +1042,21 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
}
- if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ if (hostapd_is_ht_enabled(hapd) && mode) {
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
mode->mcs_set);
}
- if (iface->current_rates && iface->num_rates) {
+ if (hapd->current_rates && hapd->num_rates) {
ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
- for (j = 0; j < iface->num_rates; j++) {
+ for (j = 0; j < hapd->num_rates; j++) {
ret = os_snprintf(buf + len, buflen - len, "%s%02x",
j > 0 ? " " : "",
- iface->current_rates[j].rate / 5);
+ hapd->current_rates[j].rate / 5);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -1094,54 +1125,352 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
}
-int hostapd_parse_csa_settings(const char *pos,
- struct csa_settings *settings)
+int hostapd_parse_freq_params(const char *pos,
+ struct hostapd_freq_params *params,
+ unsigned int freq)
{
- char *end;
+ os_memset(params, 0, sizeof(*params));
- os_memset(settings, 0, sizeof(*settings));
- settings->cs_count = strtol(pos, &end, 10);
- if (pos == end) {
- wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
- return -1;
- }
+ if (freq)
+ params->freq = freq;
+ else
+ params->freq = atoi(pos);
- settings->freq_params.freq = atoi(end);
- if (settings->freq_params.freq == 0) {
- wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+ if (params->freq == 0) {
+ wpa_printf(MSG_ERROR, "freq_params: invalid freq provided");
return -1;
}
-#define SET_CSA_SETTING(str) \
+#define SET_FREQ_PARAM(str) \
do { \
const char *pos2 = os_strstr(pos, " " #str "="); \
if (pos2) { \
pos2 += sizeof(" " #str "=") - 1; \
- settings->freq_params.str = atoi(pos2); \
+ params->str = atoi(pos2); \
} \
} while (0)
-#define SET_CSA_SETTING_EXT(str) \
- do { \
- const char *pos2 = os_strstr(pos, " " #str "="); \
- if (pos2) { \
- pos2 += sizeof(" " #str "=") - 1; \
- settings->str = atoi(pos2); \
- } \
- } while (0)
+ SET_FREQ_PARAM(center_freq1);
+ SET_FREQ_PARAM(center_freq2);
+ SET_FREQ_PARAM(bandwidth);
+ SET_FREQ_PARAM(sec_channel_offset);
+ SET_FREQ_PARAM(punct_bitmap);
+ params->ht_enabled = !!os_strstr(pos, " ht");
+ params->vht_enabled = !!os_strstr(pos, " vht");
+ params->eht_enabled = !!os_strstr(pos, " eht");
+ params->he_enabled = !!os_strstr(pos, " he") ||
+ params->eht_enabled;
+#undef SET_FREQ_PARAM
+
+ return 0;
+}
+
+
+static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface,
+ int freq)
+{
+ int i;
+ enum hostapd_hw_mode target_mode;
+ bool is_6ghz = is_6ghz_freq(freq);
+
+ if (freq < 4000)
+ target_mode = HOSTAPD_MODE_IEEE80211G;
+ else if (freq > 50000)
+ target_mode = HOSTAPD_MODE_IEEE80211AD;
+ else
+ target_mode = HOSTAPD_MODE_IEEE80211A;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode;
+
+ mode = &iface->hw_features[i];
+ if (mode->mode == target_mode && mode->is_6ghz == is_6ghz)
+ return mode;
+ }
+
+ return NULL;
+}
+
+
+static bool
+hostapd_ctrl_is_freq_in_mode(struct hostapd_hw_modes *mode,
+ struct hostapd_multi_hw_info *current_hw_info,
+ int freq)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ if (!chan_in_current_hw_info(current_hw_info, chan))
+ continue;
+
+ if (chan->freq == freq)
+ return true;
+ }
+ return false;
+}
+
+
+static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+ u16 punct_bitmap)
+{
+ u32 start_freq;
+ bool is_6g = is_6ghz_freq(params->freq);
+
+ if (is_6g) {
+ const int bw_idx[] = { 20, 40, 80, 160, 320 };
+ int idx, bw;
+
+ /* The 6 GHz band requires HE to be enabled. */
+ params->he_enabled = 1;
+
+ if (params->center_freq1) {
+ if (params->freq == 5935)
+ idx = (params->center_freq1 - 5925) / 5;
+ else
+ idx = (params->center_freq1 - 5950) / 5;
+
+ bw = center_idx_to_bw_6ghz(idx);
+ if (bw < 0 || bw >= (int) ARRAY_SIZE(bw_idx) ||
+ bw_idx[bw] != params->bandwidth)
+ return -1;
+ }
+ } else { /* Non-6 GHz channel */
+ /* An EHT STA is also an HE STA as defined in
+ * IEEE Std 802.11be-2024, 4.3.16a (Extremely high throughput
+ * (EHT) STA). */
+ if (params->he_enabled || params->eht_enabled) {
+ params->he_enabled = 1;
+ /* An HE STA is also a VHT STA if operating in the 5 GHz
+ * band and an HE STA is also an HT STA in the 2.4 GHz
+ * band as defined in IEEE Std 802.11ax-2021, 4.3.15a.
+ * A VHT STA is an HT STA as defined in IEEE
+ * Std 802.11, 4.3.15. */
+ if (IS_5GHZ(params->freq))
+ params->vht_enabled = 1;
+
+ params->ht_enabled = 1;
+ }
+ }
+
+ switch (params->bandwidth) {
+ case 0:
+ /* bandwidth not specified: use 20 MHz by default */
+ /* fall-through */
+ case 20:
+ if (params->center_freq1 &&
+ params->center_freq1 != params->freq)
+ return -1;
+
+ if (params->center_freq2 || params->sec_channel_offset)
+ return -1;
+
+ if (punct_bitmap)
+ return -1;
+ break;
+ case 40:
+ if (params->center_freq2 ||
+ (!is_6g && !params->sec_channel_offset))
+ return -1;
+
+ if (punct_bitmap)
+ return -1;
+
+ if (!params->center_freq1)
+ break;
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq + 10 != params->center_freq1)
+ return -1;
+ break;
+ case -1:
+ if (params->freq - 10 != params->center_freq1)
+ return -1;
+ break;
+ case 0:
+ if (!is_6g)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case 80:
+ if (!params->center_freq1 ||
+ (!is_6g && !params->sec_channel_offset))
+ return 1;
+
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq - 10 != params->center_freq1 &&
+ params->freq + 30 != params->center_freq1)
+ return 1;
+ break;
+ case -1:
+ if (params->freq + 10 != params->center_freq1 &&
+ params->freq - 30 != params->center_freq1)
+ return -1;
+ break;
+ case 0:
+ if (!is_6g)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (params->center_freq2 && punct_bitmap)
+ return -1;
+
+ /* Adjacent and overlapped are not allowed for 80+80 */
+ if (params->center_freq2 &&
+ params->center_freq1 - params->center_freq2 <= 80 &&
+ params->center_freq2 - params->center_freq1 <= 80)
+ return 1;
+ break;
+ case 160:
+ if (!params->center_freq1 || params->center_freq2 ||
+ (!is_6g && !params->sec_channel_offset))
+ return -1;
+
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq + 70 != params->center_freq1 &&
+ params->freq + 30 != params->center_freq1 &&
+ params->freq - 10 != params->center_freq1 &&
+ params->freq - 50 != params->center_freq1)
+ return -1;
+ break;
+ case -1:
+ if (params->freq + 50 != params->center_freq1 &&
+ params->freq + 10 != params->center_freq1 &&
+ params->freq - 30 != params->center_freq1 &&
+ params->freq - 70 != params->center_freq1)
+ return -1;
+ break;
+ case 0:
+ if (!is_6g)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case 320:
+ if (!params->center_freq1 || params->center_freq2)
+ return -1;
+
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq + 150 != params->center_freq1 &&
+ params->freq + 110 != params->center_freq1 &&
+ params->freq + 70 != params->center_freq1 &&
+ params->freq + 30 != params->center_freq1 &&
+ params->freq - 10 != params->center_freq1 &&
+ params->freq - 50 != params->center_freq1 &&
+ params->freq - 90 != params->center_freq1 &&
+ params->freq - 130 != params->center_freq1)
+ return -1;
+ break;
+ case -1:
+ if (params->freq + 130 != params->center_freq1 &&
+ params->freq + 90 != params->center_freq1 &&
+ params->freq + 50 != params->center_freq1 &&
+ params->freq + 10 != params->center_freq1 &&
+ params->freq - 30 != params->center_freq1 &&
+ params->freq - 70 != params->center_freq1 &&
+ params->freq - 110 != params->center_freq1 &&
+ params->freq - 150 != params->center_freq1)
+ return -1;
+ break;
+ case 0:
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (!punct_bitmap)
+ return 0;
+
+ if (!params->eht_enabled) {
+ wpa_printf(MSG_ERROR,
+ "Preamble puncturing supported only in EHT");
+ return -1;
+ }
+
+ if (params->freq >= 2412 && params->freq <= 2484) {
+ wpa_printf(MSG_ERROR,
+ "Preamble puncturing is not supported in 2.4 GHz");
+ return -1;
+ }
+
+ start_freq = params->center_freq1 - (params->bandwidth / 2);
+ if (!is_punct_bitmap_valid(params->bandwidth,
+ (params->freq - start_freq) / 20,
+ punct_bitmap)) {
+ wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_parse_csa_settings(struct hostapd_iface *iface,
+ const char *pos,
+ struct csa_settings *settings)
+{
+ struct hostapd_hw_modes *target_mode;
+ char *end;
+ int ret;
+
+ os_memset(settings, 0, sizeof(*settings));
+ settings->cs_count = strtol(pos, &end, 10);
+ if (pos == end) {
+ wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+ return -1;
+ }
- SET_CSA_SETTING(center_freq1);
- SET_CSA_SETTING(center_freq2);
- SET_CSA_SETTING(bandwidth);
- SET_CSA_SETTING(sec_channel_offset);
- SET_CSA_SETTING_EXT(punct_bitmap);
- settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
- settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
- settings->freq_params.he_enabled = !!os_strstr(pos, " he");
- settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
settings->block_tx = !!os_strstr(pos, " blocktx");
-#undef SET_CSA_SETTING
-#undef SET_CSA_SETTING_EXT
+
+ ret = hostapd_parse_freq_params(end, &settings->freq_params, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO,
+ "chanswitch: failed to parse frequency parameters");
+ return ret;
+ }
+
+ target_mode = get_target_hw_mode(iface, settings->freq_params.freq);
+ if (!target_mode) {
+ wpa_printf(MSG_DEBUG,
+ "chanswitch: Invalid frequency settings provided for hw mode");
+ return -1;
+ }
+
+ if (iface->num_hw_features > 1 &&
+ !hostapd_ctrl_is_freq_in_mode(target_mode, iface->current_hw_info,
+ settings->freq_params.freq)) {
+ wpa_printf(MSG_INFO,
+ "chanswitch: Invalid frequency settings provided for multi band phy");
+ return -1;
+ }
+
+ ret = hostapd_ctrl_check_freq_params(&settings->freq_params,
+ settings->freq_params.punct_bitmap);
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "chanswitch: invalid frequency settings provided");
+ return ret;
+ }
return 0;
}
@@ -1174,6 +1503,7 @@ int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
size_t pmk_len;
char *pos, *pos2;
int akmp = 0, expiration = 0;
+ int ret;
/*
* Entry format:
@@ -1209,8 +1539,18 @@ int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
return -1;
- return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
- pmkid, expiration, akmp, NULL);
+ ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+ pmkid, expiration, akmp, NULL, false);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+ pmkid, expiration, akmp, NULL, true);
+#endif /* CONFIG_IEEE80211BE */
+
+ return ret;
}
@@ -1609,6 +1949,11 @@ int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
struct vlan_description vlan_id;
for (sta = hapd->sta_list; sta; sta = sta->next) {
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+ struct mld_link_info *info;
+#endif /* CONFIG_IEEE80211BE */
+
if (hostapd_maclist_found(hapd->conf->deny_mac,
hapd->conf->num_deny_mac, sta->addr,
&vlan_id) &&
@@ -1616,6 +1961,24 @@ int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
!vlan_compare(&vlan_id, sta->vlan_desc)))
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_UNSPECIFIED);
+#ifdef CONFIG_IEEE80211BE
+ for (link_id = 0; hapd->conf->mld_ap &&
+ link_id < MAX_NUM_MLD_LINKS &&
+ sta->mld_info.mld_sta; link_id++) {
+ info = &sta->mld_info.links[link_id];
+ if (!info->valid || link_id != hapd->mld_link_id)
+ continue;
+
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac,
+ info->peer_addr,
+ &vlan_id) &&
+ (!vlan_id.notempty ||
+ !vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+#endif /* CONFIG_IEEE80211BE */
}
return 0;
diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.h b/contrib/wpa/src/ap/ctrl_iface_ap.h
index 614f0426c1a5..4de140844f0b 100644
--- a/contrib/wpa/src/ap/ctrl_iface_ap.h
+++ b/contrib/wpa/src/ap/ctrl_iface_ap.h
@@ -26,7 +26,11 @@ int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
const char *txtaddr);
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
size_t buflen);
-int hostapd_parse_csa_settings(const char *pos,
+int hostapd_parse_freq_params(const char *pos,
+ struct hostapd_freq_params *params,
+ unsigned int freq);
+int hostapd_parse_csa_settings(struct hostapd_iface *iface,
+ const char *pos,
struct csa_settings *settings);
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c
index af9dc16f5987..d72c8ddb4464 100644
--- a/contrib/wpa/src/ap/dfs.c
+++ b/contrib/wpa/src/ap/dfs.c
@@ -2,6 +2,7 @@
* DFS - Dynamic Frequency Selection
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
* Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -253,6 +254,13 @@ static int dfs_find_channel(struct hostapd_iface *iface,
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
+ if (!chan_in_current_hw_info(iface->current_hw_info, chan)) {
+ wpa_printf(MSG_DEBUG,
+ "DFS: channel %d (%d) is not under current hardware index",
+ chan->freq, chan->chan);
+ continue;
+ }
+
/* Skip HT40/VHT incompatible channels */
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel &&
@@ -974,6 +982,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
u8 new_vht_oper_chwidth;
unsigned int i;
unsigned int num_err = 0;
+ u8 op_class, chan;
wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
@@ -982,6 +991,13 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
+ if (ieee80211_freq_to_channel_ext(freq, secondary_channel,
+ new_vht_oper_chwidth, &op_class,
+ &chan) != NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG, "Update op_class %d->%d",
+ iface->conf->op_class, op_class);
+ iface->conf->op_class = op_class;
+ }
/* Setup CSA request */
os_memset(&csa_settings, 0, sizeof(csa_settings));
@@ -1039,6 +1055,14 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
oper_centr_freq_seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
oper_centr_freq_seg1_idx);
+ if (ieee80211_freq_to_channel_ext(freq, secondary_channel,
+ new_vht_oper_chwidth,
+ &op_class, &chan) !=
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG, "Update op_class %d->%d",
+ iface->conf->op_class, op_class);
+ iface->conf->op_class = op_class;
+ }
hostapd_disable_iface(iface);
hostapd_enable_iface(iface);
@@ -1107,6 +1131,25 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
static bool
+hostapd_is_freq_in_current_hw_info(struct hostapd_iface *iface, int freq)
+{
+ struct hostapd_channel_data *chan;
+
+ if (!iface->current_mode)
+ return false;
+
+ chan = hw_mode_get_channel(iface->current_mode, freq, NULL);
+
+ /* If channel data is not found for the given frequency, consider it is
+ * out of the current hardware info. */
+ if (!chan)
+ return false;
+
+ return chan_in_current_hw_info(iface->current_hw_info, chan);
+}
+
+
+static bool
hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
{
return dfs_use_radar_background(iface) &&
@@ -1143,6 +1186,14 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
+ if (!hostapd_is_freq_in_current_hw_info(iface, freq)) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ DFS_EVENT_CAC_COMPLETED
+ "Ignoring since freq=%d info is out of own range",
+ freq);
+ return 0;
+ }
+
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d",
success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
@@ -1214,6 +1265,14 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
+ if (!hostapd_is_freq_in_current_hw_info(iface, freq)) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ DFS_EVENT_PRE_CAC_EXPIRED
+ "Ignoring since freq=%d info is out of own range",
+ freq);
+ return 0;
+ }
+
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
@@ -1276,6 +1335,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
u8 oper_centr_freq_seg1_idx = 0;
enum dfs_channel_type channel_type = DFS_ANY_CHANNEL;
int err = 1;
+ u8 op_class, chan;
/* Radar detected during active CAC */
iface->cac_started = 0;
@@ -1308,6 +1368,14 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
oper_centr_freq_seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
oper_centr_freq_seg1_idx);
+ if (ieee80211_freq_to_channel_ext(channel->freq, secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf),
+ &op_class, &chan) !=
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG, "Update op_class %d->%d",
+ iface->conf->op_class, op_class);
+ iface->conf->op_class = op_class;
+ }
err = 0;
hostapd_setup_interface_complete(iface, err);
@@ -1445,6 +1513,14 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
+ if (!hostapd_is_freq_in_current_hw_info(iface, freq)) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ DFS_EVENT_RADAR_DETECTED
+ "Ignoring since freq=%d info is out of own range",
+ freq);
+ return 0;
+ }
+
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
@@ -1483,6 +1559,14 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
+ if (!hostapd_is_freq_in_current_hw_info(iface, freq)) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ DFS_EVENT_NOP_FINISHED
+ "Ignoring since freq=%d info is out of own range",
+ freq);
+ return 0;
+ }
+
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
@@ -1536,10 +1620,19 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface)
}
-int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+int hostapd_dfs_start_cac(struct hostapd_data *hapd, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
+ struct hostapd_iface *iface = hapd->iface;
+
+ if (!hostapd_is_freq_in_current_hw_info(iface, freq)) {
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ "Ignoring since freq=%d info is out of own range",
+ freq);
+ return 0;
+ }
+
if (hostapd_dfs_is_background_event(iface, freq)) {
iface->radar_background.cac_started = 1;
} else {
@@ -1558,7 +1651,7 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
}
/* TODO: How to check CAC time for ETSI weather channels? */
iface->dfs_cac_ms = 60000;
- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
"seg1=%d cac_time=%ds%s",
freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
diff --git a/contrib/wpa/src/ap/dfs.h b/contrib/wpa/src/ap/dfs.h
index 606c1b39399f..a3cb13cd87bd 100644
--- a/contrib/wpa/src/ap/dfs.h
+++ b/contrib/wpa/src/ap/dfs.h
@@ -26,7 +26,7 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
int chan_offset, int chan_width, int cf1, int cf2);
int hostapd_is_dfs_required(struct hostapd_iface *iface);
int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
-int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+int hostapd_dfs_start_cac(struct hostapd_data *hapd, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2);
int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
diff --git a/contrib/wpa/src/ap/dpp_hostapd.c b/contrib/wpa/src/ap/dpp_hostapd.c
index d1bffa81152b..eff28c6a9223 100644
--- a/contrib/wpa/src/ap/dpp_hostapd.c
+++ b/contrib/wpa/src/ap/dpp_hostapd.c
@@ -2,7 +2,7 @@
* hostapd / DPP integration
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -934,6 +934,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
}
auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
@@ -1382,6 +1383,21 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
}
+static void hostapd_gas_req_wait(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Timeout while waiting for Config Request");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
{
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
@@ -1400,6 +1416,9 @@ static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
if (!hapd->dpp_auth->configurator)
hostapd_dpp_start_gas_client(hapd);
+ else
+ eloop_register_timeout(10, 0, hostapd_gas_req_wait,
+ hapd, NULL);
}
@@ -2142,11 +2161,22 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP, pkhash) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap &&
+ wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to add PMKSA cache entry (MLD)");
+ goto done;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
DPP_STATUS_OK);
done:
@@ -2916,11 +2946,22 @@ hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
- WPA_KEY_MGMT_DPP, pkhash) < 0) {
+ WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
goto done;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap &&
+ wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to add PMKSA cache entry (MLD)");
+ goto done;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
MACSTR, MAC2STR(src));
@@ -2940,6 +2981,10 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr;
unsigned int pkex_t;
+ /* Discard DPP Action frames if there is no global DPP context */
+ if (!hapd->iface->interfaces || !hapd->iface->interfaces->dpp)
+ return;
+
if (len < DPP_HDR_LEN)
return;
if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
@@ -3079,6 +3124,7 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
struct wpabuf *resp;
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+ eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
!ether_addr_equal(sa, auth->peer_mac_addr)) {
#ifdef CONFIG_DPP2
@@ -3359,6 +3405,7 @@ void hostapd_dpp_stop(struct hostapd_data *hapd)
#ifdef CONFIG_DPP3
hostapd_dpp_push_button_stop(hapd);
#endif /* CONFIG_DPP3 */
+ eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
}
@@ -3512,6 +3559,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
@@ -3940,6 +3988,7 @@ int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
}
eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
hapd, NULL);
+ hostapd_drv_dpp_listen(hapd, true);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
@@ -3955,6 +4004,7 @@ void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
if (hostapd_dpp_pb_active(hapd)) {
wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+ hostapd_drv_dpp_listen(hapd, false);
if (!ifaces->dpp_pb_result_indicated)
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_PB_RESULT "failed");
diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c
index 8b49b531e915..116a055033a0 100644
--- a/contrib/wpa/src/ap/drv_callbacks.c
+++ b/contrib/wpa/src/ap/drv_callbacks.c
@@ -1,6 +1,7 @@
/*
* hostapd / Callback functions for driver wrappers
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -18,6 +19,7 @@
#include "common/dpp.h"
#include "common/sae.h"
#include "common/hw_features_common.h"
+#include "common/nan_de.h"
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
@@ -43,6 +45,7 @@
#include "fils_hlp.h"
#include "neighbor_db.h"
#include "nan_usd_ap.h"
+#include "interference.h"
#ifdef CONFIG_FILS
@@ -73,6 +76,8 @@ void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
elems.fils_session,
sta->fils_hlp_resp);
+ if (!p)
+ return;
reply_res = hostapd_sta_assoc(hapd, sta->addr,
sta->fils_pending_assoc_is_reassoc,
@@ -186,7 +191,22 @@ static int hostapd_update_sta_links_status(struct hostapd_data *hapd,
/* Parse Subelements */
while (rem_len > 2) {
- size_t ie_len = 2 + pos[1];
+ size_t ie_len, subelem_defrag_len;
+ int num_frag_subelems;
+
+ num_frag_subelems =
+ ieee802_11_defrag_mle_subelem(mlebuf, pos,
+ &subelem_defrag_len);
+ if (num_frag_subelems < 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to parse MLE subelem");
+ break;
+ }
+
+ ie_len = 2 + subelem_defrag_len;
+ if ((size_t) num_frag_subelems * 2 > rem_len)
+ break;
+ rem_len -= num_frag_subelems * 2;
if (rem_len < ie_len)
break;
@@ -197,13 +217,13 @@ static int hostapd_update_sta_links_status(struct hostapd_data *hapd,
size_t sta_profile_len;
u16 sta_ctrl;
- if (pos[1] < BASIC_MLE_STA_CTRL_LEN + 1) {
+ if (subelem_defrag_len < BASIC_MLE_STA_CTRL_LEN + 1) {
wpa_printf(MSG_DEBUG,
"MLO: Invalid per-STA profile IE");
goto next_subelem;
}
- sta_profile_len = pos[1];
+ sta_profile_len = subelem_defrag_len;
sta_profile = &pos[2];
sta_ctrl = WPA_GET_LE16(sta_profile);
link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
@@ -248,6 +268,52 @@ out:
#endif /* CONFIG_IEEE80211BE */
+#if defined(HOSTAPD) || defined(CONFIG_IEEE80211BE)
+static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+ const u8 *src, bool rsn,
+ struct sta_info **sta_ret)
+{
+ struct hostapd_data *hapd;
+ struct sta_info *sta;
+ unsigned int j;
+
+ if (sta_ret)
+ *sta_ret = NULL;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+ (!rsn || sta->wpa_sm)) {
+ if (sta_ret)
+ *sta_ret = sta;
+ return hapd;
+ }
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *p_hapd;
+
+ for_each_mld_link(p_hapd, hapd) {
+ if (p_hapd == hapd)
+ continue;
+
+ sta = ap_get_sta(p_hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+ (!rsn || sta->wpa_sm)) {
+ if (sta_ret)
+ *sta_ret = sta;
+ return p_hapd;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ return NULL;
+}
+#endif /* HOSTAPD || CONFIG_IEEE80211BE */
+
+
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *req_ies, size_t req_ies_len,
const u8 *resp_ies, size_t resp_ies_len,
@@ -268,6 +334,15 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
struct hostapd_iface *iface = hapd->iface;
#endif /* CONFIG_OWE */
bool updated = false;
+ bool driver_acl;
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_group && (!hapd->started || hapd->disabled)) {
+ wpa_printf(MSG_DEBUG,
+ "hostapd_notif_assoc: Ignore assoc event - P2P GO not started or disabled");
+ return 0;
+ }
+#endif /* CONFIG_P2P */
if (addr == NULL) {
/*
@@ -317,12 +392,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
ie = elems.wpa_ie - 2;
ielen = elems.wpa_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
-#ifdef CONFIG_HS20
- } else if (elems.osen) {
- ie = elems.osen - 2;
- ielen = elems.osen_len + 2;
- wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
-#endif /* CONFIG_HS20 */
} else {
ie = NULL;
ielen = 0;
@@ -340,6 +409,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
* will not remove the STA immediately.
*/
sta->timeout_next = STA_NULLFUNC;
+ } else if (!hostapd_acceptable_sta_addr(hapd, addr, addr, false)) {
+ wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+ MAC2STR(addr));
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_UNSPECIFIED);
+ return -1;
} else {
sta = ap_sta_add(hapd, addr);
if (sta == NULL) {
@@ -404,13 +478,57 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
* ACL if the driver supports ACL offload to avoid potentially
* conflicting ACL rules.
*/
- if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
+ driver_acl = hapd->iface->drv_max_acl_mac_addrs > 0;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ driver_acl = false;
+#endif /* CONFIG_IEEE80211BE */
+ if (!driver_acl &&
hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
MAC2STR(addr));
reason = WLAN_REASON_UNSPECIFIED;
goto fail;
}
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * The idea is that ACL is per link. For MLO associations, check
+ * whether peer MLD MAC address is acceptable in all requested links.
+ * For each peer link address, check the corresponding association
+ * local link's ACL configuration whether it is acceptable.
+ */
+ if (!driver_acl && hapd->conf->mld_ap && link_addr) {
+ int link_id;
+ struct mld_link_info *info;
+ struct hostapd_data *bss;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ info = &sta->mld_info.links[link_id];
+ if (!info->valid)
+ continue;
+
+ bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (bss && bss != hapd &&
+ hostapd_check_acl(bss, addr, NULL) !=
+ HOSTAPD_ACL_ACCEPT) {
+ wpa_printf(MSG_INFO, "STA " MACSTR
+ " not allowed to connect",
+ MAC2STR(addr));
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+ if (bss &&
+ hostapd_check_acl(bss, info->peer_addr, NULL) !=
+ HOSTAPD_ACL_ACCEPT) {
+ wpa_printf(MSG_INFO, "link addr " MACSTR
+ " not allowed to connect",
+ MAC2STR(info->peer_addr));
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_P2P
if (elems.p2p) {
@@ -481,7 +599,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
goto fail;
}
#ifdef CONFIG_WPS
@@ -513,6 +631,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
"Failed to initialize WPA state machine");
return -1;
}
+ wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
+ elems.rsn_selection_len);
+ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
@@ -528,9 +649,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
elems.mdie, elems.mdie_len,
- elems.owe_dh, elems.owe_dh_len, NULL);
+ elems.owe_dh, elems.owe_dh_len, NULL,
+ ap_sta_is_mld(hapd, sta));
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
switch (res) {
case WPA_IE_OK:
reason = WLAN_REASON_UNSPECIFIED;
@@ -538,23 +660,23 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
break;
case WPA_INVALID_IE:
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
break;
case WPA_INVALID_GROUP:
reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
- status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_INVALID_GROUP_CIPHER;
break;
case WPA_INVALID_PAIRWISE:
reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
- status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_INVALID_PAIRWISE_CIPHER;
break;
case WPA_INVALID_AKMP:
reason = WLAN_REASON_AKMP_NOT_VALID;
- status = WLAN_STATUS_AKMP_NOT_VALID;
+ status = WLAN_STATUS_INVALID_AKMP;
break;
case WPA_NOT_ENABLED:
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
break;
case WPA_ALLOC_FAIL:
reason = WLAN_REASON_UNSPECIFIED;
@@ -562,19 +684,19 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
break;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
break;
case WPA_INVALID_MGMT_GROUP_CIPHER:
reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
- status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ status = WLAN_STATUS_CIPHER_OUT_OF_POLICY;
break;
case WPA_INVALID_MDIE:
reason = WLAN_REASON_INVALID_MDE;
- status = WLAN_STATUS_INVALID_MDIE;
+ status = WLAN_STATUS_INVALID_MDE;
break;
case WPA_INVALID_PROTO:
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
break;
case WPA_INVALID_PMKID:
reason = WLAN_REASON_INVALID_PMKID;
@@ -598,6 +720,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
else
sta->flags &= ~WLAN_STA_MFP;
+ if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+ sta->flags |= WLAN_STA_SPP_AMSDU;
+ else
+ sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
@@ -605,9 +732,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
if (status != WLAN_STATUS_SUCCESS) {
if (status == WLAN_STATUS_INVALID_PMKID)
reason = WLAN_REASON_INVALID_IE;
- if (status == WLAN_STATUS_INVALID_MDIE)
+ if (status == WLAN_STATUS_INVALID_MDE)
reason = WLAN_REASON_INVALID_IE;
- if (status == WLAN_STATUS_INVALID_FTIE)
+ if (status == WLAN_STATUS_INVALID_FTE)
reason = WLAN_REASON_INVALID_IE;
goto fail;
}
@@ -619,14 +746,27 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
WLAN_RSNX_CAPAB_SAE_H2E)) {
- wpa_printf(MSG_INFO, "SAE: " MACSTR
- " indicates support for SAE H2E, but did not use it",
- MAC2STR(sta->addr));
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- reason = WLAN_REASON_UNSPECIFIED;
- goto fail;
+ if (hapd->conf->sae_accept_h2e_without_use) {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it - accepting due to sae_accept_h2e_without_use",
+ MAC2STR(sta->addr));
+ } else {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it",
+ MAC2STR(sta->addr));
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
}
#endif /* CONFIG_SAE */
+
+ wpa_auth_set_ssid_protection(
+ sta->wpa_sm,
+ hapd->conf->ssid_protection &&
+ ieee802_11_rsnx_capab_len(
+ elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SSID_PROTECTION));
} else if (hapd->conf->wps_state) {
#ifdef CONFIG_WPS
struct wpabuf *wps;
@@ -639,7 +779,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
#ifdef CONFIG_WPS_STRICT
if (wps && wps_validate_assoc_req(wps) < 0) {
reason = WLAN_REASON_INVALID_IE;
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
wpabuf_free(wps);
goto fail;
}
@@ -655,29 +795,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta->flags |= WLAN_STA_MAYBE_WPS;
wpabuf_free(wps);
#endif /* CONFIG_WPS */
-#ifdef CONFIG_HS20
- } else if (hapd->conf->osen) {
- if (elems.osen == NULL) {
- hostapd_logger(
- hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "No HS 2.0 OSEN element in association request");
- return WLAN_STATUS_INVALID_IE;
- }
-
- wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
- if (sta->wpa_sm == NULL)
- sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr, NULL);
- if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_WARNING,
- "Failed to initialize WPA state machine");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
- if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
- elems.osen - 2, elems.osen_len + 2) < 0)
- return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
}
#ifdef CONFIG_WPS
skip_wpa_check:
@@ -696,7 +813,7 @@ skip_wpa_check:
#ifdef CONFIG_IEEE80211R_AP
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
sta->auth_alg, req_ies, req_ies_len,
- !elems.rsnxe);
+ !elems.rsnxe, reassoc, sta->vlan_id);
if (!p) {
wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -773,6 +890,9 @@ skip_wpa_check:
p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
elems.fils_session,
sta->fils_hlp_resp);
+ if (!p)
+ goto fail;
+
wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
buf, p - buf);
}
@@ -857,21 +977,22 @@ skip_wpa_check:
}
#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+ hostapd_set_sta_flags(hapd, sta);
+
#ifdef CONFIG_IEEE80211BE
if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
!!reassoc, WLAN_STATUS_SUCCESS,
- true)) {
+ true, NULL)) {
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
reason = WLAN_REASON_UNSPECIFIED;
goto fail;
}
#endif /* CONFIG_IEEE80211BE */
- new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
- sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
- sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-
- hostapd_set_sta_flags(hapd, sta);
if (updated)
ap_sta_set_authorized_event(hapd, sta, 1);
@@ -897,6 +1018,10 @@ skip_wpa_check:
}
#endif /* CONFIG_P2P */
+ if (elems.wfa_capab)
+ hostapd_wfa_capab(hapd, sta, elems.wfa_capab,
+ elems.wfa_capab + elems.wfa_capab_len);
+
return 0;
fail:
@@ -923,33 +1048,27 @@ static void hostapd_remove_sta(struct hostapd_data *hapd, struct sta_info *sta)
#ifdef CONFIG_IEEE80211BE
-static void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
- struct sta_info *sta,
- const u8 *addr)
+void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+ struct sta_info *sta, const u8 *addr)
{
- unsigned int link_id, i;
+ unsigned int i;
struct hostapd_data *tmp_hapd;
struct hapd_interfaces *interfaces = assoc_hapd->iface->interfaces;
/* Remove STA entry in non-assoc links */
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sta->mld_info.links[link_id].valid)
- continue;
+ for (i = 0; i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
- for (i = 0; i < interfaces->count; i++) {
- struct sta_info *tmp_sta;
+ tmp_hapd = interfaces->iface[i]->bss[0];
- tmp_hapd = interfaces->iface[i]->bss[0];
-
- if (!tmp_hapd->conf->mld_ap ||
- assoc_hapd == tmp_hapd ||
- assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
- continue;
+ if (!tmp_hapd->conf->mld_ap ||
+ assoc_hapd == tmp_hapd ||
+ !hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
+ continue;
- tmp_sta = ap_get_sta(tmp_hapd, addr);
- if (tmp_sta)
- ap_free_sta(tmp_hapd, tmp_sta);
- }
+ tmp_sta = ap_get_sta(tmp_hapd, addr);
+ if (tmp_sta)
+ ap_free_sta(tmp_hapd, tmp_sta);
}
/* Remove STA in assoc link */
@@ -980,7 +1099,7 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
sta = ap_get_sta(hapd, addr);
#ifdef CONFIG_IEEE80211BE
- if (hostapd_is_mld_ap(hapd)) {
+ if (hostapd_is_multiple_link_mld(hapd)) {
struct hostapd_data *assoc_hapd;
unsigned int i;
@@ -994,7 +1113,7 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
struct hostapd_bss_config *hconf = h_hapd->conf;
if (!hconf->mld_ap ||
- hconf->mld_id != hapd->conf->mld_id)
+ !hostapd_is_ml_partner(hapd, h_hapd))
continue;
sta = ap_get_sta(h_hapd, addr);
@@ -1037,6 +1156,20 @@ legacy:
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *orig_hapd = hapd;
+
+ if (!sta && hapd->conf->mld_ap) {
+ hapd = hostapd_find_by_sta(hapd->iface, addr, true, &sta);
+ if (!hapd) {
+ wpa_printf(MSG_DEBUG,
+ "No partner link BSS found for STA " MACSTR
+ " - fallback to received context",
+ MAC2STR(addr));
+ hapd = orig_hapd;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
return;
@@ -1402,10 +1535,9 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd,
if (mode->mode == acs_res->hw_mode) {
if (hapd->iface->freq > 0 &&
- !hw_get_chan(mode->mode,
- hapd->iface->freq,
- hapd->iface->hw_features,
- hapd->iface->num_hw_features))
+ !hw_mode_get_channel(mode,
+ hapd->iface->freq,
+ NULL))
continue;
hapd->iface->current_mode = mode;
break;
@@ -1587,8 +1719,6 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
{
struct sta_info *sta;
u16 status = WLAN_STATUS_SUCCESS;
- u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
- size_t resp_ies_len = 0;
sta = ap_get_sta(hapd, rx_auth->peer);
if (!sta) {
@@ -1633,7 +1763,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
fail:
hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
- status, resp_ies, resp_ies_len);
+ status, NULL, 0);
}
@@ -1714,14 +1844,14 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
- mgmt->u.action.u.vs_public_action.variable[0] == NAN_OUI_TYPE) {
+ mgmt->u.action.u.vs_public_action.variable[0] == NAN_SDF_OUI_TYPE) {
const u8 *pos, *end;
pos = mgmt->u.action.u.vs_public_action.variable;
end = drv_mgmt->frame + drv_mgmt->frame_len;
pos++;
- hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
- pos, end - pos);
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+ drv_mgmt->freq, pos, end - pos);
return;
}
#endif /* CONFIG_NAN_USD */
@@ -1788,6 +1918,11 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
return HAPD_BROADCAST;
+#ifdef CONFIG_NAN_USD
+ if (nan_de_is_nan_network_id(bssid))
+ return HAPD_BROADCAST; /* Process NAN Network ID like broadcast
+ */
+#endif /* CONFIG_NAN_USD */
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd;
@@ -1921,18 +2056,19 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
{
struct ieee80211_hdr *hdr;
struct hostapd_data *orig_hapd, *tmp_hapd;
+ const u8 *bssid;
orig_hapd = hapd;
hdr = (struct ieee80211_hdr *) buf;
hapd = switch_link_hapd(hapd, link_id);
- tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
+ bssid = get_hdr_bssid(hdr, len);
+ tmp_hapd = get_hapd_bssid(hapd->iface, bssid, link_id);
if (tmp_hapd) {
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
- } else if (hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld->mld_addr,
- get_hdr_bssid(hdr, len))) {
+ } else if (hapd->conf->mld_ap && bssid &&
+ ether_addr_equal(hapd->mld->mld_addr, bssid)) {
/* AP MLD address match - use hapd pointer as-is */
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -1979,50 +2115,6 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
}
-static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
- const u8 *src, bool rsn,
- struct sta_info **sta_ret)
-{
- struct hostapd_data *hapd;
- struct sta_info *sta;
- unsigned int j;
-
- if (sta_ret)
- *sta_ret = NULL;
-
- for (j = 0; j < iface->num_bss; j++) {
- hapd = iface->bss[j];
- sta = ap_get_sta(hapd, src);
- if (sta && (sta->flags & WLAN_STA_ASSOC) &&
- (!rsn || sta->wpa_sm)) {
- if (sta_ret)
- *sta_ret = sta;
- return hapd;
- }
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap) {
- struct hostapd_data *p_hapd;
-
- for_each_mld_link(p_hapd, hapd) {
- if (p_hapd == hapd)
- continue;
-
- sta = ap_get_sta(p_hapd, src);
- if (sta && (sta->flags & WLAN_STA_ASSOC) &&
- (!rsn || sta->wpa_sm)) {
- if (sta_ret)
- *sta_ret = sta;
- return p_hapd;
- }
- }
- }
-#endif /* CONFIG_IEEE80211BE */
- }
-
- return NULL;
-}
-
-
static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
const u8 *data, size_t data_len,
enum frame_encryption encrypted,
@@ -2166,6 +2258,8 @@ void hostapd_event_get_survey(struct hostapd_iface *iface,
continue;
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
+ if (!(chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED))
+ continue;
dl_list_del(&survey->list);
dl_list_add_tail(&chan->survey_list, &survey->list);
@@ -2191,6 +2285,10 @@ static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
hostapd_switch_channel_fallback(hapd->iface,
&hapd->cs_freq_params);
}
+
+ /* Clear beacon_set_done so that the RNR and other beacon parameters
+ * are properly updated. */
+ hapd->beacon_set_done = 0;
}
@@ -2248,11 +2346,26 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
- hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+ hostapd_dfs_start_cac(hapd, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
+
+static void hostapd_event_incumbt_sig_intf_detected(
+ struct hostapd_data *hapd, struct incumbt_sig_intf_event *intf_info)
+{
+ wpa_printf(MSG_DEBUG, "Interference due to incumbent signal detected on %d MHz with intf bitmap 0x%x on link_id %d",
+ intf_info->freq, intf_info->chan_bw_interference_bitmap,
+ intf_info->link_id);
+ if (hostapd_incumbt_sig_intf_detected(
+ hapd->iface, intf_info->freq, intf_info->chan_width,
+ intf_info->cf1, intf_info->cf2,
+ intf_info->chan_bw_interference_bitmap))
+ wpa_printf(MSG_INFO,
+ "Failed to handle incumbent signal interference");
+}
+
#endif /* NEED_AP_MLME */
@@ -2297,7 +2410,7 @@ static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
}
if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
- status = WLAN_STATUS_AKMP_NOT_VALID;
+ status = WLAN_STATUS_INVALID_AKMP;
goto err;
}
if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
@@ -2394,8 +2507,14 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
if (bss->cca_color == 0)
continue;
- if (success)
+ if (success) {
hapd->iface->conf->he_op.he_bss_color = bss->cca_color;
+ /* Clear the disabled flag set during CCA so that
+ * subsequent beacon rebuilds advertise the new color
+ * as enabled.
+ */
+ hapd->iface->conf->he_op.he_bss_color_disabled = 0;
+ }
bss->cca_in_progress = 0;
if (ieee802_11_set_beacon(bss)) {
@@ -2409,6 +2528,88 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
#endif /* CONFIG_IEEE80211AX */
+static void hostapd_iface_enable(struct hostapd_data *hapd)
+{
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+ if (hapd->disabled && hapd->started) {
+ hapd->disabled = 0;
+ /*
+ * Try to re-enable interface if the driver stopped it
+ * when the interface got disabled.
+ */
+ if (hapd->wpa_auth)
+ wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+ else
+ hostapd_reconfig_encryption(hapd);
+ hapd->reenable_beacon = 1;
+ ieee802_11_set_beacon(hapd);
+#ifdef NEED_AP_MLME
+ } else if (hapd->disabled && hapd->iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
+ hostapd_handle_dfs(hapd->iface);
+#endif /* NEED_AP_MLME */
+ }
+}
+
+
+static void hostapd_iface_disable(struct hostapd_data *hapd)
+{
+ hostapd_free_stas(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+ hapd->disabled = 1;
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static void hostapd_mld_iface_enable(struct hostapd_data *hapd)
+{
+ struct hostapd_data *first_link, *link_bss;
+
+ first_link = hostapd_mld_is_first_bss(hapd) ? hapd :
+ hostapd_mld_get_first_bss(hapd);
+
+ /* Links have been removed. Re-add all links and enable them, but
+ * enable the first link BSS before doing that. */
+ if (hostapd_drv_link_add(first_link, first_link->mld_link_id,
+ first_link->own_addr)) {
+ wpa_printf(MSG_ERROR, "MLD: Failed to re-add link %d in MLD %s",
+ first_link->mld_link_id, first_link->conf->iface);
+ return;
+ }
+
+ hostapd_iface_enable(first_link);
+
+ /* Add other affiliated links */
+ for_each_mld_link(link_bss, first_link) {
+ if (link_bss == first_link)
+ continue;
+
+ if (hostapd_drv_link_add(link_bss, link_bss->mld_link_id,
+ link_bss->own_addr)) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to re-add link %d in MLD %s",
+ link_bss->mld_link_id,
+ link_bss->conf->iface);
+ continue;
+ }
+
+ hostapd_iface_enable(link_bss);
+ }
+}
+
+
+static void hostapd_mld_iface_disable(struct hostapd_data *hapd)
+{
+ struct hostapd_data *link_bss;
+
+ for_each_mld_link(link_bss, hapd)
+ hostapd_iface_disable(link_bss);
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -2497,6 +2698,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
hostapd_client_poll_ok(hapd, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
+ hapd = switch_link_hapd(hapd, data->rx_from_unknown.link_id);
hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
@@ -2639,6 +2841,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#ifdef NEED_AP_MLME
case EVENT_INTERFACE_UNAVAILABLE:
hostapd_event_iface_unavailable(hapd);
+ /* Update beacon information in all other interfaces to cover
+ * removal/disabling of this BSS. */
+ hostapd_refresh_all_iface_beacons(hapd->iface);
break;
case EVENT_DFS_RADAR_DETECTED:
if (!data)
@@ -2684,32 +2889,32 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
break;
+ case EVENT_INCUMBT_SIG_INTF_DETECTED:
+ if (!data)
+ break;
+ hapd = switch_link_hapd(hapd,
+ data->incumbt_sig_intf_event.link_id);
+ hostapd_event_incumbt_sig_intf_detected(
+ hapd, &data->incumbt_sig_intf_event);
+ break;
#endif /* NEED_AP_MLME */
case EVENT_INTERFACE_ENABLED:
- wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
- if (hapd->disabled && hapd->started) {
- hapd->disabled = 0;
- /*
- * Try to re-enable interface if the driver stopped it
- * when the interface got disabled.
- */
- if (hapd->wpa_auth)
- wpa_auth_reconfig_group_keys(hapd->wpa_auth);
- else
- hostapd_reconfig_encryption(hapd);
- hapd->reenable_beacon = 1;
- ieee802_11_set_beacon(hapd);
-#ifdef NEED_AP_MLME
- } else if (hapd->disabled && hapd->iface->cac_started) {
- wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
- hostapd_handle_dfs(hapd->iface);
-#endif /* NEED_AP_MLME */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ hostapd_mld_iface_enable(hapd);
+ break;
}
+#endif /* CONFIG_IEEE80211BE */
+ hostapd_iface_enable(hapd);
break;
case EVENT_INTERFACE_DISABLED:
- hostapd_free_stas(hapd);
- wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
- hapd->disabled = 1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ hostapd_mld_iface_disable(hapd);
+ break;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ hostapd_iface_disable(hapd);
break;
#ifdef CONFIG_ACS
case EVENT_ACS_CHANNEL_SELECTED:
@@ -2762,6 +2967,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
hostapd_event_color_change(hapd, true);
break;
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ case EVENT_MLD_INTERFACE_FREED:
+ wpa_printf(MSG_DEBUG, "MLD: Interface %s freed",
+ hapd->conf->iface);
+ hostapd_mld_interface_freed(hapd);
+ break;
+#endif /* CONFIG_IEEE80211BE */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/contrib/wpa/src/ap/eap_user_db.c b/contrib/wpa/src/ap/eap_user_db.c
index a510ee3e29fd..c0e903040fbe 100644
--- a/contrib/wpa/src/ap/eap_user_db.c
+++ b/contrib/wpa/src/ap/eap_user_db.c
@@ -89,8 +89,6 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
user->next = (void *) 1;
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
set_user_methods(user, argv[i]);
- } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
- user->remediation = strlen(argv[i]) > 0;
} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
user->t_c_timestamp = strtol(argv[i], NULL, 10);
}
diff --git a/contrib/wpa/src/ap/gas_query_ap.c b/contrib/wpa/src/ap/gas_query_ap.c
index a471c793c413..5629e20909e3 100644
--- a/contrib/wpa/src/ap/gas_query_ap.c
+++ b/contrib/wpa/src/ap/gas_query_ap.c
@@ -536,16 +536,24 @@ int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
}
if (action == WLAN_PA_GAS_COMEBACK_RESP) {
- if (pos + 1 > data + len)
+ if (pos + 1 > data + len) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: No room for frag_id in the response from "
+ MACSTR, MAC2STR(sa));
return 0;
+ }
frag_id = *pos & 0x7f;
more_frags = (*pos & 0x80) >> 7;
pos++;
}
/* Comeback Delay */
- if (pos + 2 > data + len)
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: No room for Comeback Delay in the response from "
+ MACSTR, MAC2STR(sa));
return 0;
+ }
comeback_delay = WPA_GET_LE16(pos);
if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
diff --git a/contrib/wpa/src/ap/gas_serv.c b/contrib/wpa/src/ap/gas_serv.c
index 4642e49273d8..afb1b1186f6b 100644
--- a/contrib/wpa/src/ap/gas_serv.c
+++ b/contrib/wpa/src/ap/gas_serv.c
@@ -159,9 +159,14 @@ static void gas_serv_free_dialogs(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **_buf)
{
u8 *len;
+ struct wpabuf *buf;
+
+ if (wpabuf_resize(_buf, 2 + 2 + 4 + 1 + 1 + 6) < 0)
+ return;
+ buf = *_buf;
len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
wpabuf_put_be24(buf, OUI_WFA);
@@ -179,14 +184,6 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
- if (hapd->conf->hs20_osu_providers_count)
- wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
- if (hapd->conf->hs20_osu_providers_nai_count)
- wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
- if (hapd->conf->hs20_icons_count)
- wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
- if (hapd->conf->hs20_operator_icon_count)
- wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -207,7 +204,7 @@ static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
}
-static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf **buf,
u16 infoid)
{
struct anqp_element *elem;
@@ -215,19 +212,16 @@ static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
elem = get_anqp_elem(hapd, infoid);
if (!elem)
return;
- if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
- wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
- infoid);
+ if (wpabuf_resize(buf, 2 + 2 + wpabuf_len(elem->payload)) < 0)
return;
- }
- wpabuf_put_le16(buf, infoid);
- wpabuf_put_le16(buf, wpabuf_len(elem->payload));
- wpabuf_put_buf(buf, elem->payload);
+ wpabuf_put_le16(*buf, infoid);
+ wpabuf_put_le16(*buf, wpabuf_len(elem->payload));
+ wpabuf_put_buf(*buf, elem->payload);
}
-static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf **buf,
u16 infoid)
{
if (get_anqp_elem(hapd, infoid)) {
@@ -240,14 +234,16 @@ static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
static void anqp_add_capab_list(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **_buf)
{
u8 *len;
u16 id;
+ struct wpabuf *buf;
- if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+ if (anqp_add_override(hapd, _buf, ANQP_CAPABILITY_LIST))
return;
+ buf = *_buf;
len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
@@ -303,36 +299,54 @@ static void anqp_add_capab_list(struct hostapd_data *hapd,
wpabuf_put_le16(buf, id);
}
#ifdef CONFIG_HS20
- anqp_add_hs_capab_list(hapd, buf);
+ anqp_add_hs_capab_list(hapd, &buf);
#endif /* CONFIG_HS20 */
gas_anqp_set_element_len(buf, len);
}
-static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
return;
if (hapd->conf->venue_name) {
u8 *len;
- unsigned int i;
- len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
- wpabuf_put_u8(buf, hapd->conf->venue_group);
- wpabuf_put_u8(buf, hapd->conf->venue_type);
+ unsigned int i, count = 0;
+ size_t tlen = 2 + 2 + 1 + 1;
+
for (i = 0; i < hapd->conf->venue_name_count; i++) {
struct hostapd_lang_string *vn;
+
vn = &hapd->conf->venue_name[i];
- wpabuf_put_u8(buf, 3 + vn->name_len);
- wpabuf_put_data(buf, vn->lang, 3);
- wpabuf_put_data(buf, vn->name, vn->name_len);
+ if (tlen + 1 + 3 + vn->name_len > 2 + 2 + 65535) {
+ wpa_printf(MSG_INFO,
+ "ANQP: Too many venue names configured to fit into an ANQP-element");
+ break;
+ }
+ tlen += 1 + 3 + vn->name_len;
+ count++;
}
- gas_anqp_set_element_len(buf, len);
+ if (wpabuf_resize(buf, tlen) < 0)
+ return;
+
+ len = gas_anqp_add_element(*buf, ANQP_VENUE_NAME);
+ wpabuf_put_u8(*buf, hapd->conf->venue_group);
+ wpabuf_put_u8(*buf, hapd->conf->venue_type);
+ for (i = 0; i < count; i++) {
+ struct hostapd_lang_string *vn;
+
+ vn = &hapd->conf->venue_name[i];
+ wpabuf_put_u8(*buf, 3 + vn->name_len);
+ wpabuf_put_data(*buf, vn->lang, 3);
+ wpabuf_put_data(*buf, vn->name, vn->name_len);
+ }
+ gas_anqp_set_element_len(*buf, len);
}
}
-static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
return;
@@ -340,67 +354,115 @@ static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
if (hapd->conf->venue_url) {
u8 *len;
unsigned int i;
+ size_t tlen = 2 + 2;
- len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
for (i = 0; i < hapd->conf->venue_url_count; i++) {
struct hostapd_venue_url *url;
url = &hapd->conf->venue_url[i];
- wpabuf_put_u8(buf, 1 + url->url_len);
- wpabuf_put_u8(buf, url->venue_number);
- wpabuf_put_data(buf, url->url, url->url_len);
+ tlen += 1 + 1 + url->url_len;
}
- gas_anqp_set_element_len(buf, len);
+ if (wpabuf_resize(buf, tlen) < 0)
+ return;
+
+ len = gas_anqp_add_element(*buf, ANQP_VENUE_URL);
+ for (i = 0; i < hapd->conf->venue_url_count; i++) {
+ struct hostapd_venue_url *url;
+
+ url = &hapd->conf->venue_url[i];
+ wpabuf_put_u8(*buf, 1 + url->url_len);
+ wpabuf_put_u8(*buf, url->venue_number);
+ wpabuf_put_data(*buf, url->url, url->url_len);
+ }
+ gas_anqp_set_element_len(*buf, len);
}
}
static void anqp_add_network_auth_type(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
return;
if (hapd->conf->network_auth_type) {
- wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
- wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
- wpabuf_put_data(buf, hapd->conf->network_auth_type,
+ if (wpabuf_resize(buf, 2 + 2 +
+ hapd->conf->network_auth_type_len) < 0)
+ return;
+ wpabuf_put_le16(*buf, ANQP_NETWORK_AUTH_TYPE);
+ wpabuf_put_le16(*buf, hapd->conf->network_auth_type_len);
+ wpabuf_put_data(*buf, hapd->conf->network_auth_type,
hapd->conf->network_auth_type_len);
}
}
static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
- unsigned int i;
+ unsigned int i, count = 0;
u8 *len;
+ size_t tlen = 2 + 2;
if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
return;
- len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
struct hostapd_roaming_consortium *rc;
+
rc = &hapd->conf->roaming_consortium[i];
- wpabuf_put_u8(buf, rc->len);
- wpabuf_put_data(buf, rc->oi, rc->len);
+ if (tlen + 1 + rc->len > 2 + 2 + 65535) {
+ wpa_printf(MSG_INFO,
+ "ANQP: Too many roaming consortium OIs configured to fit into an ANQP-element");
+ break;
+ }
+ tlen += 1 + rc->len;
+ count++;
}
- gas_anqp_set_element_len(buf, len);
+
+ if (wpabuf_resize(buf, tlen) < 0)
+ return;
+
+ len = gas_anqp_add_element(*buf, ANQP_ROAMING_CONSORTIUM);
+ for (i = 0; i < count; i++) {
+ struct hostapd_roaming_consortium *rc;
+
+ rc = &hapd->conf->roaming_consortium[i];
+ wpabuf_put_u8(*buf, rc->len);
+ wpabuf_put_data(*buf, rc->oi, rc->len);
+ }
+ gas_anqp_set_element_len(*buf, len);
}
static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
return;
if (hapd->conf->ipaddr_type_configured) {
- wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
- wpabuf_put_le16(buf, 1);
- wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+ if (wpabuf_resize(buf, 2 + 2 + 1) < 0)
+ return;
+ wpabuf_put_le16(*buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ wpabuf_put_le16(*buf, 1);
+ wpabuf_put_u8(*buf, hapd->conf->ipaddr_type_availability);
+ }
+}
+
+
+static size_t anqp_len_nai_realm_eap(struct hostapd_nai_realm_data *realm)
+{
+ unsigned int i;
+ size_t len = 1;
+
+ for (i = 0; i < realm->eap_method_count; i++) {
+ struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+
+ len += 1 + 1 + 1 + 3 * eap->num_auths;
}
+
+ return len;
}
@@ -443,7 +505,7 @@ static void anqp_add_nai_realm_data(struct wpabuf *buf,
static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
- struct wpabuf *buf,
+ struct wpabuf **buf,
const u8 *home_realm,
size_t home_realm_len)
{
@@ -455,6 +517,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
unsigned int realm_data_idx;
unsigned int realm_idx;
} matches[10];
+ size_t tlen = 2 + 2 + 2;
pos = home_realm;
end = pos + home_realm_len;
@@ -499,12 +562,17 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
for (k = 0; k < MAX_NAI_REALMS &&
realm->realm[k] &&
num_matching < 10; k++) {
- if ((int) os_strlen(realm->realm[k]) !=
- rend - rpos ||
+ size_t rlen;
+
+ rlen = os_strlen(realm->realm[k]);
+ if ((int) rlen != rend - rpos ||
os_strncmp((char *) rpos,
realm->realm[k],
rend - rpos) != 0)
continue;
+ tlen += 2 + 1 + 1 + rlen +
+ anqp_len_nai_realm_eap(realm);
+
matches[num_matching].realm_data_idx =
j;
matches[num_matching].realm_idx = k;
@@ -516,8 +584,11 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
pos += realm_len;
}
- realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
- wpabuf_put_le16(buf, num_matching);
+ if (wpabuf_resize(buf, tlen) < 0)
+ return -1;
+
+ realm_list_len = gas_anqp_add_element(*buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(*buf, num_matching);
/*
* There are two ways to format. 1. each realm in a NAI Realm Data unit
@@ -529,14 +600,14 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
matches[i].realm_data_idx, matches[i].realm_idx);
realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
- anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+ anqp_add_nai_realm_data(*buf, realm, matches[i].realm_idx);
}
- gas_anqp_set_element_len(buf, realm_list_len);
+ gas_anqp_set_element_len(*buf, realm_list_len);
return 0;
}
-static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf **buf,
const u8 *home_realm, size_t home_realm_len,
int nai_realm, int nai_home_realm)
{
@@ -546,27 +617,52 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
if (nai_realm && hapd->conf->nai_realm_data) {
u8 *len;
- unsigned int i, j;
- len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
- wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+ unsigned int i, j, count = 0;
+ size_t tlen = 2 + 2 + 2;
+
for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+ struct hostapd_nai_realm_data *realm;
+ size_t prev_tlen = tlen;
+
+ realm = &hapd->conf->nai_realm_data[i];
+ tlen += 2 + 1 + 1;
+ for (j = 0; realm->realm[j]; j++) {
+ if (j > 0)
+ tlen++;
+ tlen += os_strlen(realm->realm[j]);
+ }
+ tlen += anqp_len_nai_realm_eap(realm);
+ if (tlen > 65535) {
+ tlen = prev_tlen;
+ wpa_printf(MSG_INFO,
+ "ANQP: Too many NAI realms configured to fit into an ANQP-element");
+ break;
+ }
+ count++;
+ }
+ if (wpabuf_resize(buf, tlen) < 0)
+ return;
+
+ len = gas_anqp_add_element(*buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(*buf, count);
+ for (i = 0; i < count; i++) {
u8 *realm_data_len, *realm_len;
struct hostapd_nai_realm_data *realm;
realm = &hapd->conf->nai_realm_data[i];
- realm_data_len = wpabuf_put(buf, 2);
- wpabuf_put_u8(buf, realm->encoding);
- realm_len = wpabuf_put(buf, 1);
+ realm_data_len = wpabuf_put(*buf, 2);
+ wpabuf_put_u8(*buf, realm->encoding);
+ realm_len = wpabuf_put(*buf, 1);
for (j = 0; realm->realm[j]; j++) {
if (j > 0)
- wpabuf_put_u8(buf, ';');
- wpabuf_put_str(buf, realm->realm[j]);
+ wpabuf_put_u8(*buf, ';');
+ wpabuf_put_str(*buf, realm->realm[j]);
}
- *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
- anqp_add_nai_realm_eap(buf, realm);
- gas_anqp_set_element_len(buf, realm_data_len);
+ *realm_len = (u8 *) wpabuf_put(*buf, 0) - realm_len - 1;
+ anqp_add_nai_realm_eap(*buf, realm);
+ gas_anqp_set_element_len(*buf, realm_data_len);
}
- gas_anqp_set_element_len(buf, len);
+ gas_anqp_set_element_len(*buf, len);
} else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
home_realm_len);
@@ -575,30 +671,35 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
return;
if (hapd->conf->anqp_3gpp_cell_net) {
- wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
- wpabuf_put_le16(buf,
+ if (wpabuf_resize(buf, 2 + 2 +
+ hapd->conf->anqp_3gpp_cell_net_len) < 0)
+ return;
+ wpabuf_put_le16(*buf, ANQP_3GPP_CELLULAR_NETWORK);
+ wpabuf_put_le16(*buf,
hapd->conf->anqp_3gpp_cell_net_len);
- wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+ wpabuf_put_data(*buf, hapd->conf->anqp_3gpp_cell_net,
hapd->conf->anqp_3gpp_cell_net_len);
}
}
-static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf **buf)
{
if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
return;
if (hapd->conf->domain_name) {
- wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
- wpabuf_put_le16(buf, hapd->conf->domain_name_len);
- wpabuf_put_data(buf, hapd->conf->domain_name,
+ if (wpabuf_resize(buf, 2 + 2 + hapd->conf->domain_name_len) < 0)
+ return;
+ wpabuf_put_le16(*buf, ANQP_DOMAIN_NAME);
+ wpabuf_put_le16(*buf, hapd->conf->domain_name_len);
+ wpabuf_put_data(*buf, hapd->conf->domain_name,
hapd->conf->domain_name_len);
}
}
@@ -606,7 +707,7 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
#ifdef CONFIG_FILS
static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
size_t count;
@@ -619,14 +720,16 @@ static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
if (count) {
struct fils_realm *realm;
- wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
- wpabuf_put_le16(buf, 2 * count);
+ if (wpabuf_resize(buf, 2 + 2 + 2 * count) < 0)
+ return;
+ wpabuf_put_le16(*buf, ANQP_FILS_REALM_INFO);
+ wpabuf_put_le16(*buf, 2 * count);
dl_list_for_each(realm, &hapd->conf->fils_realms,
struct fils_realm, list) {
if (count == 0)
break;
- wpabuf_put_data(buf, realm->hash, 2);
+ wpabuf_put_data(*buf, realm->hash, 2);
count--;
}
}
@@ -637,11 +740,26 @@ static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **_buf)
{
if (hapd->conf->hs20_oper_friendly_name) {
u8 *len;
unsigned int i;
+ size_t tlen = 2 + 2 + 4 + 1 + 1;
+ struct wpabuf *buf;
+
+ for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+ {
+ struct hostapd_lang_string *vn;
+
+ vn = &hapd->conf->hs20_oper_friendly_name[i];
+ tlen += 1 + 3 + vn->name_len;
+ }
+
+ if (wpabuf_resize(_buf, tlen) < 0)
+ return;
+ buf = *_buf;
+
len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
@@ -661,275 +779,64 @@ static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
static void anqp_add_wan_metrics(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (hapd->conf->hs20_wan_metrics) {
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
- wpabuf_put_u8(buf, 0); /* Reserved */
- wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
- gas_anqp_set_element_len(buf, len);
+ u8 *len;
+
+ if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 + 13) < 0)
+ return;
+ len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(*buf, OUI_WFA);
+ wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(*buf, HS20_STYPE_WAN_METRICS);
+ wpabuf_put_u8(*buf, 0); /* Reserved */
+ wpabuf_put_data(*buf, hapd->conf->hs20_wan_metrics, 13);
+ gas_anqp_set_element_len(*buf, len);
}
}
static void anqp_add_connection_capability(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (hapd->conf->hs20_connection_capability) {
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
- wpabuf_put_u8(buf, 0); /* Reserved */
- wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+ u8 *len;
+
+ if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 +
+ hapd->conf->hs20_connection_capability_len) <
+ 0)
+ return;
+ len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(*buf, OUI_WFA);
+ wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(*buf, HS20_STYPE_CONNECTION_CAPABILITY);
+ wpabuf_put_u8(*buf, 0); /* Reserved */
+ wpabuf_put_data(*buf, hapd->conf->hs20_connection_capability,
hapd->conf->hs20_connection_capability_len);
- gas_anqp_set_element_len(buf, len);
+ gas_anqp_set_element_len(*buf, len);
}
}
static void anqp_add_operating_class(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (hapd->conf->hs20_operating_class) {
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
- wpabuf_put_u8(buf, 0); /* Reserved */
- wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
- hapd->conf->hs20_operating_class_len);
- gas_anqp_set_element_len(buf, len);
- }
-}
-
-
-static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
- const char *name)
-{
- size_t j;
- struct hs20_icon *icon = NULL;
-
- for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
- if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
- icon = &bss->hs20_icons[j];
- }
- if (!icon)
- return; /* icon info not found */
-
- wpabuf_put_le16(buf, icon->width);
- wpabuf_put_le16(buf, icon->height);
- wpabuf_put_data(buf, icon->language, 3);
- wpabuf_put_u8(buf, os_strlen(icon->type));
- wpabuf_put_str(buf, icon->type);
- wpabuf_put_u8(buf, os_strlen(icon->name));
- wpabuf_put_str(buf, icon->name);
-}
-
-
-static void anqp_add_osu_provider(struct wpabuf *buf,
- struct hostapd_bss_config *bss,
- struct hs20_osu_provider *p)
-{
- u8 *len, *len2, *count;
- unsigned int i;
-
- len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
-
- /* OSU Friendly Name Duples */
- len2 = wpabuf_put(buf, 2);
- for (i = 0; i < p->friendly_name_count; i++) {
- struct hostapd_lang_string *s = &p->friendly_name[i];
- wpabuf_put_u8(buf, 3 + s->name_len);
- wpabuf_put_data(buf, s->lang, 3);
- wpabuf_put_data(buf, s->name, s->name_len);
- }
- WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
- /* OSU Server URI */
- if (p->server_uri) {
- wpabuf_put_u8(buf, os_strlen(p->server_uri));
- wpabuf_put_str(buf, p->server_uri);
- } else
- wpabuf_put_u8(buf, 0);
-
- /* OSU Method List */
- count = wpabuf_put(buf, 1);
- for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
- wpabuf_put_u8(buf, p->method_list[i]);
- *count = i;
-
- /* Icons Available */
- len2 = wpabuf_put(buf, 2);
- for (i = 0; i < p->icons_count; i++)
- anqp_add_icon(buf, bss, p->icons[i]);
- WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
- /* OSU_NAI */
- if (p->osu_nai) {
- wpabuf_put_u8(buf, os_strlen(p->osu_nai));
- wpabuf_put_str(buf, p->osu_nai);
- } else
- wpabuf_put_u8(buf, 0);
-
- /* OSU Service Description Duples */
- len2 = wpabuf_put(buf, 2);
- for (i = 0; i < p->service_desc_count; i++) {
- struct hostapd_lang_string *s = &p->service_desc[i];
- wpabuf_put_u8(buf, 3 + s->name_len);
- wpabuf_put_data(buf, s->lang, 3);
- wpabuf_put_data(buf, s->name, s->name_len);
- }
- WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
- WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
-}
-
-
-static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
- struct wpabuf *buf)
-{
- if (hapd->conf->hs20_osu_providers_count) {
- size_t i;
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
- wpabuf_put_u8(buf, 0); /* Reserved */
-
- /* OSU SSID */
- wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
- wpabuf_put_data(buf, hapd->conf->osu_ssid,
- hapd->conf->osu_ssid_len);
-
- /* Number of OSU Providers */
- wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
-
- for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
- anqp_add_osu_provider(
- buf, hapd->conf,
- &hapd->conf->hs20_osu_providers[i]);
- }
-
- gas_anqp_set_element_len(buf, len);
- }
-}
-
-
-static void anqp_add_osu_provider_nai(struct wpabuf *buf,
- struct hs20_osu_provider *p)
-{
- /* OSU_NAI for shared BSS (Single SSID) */
- if (p->osu_nai2) {
- wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
- wpabuf_put_str(buf, p->osu_nai2);
- } else {
- wpabuf_put_u8(buf, 0);
- }
-}
-
-
-static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
- struct wpabuf *buf)
-{
- if (hapd->conf->hs20_osu_providers_nai_count) {
- size_t i;
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
- wpabuf_put_u8(buf, 0); /* Reserved */
-
- for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
- anqp_add_osu_provider_nai(
- buf, &hapd->conf->hs20_osu_providers[i]);
- }
-
- gas_anqp_set_element_len(buf, len);
- }
-}
-
-
-static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
- struct wpabuf *buf,
- const u8 *name, size_t name_len)
-{
- struct hs20_icon *icon;
- size_t i;
- u8 *len;
-
- wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
- name, name_len);
- for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
- icon = &hapd->conf->hs20_icons[i];
- if (name_len == os_strlen(icon->name) &&
- os_memcmp(name, icon->name, name_len) == 0)
- break;
- }
-
- if (i < hapd->conf->hs20_icons_count)
- icon = &hapd->conf->hs20_icons[i];
- else
- icon = NULL;
-
- len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
- wpabuf_put_u8(buf, 0); /* Reserved */
-
- if (icon) {
- char *data;
- size_t data_len;
+ u8 *len;
- data = os_readfile(icon->file, &data_len);
- if (data == NULL || data_len > 65535) {
- wpabuf_put_u8(buf, 2); /* Download Status:
- * Unspecified file error */
- wpabuf_put_u8(buf, 0);
- wpabuf_put_le16(buf, 0);
- } else {
- wpabuf_put_u8(buf, 0); /* Download Status: Success */
- wpabuf_put_u8(buf, os_strlen(icon->type));
- wpabuf_put_str(buf, icon->type);
- wpabuf_put_le16(buf, data_len);
- wpabuf_put_data(buf, data, data_len);
- }
- os_free(data);
- } else {
- wpabuf_put_u8(buf, 1); /* Download Status: File not found */
- wpabuf_put_u8(buf, 0);
- wpabuf_put_le16(buf, 0);
+ if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 +
+ hapd->conf->hs20_operating_class_len) < 0)
+ return;
+ len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(*buf, OUI_WFA);
+ wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(*buf, HS20_STYPE_OPERATING_CLASS);
+ wpabuf_put_u8(*buf, 0); /* Reserved */
+ wpabuf_put_data(*buf, hapd->conf->hs20_operating_class,
+ hapd->conf->hs20_operating_class_len);
+ gas_anqp_set_element_len(*buf, len);
}
-
- gas_anqp_set_element_len(buf, len);
-}
-
-
-static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
- struct wpabuf *buf)
-{
- struct hostapd_bss_config *bss = hapd->conf;
- size_t i;
- u8 *len;
-
- if (!bss->hs20_operator_icon_count)
- return;
-
- len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
- wpabuf_put_u8(buf, 0); /* Reserved */
-
- for (i = 0; i < bss->hs20_operator_icon_count; i++)
- anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
-
- gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -937,15 +844,19 @@ static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
#ifdef CONFIG_MBO
static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
- struct wpabuf *buf)
+ struct wpabuf **buf)
{
if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
- u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
- wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
- wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
- gas_anqp_set_element_len(buf, len);
+ u8 *len;
+
+ if (wpabuf_resize(buf, 2 + 4 + 1 + 1) < 0)
+ return;
+ len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(*buf, OUI_WFA);
+ wpabuf_put_u8(*buf, MBO_ANQP_OUI_TYPE);
+ wpabuf_put_u8(*buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+ wpabuf_put_u8(*buf, hapd->conf->mbo_cell_data_conn_pref);
+ gas_anqp_set_element_len(*buf, len);
}
}
#endif /* CONFIG_MBO */
@@ -973,19 +884,17 @@ static struct wpabuf *
gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
unsigned int request,
const u8 *home_realm, size_t home_realm_len,
- const u8 *icon_name, size_t icon_name_len,
const u16 *extra_req,
unsigned int num_extra_req)
{
struct wpabuf *buf;
size_t len;
unsigned int i;
+ size_t prev_len, max_len = 128 * hapd->conf->gas_frag_limit;
len = 1400;
if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
len += 1000;
- if (request & ANQP_REQ_ICON_REQUEST)
- len += 65536;
#ifdef CONFIG_FILS
if (request & ANQP_FILS_REALM_INFO)
len += 2 * dl_list_len(&hapd->conf->fils_realms);
@@ -997,76 +906,238 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
return NULL;
if (request & ANQP_REQ_CAPABILITY_LIST)
- anqp_add_capab_list(hapd, buf);
- if (request & ANQP_REQ_VENUE_NAME)
- anqp_add_venue_name(hapd, buf);
- if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
- anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
- if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
- anqp_add_network_auth_type(hapd, buf);
- if (request & ANQP_REQ_ROAMING_CONSORTIUM)
- anqp_add_roaming_consortium(hapd, buf);
- if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
- anqp_add_ip_addr_type_availability(hapd, buf);
- if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
- anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+ anqp_add_capab_list(hapd, &buf);
+
+ if (request & ANQP_REQ_VENUE_NAME) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_venue_name(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_CALL_NUMBER);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+ if (request & ANQP_REQ_NETWORK_AUTH_TYPE) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_network_auth_type(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+ if (request & ANQP_REQ_ROAMING_CONSORTIUM) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_roaming_consortium(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+ if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_ip_addr_type_availability(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_nai_realm(hapd, &buf, home_realm, home_realm_len,
request & ANQP_REQ_NAI_REALM,
request & ANQP_REQ_NAI_HOME_REALM);
- if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
- anqp_add_3gpp_cellular_network(hapd, buf);
- if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
- anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
- if (request & ANQP_REQ_AP_CIVIC_LOCATION)
- anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
- if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
- anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
- if (request & ANQP_REQ_DOMAIN_NAME)
- anqp_add_domain_name(hapd, buf);
- if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
- anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
- if (request & ANQP_REQ_TDLS_CAPABILITY)
- anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
- if (request & ANQP_REQ_EMERGENCY_NAI)
- anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_3gpp_cellular_network(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_AP_GEOSPATIAL_LOCATION);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_AP_CIVIC_LOCATION) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_AP_CIVIC_LOCATION);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_AP_LOCATION_PUBLIC_URI);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_DOMAIN_NAME) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_domain_name(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_EMERGENCY_ALERT_URI) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_ALERT_URI);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_TDLS_CAPABILITY) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_TDLS_CAPABILITY);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_EMERGENCY_NAI) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_NAI);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
for (i = 0; i < num_extra_req; i++) {
+ prev_len = wpabuf_len(buf);
#ifdef CONFIG_FILS
if (extra_req[i] == ANQP_FILS_REALM_INFO) {
- anqp_add_fils_realm_info(hapd, buf);
+ anqp_add_fils_realm_info(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
continue;
}
#endif /* CONFIG_FILS */
if (extra_req[i] == ANQP_VENUE_URL) {
- anqp_add_venue_url(hapd, buf);
+ anqp_add_venue_url(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
continue;
}
- anqp_add_elem(hapd, buf, extra_req[i]);
+ anqp_add_elem(hapd, &buf, extra_req[i]);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
}
#ifdef CONFIG_HS20
- if (request & ANQP_REQ_HS_CAPABILITY_LIST)
- anqp_add_hs_capab_list(hapd, buf);
- if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
- anqp_add_operator_friendly_name(hapd, buf);
- if (request & ANQP_REQ_WAN_METRICS)
- anqp_add_wan_metrics(hapd, buf);
- if (request & ANQP_REQ_CONNECTION_CAPABILITY)
- anqp_add_connection_capability(hapd, buf);
- if (request & ANQP_REQ_OPERATING_CLASS)
- anqp_add_operating_class(hapd, buf);
- if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
- anqp_add_osu_providers_list(hapd, buf);
- if (request & ANQP_REQ_ICON_REQUEST)
- anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
- if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
- anqp_add_operator_icon_metadata(hapd, buf);
- if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
- anqp_add_osu_providers_nai_list(hapd, buf);
+ if (request & ANQP_REQ_HS_CAPABILITY_LIST) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_hs_capab_list(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_operator_friendly_name(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_WAN_METRICS) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_wan_metrics(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_CONNECTION_CAPABILITY) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_connection_capability(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
+
+ if (request & ANQP_REQ_OPERATING_CLASS) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_operating_class(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
- if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
- anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+ if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) {
+ prev_len = wpabuf_len(buf);
+ anqp_add_mbo_cell_data_conn_pref(hapd, &buf);
+ if (wpabuf_len(buf) > max_len) {
+ wpa_printf(MSG_INFO,
+ "ANQP: ANQP-element would go beyond maximum GAS response length - skip");
+ buf->used = prev_len;
+ }
+ }
#endif /* CONFIG_MBO */
return buf;
@@ -1079,8 +1150,6 @@ struct anqp_query_info {
unsigned int request;
const u8 *home_realm_query;
size_t home_realm_query_len;
- const u8 *icon_name;
- size_t icon_name_len;
int p2p_sd;
u16 extra_req[ANQP_MAX_EXTRA_REQ];
unsigned int num_extra_req;
@@ -1245,20 +1314,6 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
hapd->conf->hs20_operating_class != NULL, qi);
break;
- case HS20_STYPE_OSU_PROVIDERS_LIST:
- set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
- hapd->conf->hs20_osu_providers_count, qi);
- break;
- case HS20_STYPE_OPERATOR_ICON_METADATA:
- set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
- "Operator Icon Metadata",
- hapd->conf->hs20_operator_icon_count, qi);
- break;
- case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
- set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
- "OSU Providers NAI List",
- hapd->conf->hs20_osu_providers_nai_count, qi);
- break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);
@@ -1284,23 +1339,6 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
}
-static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
- const u8 *pos, const u8 *end,
- struct anqp_query_info *qi)
-{
- qi->request |= ANQP_REQ_ICON_REQUEST;
- qi->icon_name = pos;
- qi->icon_name_len = end - pos;
- if (hapd->conf->hs20_icons_count) {
- wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
- "(local)");
- } else {
- wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
- "available");
- }
-}
-
-
static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
const u8 *pos, const u8 *end,
struct anqp_query_info *qi)
@@ -1323,9 +1361,6 @@ static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
case HS20_STYPE_NAI_HOME_REALM_QUERY:
rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
break;
- case HS20_STYPE_ICON_REQUEST:
- rx_anqp_hs_icon_request(hapd, pos, end, qi);
- break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
"%u", subtype);
@@ -1455,7 +1490,6 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
qi->home_realm_query,
qi->home_realm_query_len,
- qi->icon_name, qi->icon_name_len,
qi->extra_req, qi->num_extra_req);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf);
@@ -1474,14 +1508,28 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
hapd->conf->gas_comeback_delay) {
struct gas_dialog_info *di;
u16 comeback_delay = 1;
+ unsigned int num_frag;
if (hapd->conf->gas_comeback_delay) {
/* Testing - allow overriding of the delay value */
comeback_delay = hapd->conf->gas_comeback_delay;
}
- wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
- "initial response - use GAS comeback");
+ num_frag = wpabuf_len(buf) / hapd->conf->gas_frag_limit;
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Too long response (%zu bytes) to fit in initial response (frag_limit %zu bytes) - use GAS comeback (%u fragments)",
+ wpabuf_len(buf), hapd->conf->gas_frag_limit,
+ num_frag);
+ if (num_frag > 128) {
+ wpa_printf(MSG_INFO,
+ "ANQP: Too long response (%zu bytes) to fit into GAS response",
+ wpabuf_len(buf));
+ wpabuf_free(buf);
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, NULL);
+ goto done;
+ }
di = gas_dialog_create(hapd, sa, dialog_token);
if (!di) {
wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
@@ -1505,6 +1553,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
wpabuf_free(buf);
}
+done:
if (!tx_buf)
return;
if (prot)
diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c
index 6ecf6ca7834e..c327ee88c5ae 100644
--- a/contrib/wpa/src/ap/hostapd.c
+++ b/contrib/wpa/src/ap/hostapd.c
@@ -66,12 +66,9 @@ static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
#endif /* CONFIG_WEP */
static int setup_interface2(struct hostapd_iface *iface);
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces);
static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
void *timeout_ctx);
-#ifdef CONFIG_IEEE80211AX
-static void hostapd_switch_color_timeout_handler(void *eloop_data,
- void *user_ctx);
-#endif /* CONFIG_IEEE80211AX */
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -162,7 +159,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
else
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
- if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
+ if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
if (hapd->wpa_auth)
wpa_init_keys(hapd->wpa_auth);
@@ -186,6 +183,14 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
hostapd_neighbor_sync_own_report(hapd);
+ if (hapd->iface->current_mode &&
+ hostapd_prepare_rates(hapd, hapd->iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ }
+
ieee802_11_set_beacon(hapd);
hostapd_update_wps(hapd);
@@ -242,8 +247,43 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
if (os_strcmp(newconf->bss[i]->iface,
oldconf->bss[i]->iface) != 0)
return 1;
+#ifdef CONFIG_IEEE80211BE
+ if (newconf->bss[i]->mld_ap != oldconf->bss[i]->mld_ap)
+ return 1;
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ return 0;
+}
+
+
+static int hostapd_remove_hapd_iface(struct hostapd_iface *hapd_iface)
+{
+ struct hapd_interfaces *interfaces;
+ size_t i;
+
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+ interfaces = hapd_iface->interfaces;
+ for (i = 0; i < interfaces->count; i++) {
+ if (interfaces->iface[i] == hapd_iface)
+ break;
+ }
+
+ if (i >= interfaces->count)
+ return -1;
+
+ hostapd_interface_deinit_free(hapd_iface);
+
+ while (i < interfaces->count - 1) {
+ interfaces->iface[i] = interfaces->iface[i + 1];
+ i++;
}
+ interfaces->count--;
+ hostapd_cleanup_unused_mlds(interfaces);
return 0;
}
@@ -284,7 +324,12 @@ int hostapd_reload_config(struct hostapd_iface *iface)
hostapd_config_free(newconf);
return -1;
}
- hostapd_remove_iface(interfaces, hapd->conf->iface);
+ if (hostapd_remove_hapd_iface(iface) != 0) {
+ os_free(fname);
+ hostapd_config_free(newconf);
+ return -1;
+ }
+
iface = hostapd_init(interfaces, fname);
os_free(fname);
hostapd_config_free(newconf);
@@ -302,7 +347,6 @@ int hostapd_reload_config(struct hostapd_iface *iface)
"Failed to enable interface on config reload");
return res;
}
- iface->conf = newconf;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
@@ -330,6 +374,7 @@ int hostapd_reload_config(struct hostapd_iface *iface)
hostapd_reload_bss(hapd);
}
+ iface->conf = newconf;
hostapd_config_free(oldconf);
@@ -354,7 +399,7 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
ifname, i);
}
}
- if (hapd->conf->ieee80211w) {
+ if (ap_pmf_enabled(hapd->conf)) {
for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
NULL, i, 0, 0, NULL,
@@ -400,8 +445,6 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
#ifdef CONFIG_IEEE80211BE
#ifdef CONFIG_TESTING_OPTIONS
-#define TU_TO_USEC(_val) ((_val) * 1024)
-
static void hostapd_link_remove_timeout_handler(void *eloop_data,
void *user_ctx)
{
@@ -440,6 +483,8 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
hapd->eht_mld_link_removal_count = count;
hapd->eht_mld_bss_param_change++;
+ if (hapd->eht_mld_bss_param_change == 255)
+ hapd->eht_mld_bss_param_change = 0;
eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
hostapd_link_remove_timeout_handler,
@@ -459,6 +504,11 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
hapd->probereq_cb = NULL;
hapd->num_probereq_cb = 0;
+ os_free(hapd->current_rates);
+ hapd->current_rates = NULL;
+ os_free(hapd->basic_rates);
+ hapd->basic_rates = NULL;
+
#ifdef CONFIG_P2P
wpabuf_free(hapd->p2p_beacon_ie);
hapd->p2p_beacon_ie = NULL;
@@ -521,7 +571,10 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
authsrv_deinit(hapd);
- if (hapd->interface_added) {
+ /* For single drv, first bss would have interface_added flag set.
+ * Don't remove interface now. Driver deinit part will take care
+ */
+ if (hapd->interface_added && hapd->iface->bss[0] != hapd) {
hapd->interface_added = 0;
if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
wpa_printf(MSG_WARNING,
@@ -551,9 +604,9 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
wpabuf_free(hapd->time_adv);
hapd->time_adv = NULL;
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
gas_serv_deinit(hapd);
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
bss_load_update_deinit(hapd);
ndisc_snoop_deinit(hapd);
@@ -610,6 +663,21 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
}
+#ifdef CONFIG_IEEE80211BE
+/* hostapd_mld_move_vlan_list - Move the VLAN list to the new first bss
+ *
+ * This function is used to copy the reference to the VLAN list from the old
+ * first BSS to the new first BSS when the first BSS is removed.
+ */
+static void hostapd_mld_move_vlan_list(struct hostapd_data *old_fbss,
+ struct hostapd_data *new_fbss)
+{
+ new_fbss->conf->vlan = old_fbss->conf->vlan;
+ old_fbss->conf->vlan = NULL;
+
+}
+#endif /* CONFIG_IEEE80211BE */
+
/* hostapd_bss_link_deinit - Per-BSS ML cleanup (deinitialization)
* @hapd: Pointer to BSS data
*
@@ -620,10 +688,20 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
static void hostapd_bss_link_deinit(struct hostapd_data *hapd)
{
#ifdef CONFIG_IEEE80211BE
+ int i;
+
if (!hapd->conf || !hapd->conf->mld_ap)
return;
- if (!hapd->mld->num_links)
+ /* Free per STA profiles */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(hapd->partner_links[i].resp_sta_profile);
+ os_memset(&hapd->partner_links[i], 0,
+ sizeof(hapd->partner_links[i]));
+ }
+
+ /* Put all freeing logic above this */
+ if (!hapd->mld || !hapd->mld->num_links)
return;
/* If not started, not yet linked to the MLD. However, the first
@@ -693,15 +771,16 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
acs_cleanup(iface);
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = NULL;
+ iface->num_hw_features = 0;
iface->current_mode = NULL;
- os_free(iface->current_rates);
- iface->current_rates = NULL;
- os_free(iface->basic_rates);
- iface->basic_rates = NULL;
iface->cac_started = 0;
ap_list_deinit(iface);
sta_track_deinit(iface);
airtime_policy_update_deinit(iface);
+ hostapd_free_multi_hw_info(iface->multi_hw_info);
+ iface->multi_hw_info = NULL;
+ iface->num_multi_hws = 0;
+ iface->current_hw_info = NULL;
}
@@ -715,6 +794,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);
@@ -724,6 +804,12 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
os_free(iface->config_fname);
os_free(iface->bss);
+#ifdef CONFIG_AFC
+ wpabuf_free(iface->afc_request);
+ os_free(iface->afc_response);
+ os_free(iface->afc.chan_info_list);
+ os_free(iface->afc.freq_range);
+#endif /* CONFIG_AFC */
wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
os_free(iface);
}
@@ -751,12 +837,6 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
return 0;
}
- /*
- * When IEEE 802.1X is not enabled, the driver may need to know how to
- * set authentication algorithms for static WEP.
- */
- hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
-
for (i = 0; i < 4; i++) {
if (hapd->conf->ssid.wep.key[i] &&
hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, 0,
@@ -1363,7 +1443,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
- * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ * @first: Whether this BSS is the first BSS of an interface; false = not first,
* but interface may exist
* @start_beacon: Whether Beacon frame template should be configured and
* transmission of Beaconf rames started at this time. This is used when
@@ -1377,7 +1457,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd)
* initialized. Most of the modules that are initialized here will be
* deinitialized in hostapd_cleanup().
*/
-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+static int hostapd_setup_bss(struct hostapd_data *hapd, bool first,
bool start_beacon)
{
struct hostapd_bss_config *conf = hapd->conf;
@@ -1394,6 +1474,16 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
__func__, hapd, conf->iface, first);
+ /* Prepare per-BSS rates early from BSS config and current mode */
+ if (hapd->iface->current_mode &&
+ hostapd_prepare_rates(hapd, hapd->iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ return -1;
+ }
+
#ifdef EAP_SERVER_TNC
if (conf->tnc && tncs_global_init() < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
@@ -1408,7 +1498,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
}
hapd->started = 1;
- if (!first || first == -1) {
+ if (!first) {
u8 *addr = hapd->own_addr;
if (!is_zero_ether_addr(conf->bssid)) {
@@ -1440,7 +1530,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
if (h_hapd) {
hapd->drv_priv = h_hapd->drv_priv;
hapd->interface_added = h_hapd->interface_added;
- hostapd_mld_add_link(hapd);
wpa_printf(MSG_DEBUG,
"Setup of non first link (%d) BSS of MLD %s",
hapd->mld_link_id, hapd->conf->iface);
@@ -1454,7 +1543,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
conf->iface, addr, hapd,
&hapd->drv_priv, force_ifname, if_addr,
conf->bridge[0] ? conf->bridge : NULL,
- first == -1)) {
+ 1)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
hapd->interface_added = 0;
@@ -1471,7 +1560,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
hapd->mld_link_id, hapd->conf->iface);
os_memcpy(hapd->mld->mld_addr, hapd->own_addr,
ETH_ALEN);
- hostapd_mld_add_link(hapd);
}
#endif /* CONFIG_IEEE80211BE */
}
@@ -1486,8 +1574,13 @@ setup_mld:
MAC2STR(hapd->own_addr));
if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
- hapd->own_addr))
+ hapd->own_addr)) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to add link %d in MLD %s",
+ hapd->mld_link_id, hapd->conf->iface);
return -1;
+ }
+ hostapd_mld_add_link(hapd);
}
#endif /* CONFIG_IEEE80211BE */
@@ -1546,7 +1639,7 @@ setup_mld:
/*
* Short SSID calculation is identical to FCS and it is defined in
- * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
+ * IEEE Std 802.11-2024, 9.4.2.169.3 (Calculating the Short-SSID).
*/
conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
conf->ssid.ssid_len);
@@ -1646,8 +1739,11 @@ setup_mld:
wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
return -1;
}
+#ifdef CONFIG_IEEE8021X_AUTH
+ hapd->send_eap_req = ieee80211_send_eap_req;
+#endif /* CONFIG_IEEE8021X_AUTH */
- if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+ if (conf->wpa && hostapd_setup_wpa(hapd))
return -1;
if (accounting_init(hapd)) {
@@ -1655,12 +1751,12 @@ setup_mld:
return -1;
}
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
if (gas_serv_init(hapd)) {
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
if (conf->qos_map_set_len &&
hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
@@ -1778,26 +1874,34 @@ static int hostapd_set_acl_list(struct hostapd_data *hapd,
int hostapd_set_acl(struct hostapd_data *hapd)
{
- struct hostapd_config *conf = hapd->iconf;
+ struct hostapd_bss_config *conf = hapd->conf;
int err = 0;
u8 accept_acl;
if (hapd->iface->drv_max_acl_mac_addrs == 0)
return 0;
- if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "Kernel doesn't support offloaded ACL for AP MLD. Use hostapd based ACL instead.");
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (conf->macaddr_acl == DENY_UNLESS_ACCEPTED) {
accept_acl = 1;
- err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
- conf->bss[0]->num_accept_mac,
+ err = hostapd_set_acl_list(hapd, conf->accept_mac,
+ conf->num_accept_mac,
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set accept acl");
return -1;
}
- } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+ } else if (conf->macaddr_acl == ACCEPT_UNLESS_DENIED) {
accept_acl = 0;
- err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
- conf->bss[0]->num_deny_mac,
+ err = hostapd_set_acl_list(hapd, conf->deny_mac,
+ conf->num_deny_mac,
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set deny acl");
@@ -1808,12 +1912,36 @@ int hostapd_set_acl(struct hostapd_data *hapd)
}
+static int hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ int ret;
+
+ if (hapd->conf->mld_ap) {
+ ret = os_snprintf(hapd->ctrl_sock_iface,
+ sizeof(hapd->ctrl_sock_iface), "%s_%s%d",
+ hapd->conf->iface, WPA_CTRL_IFACE_LINK_NAME,
+ hapd->mld_link_id);
+ if (os_snprintf_error(sizeof(hapd->ctrl_sock_iface), ret))
+ return -1;
+ } else {
+ os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface,
+ sizeof(hapd->ctrl_sock_iface));
+ }
+#endif /* CONFIG_IEEE80211BE */
+ return 0;
+}
+
+
static int start_ctrl_iface_bss(struct hostapd_data *hapd)
{
if (!hapd->iface->interfaces ||
!hapd->iface->interfaces->ctrl_iface_init)
return 0;
+ if (hostapd_set_ctrl_sock_iface(hapd))
+ return -1;
+
if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
wpa_printf(MSG_ERROR,
"Failed to setup control interface for %s",
@@ -1834,6 +1962,10 @@ static int start_ctrl_iface(struct hostapd_iface *iface)
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
+
+ if (hostapd_set_ctrl_sock_iface(hapd))
+ return -1;
+
if (iface->interfaces->ctrl_iface_init(hapd)) {
wpa_printf(MSG_ERROR,
"Failed to setup control interface for %s",
@@ -1862,18 +1994,30 @@ static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
void *ctx)
{
+ struct hostapd_data *hapd = iface->bss[0];
bool all_no_ir, is_6ghz;
int i, j;
struct hostapd_hw_modes *mode = NULL;
+ struct hostapd_hw_modes *hw_features;
+ u16 num_hw_features, flags;
+ u8 dfs_domain;
- if (hostapd_get_hw_features(iface))
- return 0;
+ if (hostapd_drv_none(hapd))
+ return -1;
+
+ hw_features = hostapd_get_hw_feature_data(hapd, &num_hw_features,
+ &flags, &dfs_domain);
+ if (!hw_features) {
+ wpa_printf(MSG_DEBUG,
+ "Could not fetching hardware channel list");
+ return -1;
+ }
all_no_ir = true;
is_6ghz = false;
- for (i = 0; i < iface->num_hw_features; i++) {
- mode = &iface->hw_features[i];
+ for (i = 0; i < num_hw_features; i++) {
+ mode = &hw_features[i];
if (mode->mode == iface->conf->hw_mode) {
if (iface->freq > 0 &&
@@ -1895,26 +2039,25 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
}
if (!mode || !is_6ghz)
- return 0;
- iface->current_mode = mode;
+ goto free_hw_features;
if (iface->state == HAPD_IFACE_ENABLED) {
if (!all_no_ir) {
struct hostapd_channel_data *chan;
- chan = hw_get_channel_freq(iface->current_mode->mode,
+ chan = hw_get_channel_freq(mode->mode,
iface->freq, NULL,
- iface->hw_features,
- iface->num_hw_features);
+ hw_features,
+ num_hw_features);
if (!chan) {
wpa_printf(MSG_ERROR,
"NO_IR: Could not derive chan from freq");
- return 0;
+ goto free_hw_features;
}
if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
- return 0;
+ goto free_hw_features;
wpa_printf(MSG_DEBUG,
"NO_IR: The current channel has NO_IR flag now, stop AP.");
} else {
@@ -1931,20 +2074,20 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
if (all_no_ir) {
wpa_printf(MSG_DEBUG,
"NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
- return 0;
+ goto free_hw_features;
}
if (!iface->conf->acs) {
struct hostapd_channel_data *chan;
- chan = hw_get_channel_freq(iface->current_mode->mode,
+ chan = hw_get_channel_freq(mode->mode,
iface->freq, NULL,
- iface->hw_features,
- iface->num_hw_features);
+ hw_features,
+ num_hw_features);
if (!chan) {
wpa_printf(MSG_ERROR,
"NO_IR: Could not derive chan from freq");
- return 0;
+ goto free_hw_features;
}
/* If the last operating channel is NO_IR, trigger ACS.
@@ -1955,13 +2098,15 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
if (acs_init(iface) != HOSTAPD_CHAN_ACS)
wpa_printf(MSG_ERROR,
"NO_IR: Could not start ACS");
- return 0;
+ goto free_hw_features;
}
}
setup_interface2(iface);
}
+free_hw_features:
+ hostapd_free_hw_features(hw_features, num_hw_features);
return 0;
}
@@ -2507,9 +2652,19 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
hostapd_hw_mode_txt(iface->conf->hw_mode),
iface->conf->channel, iface->freq);
+ if (hostapd_set_current_hw_info(iface, iface->freq)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to set current hardware info");
+ goto fail;
+ }
+
#ifdef NEED_AP_MLME
- /* Handle DFS only if it is not offloaded to the driver */
- if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+ if (iface->assisted_dfs) {
+ wpa_printf(MSG_DEBUG,
+ "Request to start AP with assisted DFS");
+ } else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+ /* Handle DFS only if it is not offloaded to the driver
+ */
/* Check DFS */
res = hostapd_handle_dfs(iface);
if (res <= 0) {
@@ -2546,6 +2701,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
}
#endif /* CONFIG_MESH */
+#ifdef CONFIG_IEEE80211AX
+ /* Check AFC for 6 GHz 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,
@@ -2567,17 +2732,6 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
}
}
- if (iface->current_mode) {
- if (hostapd_prepare_rates(iface, iface->current_mode)) {
- wpa_printf(MSG_ERROR, "Failed to prepare rates "
- "table.");
- hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Failed to prepare rates table.");
- goto fail;
- }
- }
-
if (hapd->iconf->rts_threshold >= -1 &&
hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
hapd->iconf->rts_threshold >= -1) {
@@ -2604,6 +2758,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
+ hostapd_bss_link_deinit(hapd);
hostapd_free_hapd_data(hapd);
if (j == 0)
break;
@@ -2622,6 +2777,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
+ hostapd_bss_link_deinit(hapd);
hostapd_free_hapd_data(hapd);
if (j == 0)
break;
@@ -2640,17 +2796,11 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
hostapd_set_acl(hapd);
- if (hostapd_driver_commit(hapd) < 0) {
- wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
- "configuration", __func__);
- goto fail;
- }
-
/*
* WPS UPnP module can be initialized only when the "upnp_iface" is up.
* If "interface" and "upnp_iface" are the same (e.g., non-bridge
- * mode), the interface is up only after driver_commit, so initialize
- * WPS after driver_commit.
+ * mode), the interface is up only after the driver interface has been
+ * initialized, so initialize WPS after that.
*/
for (j = 0; j < iface->num_bss; j++) {
if (hostapd_init_wps_complete(iface->bss[j]))
@@ -2658,7 +2808,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
}
if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
- !res_dfs_offload) {
+ !res_dfs_offload && !iface->assisted_dfs) {
/*
* If freq is DFS, and DFS is offloaded to the driver, then wait
* for CAC to complete.
@@ -2686,6 +2836,11 @@ dfs_offload:
}
#endif /* CONFIG_FST */
+#ifdef CONFIG_AFC
+ if (iface->afc.data_valid)
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
+#endif /* CONFIG_AFC */
+
hostapd_set_state(iface, HAPD_IFACE_ENABLED);
hostapd_owe_update_trans(iface);
airtime_policy_update_init(iface);
@@ -2709,7 +2864,7 @@ dfs_offload:
hostapd_neighbor_set_own_report(iface->bss[j]);
if (iface->interfaces && iface->interfaces->count > 1)
- ieee802_11_set_beacons(iface);
+ ieee802_11_update_beacons(iface);
return 0;
@@ -2909,6 +3064,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
#ifdef CONFIG_SAE
dl_list_init(&hapd->sae_commit_queue);
#endif /* CONFIG_SAE */
+ dl_list_init(&hapd->erp_keys);
return hapd;
}
@@ -2944,6 +3100,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;
@@ -3045,18 +3202,20 @@ static void hostapd_bss_alloc_link_id(struct hostapd_data *hapd)
#endif /* CONFIG_IEEE80211BE */
-static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
- struct hapd_interfaces *interfaces)
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ struct hapd_interfaces *interfaces)
{
#ifdef CONFIG_IEEE80211BE
struct hostapd_mld *mld, **all_mld;
struct hostapd_bss_config *conf;
size_t i;
+ if (hapd->mld)
+ return;
+
conf = hapd->conf;
- if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
- conf->disable_11be)
+ if (!hapd->iconf || !conf->mld_ap || !hostapd_is_eht_enabled(hapd))
return;
for (i = 0; i < interfaces->mld_count; i++) {
@@ -3080,9 +3239,17 @@ 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;
+ if (hapd->conf->ctrl_interface)
+ 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);
@@ -3101,6 +3268,7 @@ fail:
if (!mld)
return;
+ interfaces->mld_ctrl_iface_deinit(mld);
wpa_printf(MSG_DEBUG, "AP MLD %s: free mld %p", mld->name, mld);
os_free(mld);
hapd->mld = NULL;
@@ -3142,6 +3310,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);
@@ -3285,6 +3455,7 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
{
struct hostapd_iface *new_iface = NULL, *iface = NULL;
struct hostapd_data *hapd;
+ struct hostapd_config *conf;
int k;
size_t i, bss_idx;
@@ -3300,17 +3471,26 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
config_fname, phy, iface ? "" : " --> new PHY");
+
+ conf = interfaces->config_read_cb(config_fname);
+ if (!conf)
+ return NULL;
+
+#ifdef CONFIG_IEEE80211BE
+ /* AP MLD can be enabled with the same interface name, so even if we
+ * get the interface, we still need to allocate a new hostapd_iface
+ * structure. */
+ if (conf->bss[0]->mld_ap)
+ iface = NULL;
+#endif /* CONFIG_IEEE80211BE */
+
if (iface) {
- struct hostapd_config *conf;
struct hostapd_bss_config **tmp_conf;
struct hostapd_data **tmp_bss;
struct hostapd_bss_config *bss;
const char *ifname;
/* Add new BSS to existing iface */
- conf = interfaces->config_read_cb(config_fname);
- if (conf == NULL)
- return NULL;
if (conf->num_bss > 1) {
wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
hostapd_config_free(conf);
@@ -3360,6 +3540,8 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
conf->bss[0] = NULL;
hostapd_config_free(conf);
} else {
+ hostapd_config_free(conf);
+
/* Add a new iface with the first BSS */
new_iface = iface = hostapd_init(interfaces, config_fname);
if (!iface)
@@ -3394,20 +3576,24 @@ static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
return;
#ifdef CONFIG_IEEE80211BE
- /* In case of non-ML operation, de-init. But if ML operation exist,
- * even if that's the last BSS in the interface, the driver (drv) could
- * be in use for a different AP MLD. Hence, need to check if drv is
- * still being used by some other BSS before de-initiallizing. */
- if (!iface->bss[0]->conf->mld_ap) {
+ if (!driver->is_drv_shared ||
+ !driver->is_drv_shared(drv_priv, iface->bss[0]->mld_link_id)) {
driver->hapd_deinit(drv_priv);
- } else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
- driver->is_drv_shared &&
- !driver->is_drv_shared(drv_priv, iface->bss[0])) {
- driver->hapd_deinit(drv_priv);
- } else if (hostapd_if_link_remove(iface->bss[0],
- WPA_IF_AP_BSS,
- iface->bss[0]->conf->iface,
- iface->bss[0]->mld_link_id)) {
+ hostapd_mld_interface_freed(iface->bss[0]);
+ iface->bss[0]->drv_priv = NULL;
+ return;
+ }
+
+ if (iface->bss[0]->conf->mld_ap) {
+ if (hostapd_if_link_remove(iface->bss[0],
+ WPA_IF_AP_BSS,
+ iface->bss[0]->conf->iface,
+ iface->bss[0]->mld_link_id))
+ wpa_printf(MSG_WARNING,
+ "Failed to remove link BSS interface %s",
+ iface->bss[0]->conf->iface);
+ } else if (hostapd_if_remove(iface->bss[0], WPA_IF_AP_BSS,
+ iface->bss[0]->conf->iface)) {
wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
iface->bss[0]->conf->iface);
}
@@ -3466,7 +3652,7 @@ static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
}
-static void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface)
+void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface)
{
size_t j;
@@ -3510,8 +3696,12 @@ int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
if (hapd_iface->interfaces == NULL ||
hapd_iface->interfaces->driver_init == NULL ||
- hapd_iface->interfaces->driver_init(hapd_iface))
+ hapd_iface->interfaces->driver_init(hapd_iface)) {
+ hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
return -1;
+ }
if (hostapd_setup_interface(hapd_iface)) {
hostapd_deinit_driver(hapd_iface->bss[0]->driver,
@@ -3564,8 +3754,6 @@ int hostapd_reload_bss_only(struct hostapd_data *bss)
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
- const struct wpa_driver_ops *driver;
- void *drv_priv;
if (hapd_iface == NULL)
return -1;
@@ -3580,8 +3768,6 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
}
wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
- driver = hapd_iface->bss[0]->driver;
- drv_priv = hapd_iface->bss[0]->drv_priv;
hapd_iface->driver_ap_teardown =
!!(hapd_iface->drv_flags &
@@ -3600,7 +3786,8 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
hostapd_free_hapd_data(hapd);
}
- hostapd_deinit_driver(driver, drv_priv, hapd_iface);
+ hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv, hapd_iface);
/* From hostapd_cleanup_iface: These were initialized in
* hostapd_setup_interface and hostapd_setup_interface_complete
@@ -3772,10 +3959,17 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
}
if (new_iface) {
- if (interfaces->driver_init(hapd_iface))
+ if (interfaces->driver_init(hapd_iface)) {
+ hostapd_deinit_driver(
+ hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
goto fail;
+ }
if (hostapd_setup_interface(hapd_iface)) {
+ /* Deinit link for the first bss */
+ hostapd_bss_link_deinit(hapd_iface->bss[0]);
hostapd_deinit_driver(
hapd_iface->bss[0]->driver,
hapd_iface->bss[0]->drv_priv,
@@ -3792,7 +3986,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
- hostapd_setup_bss(hapd, -1, true))) {
+ hostapd_setup_bss(hapd, false, true))) {
hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
@@ -3956,7 +4150,7 @@ static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_iface *hapd_iface;
- size_t i, j, k = 0;
+ size_t i, j;
for (i = 0; i < interfaces->count; i++) {
hapd_iface = interfaces->iface[i];
@@ -3964,21 +4158,7 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
return -1;
if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
- hapd_iface->driver_ap_teardown =
- !!(hapd_iface->drv_flags &
- WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
-
- hostapd_interface_deinit_free(hapd_iface);
- k = i;
- while (k < (interfaces->count - 1)) {
- interfaces->iface[k] =
- interfaces->iface[k + 1];
- k++;
- }
- interfaces->count--;
- hostapd_cleanup_unused_mlds(interfaces);
-
- return 0;
+ return hostapd_remove_hapd_iface(hapd_iface);
}
for (j = 0; j < hapd_iface->conf->num_bss; j++) {
@@ -4021,6 +4201,23 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211BE */
ap_sta_clear_disconnect_timeouts(hapd, sta);
+ ap_sta_clear_assoc_timeout(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ struct hostapd_data *bss;
+ struct sta_info *lsta;
+
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd)
+ continue;
+ lsta = ap_get_sta(bss, sta->addr);
+ if (lsta)
+ ap_sta_clear_assoc_timeout(bss, lsta);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
sta->post_csa_sa_query = 0;
#ifdef CONFIG_P2P
@@ -4037,14 +4234,57 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
/* Start accounting here, if IEEE 802.1X and WPA are not used.
* IEEE 802.1X/WPA code will start accounting after the station has
* been authorized. */
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
- ap_sta_set_authorized(hapd, sta, 1);
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
+ if (ap_sta_set_authorized(hapd, sta, 1)) {
+ /* Update driver authorized flag for the STA to cover
+ * the case where AP SME is in the driver and there is
+ * no separate event for handling TX status event for
+ * the (Re)Association Response frame. */
+ hostapd_set_sta_flags(hapd, sta);
+ }
os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
/* Start IEEE 802.1X authentication process for new stations */
ieee802_1x_new_station(hapd, sta);
+
+#ifdef CONFIG_ENC_ASSOC
+ if (ap_sta_is_epp(sta) && sta->wpa_sm) {
+ const struct wpa_ptk *ptk;
+ const u8 *pmk;
+ size_t pmk_len;
+
+ switch (sta->auth_alg) {
+ case WLAN_AUTH_EPPKE:
+ if (!sta->pasn) {
+ wpa_printf(MSG_INFO,
+ "EPP: Missing PASN data");
+ return;
+ }
+ ptk = &sta->pasn->ptk;
+ pmk = sta->pasn->pmk;
+ pmk_len = sta->pasn->pmk_len;
+ break;
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X:
+ ptk = &sta->eap_auth_data.ptk;
+ pmk = sta->eap_auth_data.pmk;
+ pmk_len = sta->eap_auth_data.pmk_len;
+ break;
+#endif /* CONFIG_IEEE8021X_AUTH */
+ default:
+ wpa_printf(MSG_INFO,
+ "EPP: Unsupported auth alg=%d for an EPP station",
+ sta->auth_alg);
+ return;
+ }
+ wpa_store_eppke_pmk_ptk_sm(sta->wpa_sm, ptk, pmk,
+ pmk_len);
+ wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
if (reassoc) {
if (sta->auth_alg != WLAN_AUTH_FT &&
sta->auth_alg != WLAN_AUTH_FILS_SK &&
@@ -4150,14 +4390,26 @@ void free_beacon_data(struct beacon_data *beacon)
beacon->proberesp_ies = NULL;
os_free(beacon->assocresp_ies);
beacon->assocresp_ies = NULL;
+ os_free(beacon->mbssid.mbssid_elem);
+ beacon->mbssid.mbssid_elem = NULL;
+ os_free(beacon->mbssid.mbssid_elem_offset);
+ beacon->mbssid.mbssid_elem_offset = NULL;
+ os_free(beacon->mbssid.rnr_elem);
+ beacon->mbssid.rnr_elem = NULL;
+ os_free(beacon->mbssid.rnr_elem_offset);
+ beacon->mbssid.rnr_elem_offset = NULL;
}
-static int hostapd_build_beacon_data(struct hostapd_data *hapd,
- struct beacon_data *beacon)
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+ struct beacon_data *beacon)
{
struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
struct wpa_driver_ap_params params;
+ struct hostapd_data *tx_bss;
+ u8 *mbssid_start_eid, *rnr_start_eid;
+ size_t size = 0;
+ int i;
int ret;
os_memset(beacon, 0, sizeof(*beacon));
@@ -4221,6 +4473,76 @@ static int hostapd_build_beacon_data(struct hostapd_data *hapd,
beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
}
+ /* MBSSID element */
+ if (!params.mbssid.mbssid_elem_len)
+ goto done;
+
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ beacon->mbssid.mbssid_tx_iface = tx_bss->conf->iface;
+ beacon->mbssid.mbssid_tx_iface_linkid =
+ params.mbssid.mbssid_tx_iface_linkid;
+ beacon->mbssid.mbssid_index = params.mbssid.mbssid_index;
+
+ beacon->mbssid.mbssid_elem_len = params.mbssid.mbssid_elem_len;
+ beacon->mbssid.mbssid_elem_count = params.mbssid.mbssid_elem_count;
+ if (params.mbssid.mbssid_elem) {
+ beacon->mbssid.mbssid_elem =
+ os_memdup(params.mbssid.mbssid_elem,
+ params.mbssid.mbssid_elem_len);
+ if (!beacon->mbssid.mbssid_elem)
+ goto free_beacon;
+ }
+ beacon->mbssid.ema = params.mbssid.ema;
+
+ if (params.mbssid.mbssid_elem_offset) {
+ beacon->mbssid.mbssid_elem_offset =
+ os_calloc(beacon->mbssid.mbssid_elem_count,
+ sizeof(u8 *));
+ if (!beacon->mbssid.mbssid_elem_offset)
+ goto free_beacon;
+
+ mbssid_start_eid = beacon->mbssid.mbssid_elem;
+ beacon->mbssid.mbssid_elem_offset[0] = mbssid_start_eid;
+ for (i = 0; i < beacon->mbssid.mbssid_elem_count - 1; i++) {
+ size = params.mbssid.mbssid_elem_offset[i + 1] -
+ params.mbssid.mbssid_elem_offset[i];
+ mbssid_start_eid = mbssid_start_eid + size;
+ beacon->mbssid.mbssid_elem_offset[i + 1] =
+ mbssid_start_eid;
+ }
+ }
+
+ /* RNR element */
+ if (!params.mbssid.rnr_elem_len)
+ goto done;
+
+ if (params.mbssid.rnr_elem) {
+ beacon->mbssid.rnr_elem = os_memdup(params.mbssid.rnr_elem,
+ params.mbssid.rnr_elem_len);
+ if (!beacon->mbssid.rnr_elem)
+ goto free_beacon;
+ }
+
+ beacon->mbssid.rnr_elem_len = params.mbssid.rnr_elem_len;
+ beacon->mbssid.rnr_elem_count = params.mbssid.rnr_elem_count;
+ if (params.mbssid.rnr_elem_offset) {
+ beacon->mbssid.rnr_elem_offset =
+ os_calloc(beacon->mbssid.rnr_elem_count + 1,
+ sizeof(u8 *));
+ if (!beacon->mbssid.rnr_elem_offset)
+ goto free_beacon;
+
+ rnr_start_eid = beacon->mbssid.rnr_elem;
+ beacon->mbssid.rnr_elem_offset[0] = rnr_start_eid;
+ for (i = 0; i < beacon->mbssid.rnr_elem_count - 1; i++) {
+ size = params.mbssid.rnr_elem_offset[i + 1] -
+ params.mbssid.rnr_elem_offset[i];
+ rnr_start_eid = rnr_start_eid + size;
+ beacon->mbssid.rnr_elem_offset[i + 1] = rnr_start_eid;
+ }
+ }
+
+done:
ret = 0;
free_beacon:
/* if the function fails, the caller should not free beacon data */
@@ -4241,13 +4563,12 @@ free_ap_params:
* the same hw_mode. Any other changes to MAC parameters or provided settings
* are not supported.
*/
-static int hostapd_change_config_freq(struct hostapd_data *hapd,
- struct hostapd_config *conf,
- struct hostapd_freq_params *params,
- struct hostapd_freq_params *old_params)
+int hostapd_change_config_freq(struct hostapd_data *hapd,
+ struct hostapd_config *conf,
+ struct hostapd_freq_params *params,
+ struct hostapd_freq_params *old_params)
{
- int channel;
- u8 seg0 = 0, seg1 = 0;
+ u8 channel, op_class, seg0 = 0, seg1 = 0;
struct hostapd_hw_modes *mode;
if (!params->channel) {
@@ -4332,52 +4653,120 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
ieee80211_freq_to_chan(params->center_freq2,
&seg1) == NUM_HOSTAPD_MODES)
return -1;
+ if (ieee80211_freq_to_channel_ext(params->freq,
+ conf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "%s:Update op_class %d->%d and channel=%d based on freq=%d",
+ __func__, conf->op_class, op_class, channel, params->freq);
+ conf->op_class = op_class;
hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
+#ifdef CONFIG_IEEE80211BE
+ conf->punct_bitmap = params->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
/* TODO: maybe call here hostapd_config_check here? */
return 0;
}
+enum oper_chan_width
+hostapd_chan_width_from_freq_params(struct hostapd_freq_params *freq_params)
+{
+ switch (freq_params->bandwidth) {
+ case 80:
+ if (freq_params->center_freq2)
+ return CONF_OPER_CHWIDTH_80P80MHZ;
+ else
+ return CONF_OPER_CHWIDTH_80MHZ;
+ case 160:
+ return CONF_OPER_CHWIDTH_160MHZ;
+ case 320:
+ return CONF_OPER_CHWIDTH_320MHZ;
+ default:
+ return CONF_OPER_CHWIDTH_USE_HT;
+ }
+}
+
+
static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
struct csa_settings *settings)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_freq_params old_freq;
int ret;
-#ifdef CONFIG_IEEE80211BE
- u16 old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
- u8 chan, bandwidth;
+ enum oper_chan_width chanwidth;
+ u8 chan;
+ int sec_channel_offset;
os_memset(&old_freq, 0, sizeof(old_freq));
if (!iface || !iface->freq || hapd->csa_in_progress)
return -1;
- switch (settings->freq_params.bandwidth) {
- case 80:
- if (settings->freq_params.center_freq2)
- bandwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+ chanwidth = hostapd_chan_width_from_freq_params(&settings->freq_params);
+ sec_channel_offset = settings->freq_params.sec_channel_offset;
+#ifdef CONFIG_IEEE80211BE
+ /* When Switching to an 11BE channel, adjust the legacy BW and center
+ * frequencies accordingly
+ */
+ if (chanwidth == CONF_OPER_CHWIDTH_320MHZ ||
+ settings->freq_params.punct_bitmap) {
+ enum oper_chan_width chan_op_bw = chanwidth;
+ u8 oper_centr_freq0_idx = 0, oper_centr_freq1_idx = 0, pri_chan = 0;
+
+ ieee80211_freq_to_chan(settings->freq_params.center_freq1,
+ &oper_centr_freq0_idx);
+ ieee80211_freq_to_chan(settings->freq_params.center_freq2,
+ &oper_centr_freq1_idx);
+ ieee80211_freq_to_chan(settings->freq_params.freq,
+ &pri_chan);
+
+ punct_update_legacy_bw(settings->freq_params.punct_bitmap,
+ pri_chan,
+ &chan_op_bw,
+ &oper_centr_freq0_idx,
+ &oper_centr_freq1_idx);
+
+ /* Downgrade the EHT 320 MHz BW to legacy 160 MHz BW and
+ * calculate the correspoding 160 MHz center freq for later
+ * ECSA opclass calculation which needs legacy BW and
+ * secondary channel offset.
+ */
+ if (chan_op_bw == CONF_OPER_CHWIDTH_320MHZ) {
+ chan_op_bw = CONF_OPER_CHWIDTH_160MHZ;
+
+ if (pri_chan < oper_centr_freq0_idx)
+ oper_centr_freq0_idx -= 16;
+ else
+ oper_centr_freq0_idx += 16;
+ }
+
+ chanwidth = chan_op_bw;
+ if (oper_centr_freq0_idx == 0 || oper_centr_freq0_idx == pri_chan)
+ sec_channel_offset = 0;
+ else if (oper_centr_freq0_idx > pri_chan)
+ sec_channel_offset = 1;
else
- bandwidth = CONF_OPER_CHWIDTH_80MHZ;
- break;
- case 160:
- bandwidth = CONF_OPER_CHWIDTH_160MHZ;
- break;
- case 320:
- bandwidth = CONF_OPER_CHWIDTH_320MHZ;
- break;
- default:
- bandwidth = CONF_OPER_CHWIDTH_USE_HT;
- break;
+ sec_channel_offset = -1;
+
+ wpa_printf(MSG_DEBUG,
+ "ECSA Legacy BW %d chan1 %d chan2 %d sec_chan %d",
+ chan_op_bw, oper_centr_freq0_idx, oper_centr_freq1_idx,
+ sec_channel_offset);
}
+#endif /* CONFIG_IEEE80211BE */
if (ieee80211_freq_to_channel_ext(
settings->freq_params.freq,
- settings->freq_params.sec_channel_offset,
- bandwidth,
+ sec_channel_offset,
+ chanwidth,
&hapd->iface->cs_oper_class,
&chan) == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_DEBUG,
@@ -4398,18 +4787,13 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
if (ret)
return ret;
-#ifdef CONFIG_IEEE80211BE
- old_punct_bitmap = iface->conf->punct_bitmap;
- iface->conf->punct_bitmap = settings->punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
/* change back the configuration */
-#ifdef CONFIG_IEEE80211BE
- iface->conf->punct_bitmap = old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
- hostapd_change_config_freq(iface->bss[0], iface->conf,
- &old_freq, NULL);
+ if (hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &old_freq, NULL) < 0)
+ wpa_printf(MSG_INFO,
+ "Failed to switch back to old frequency after preparing beacon data for CSA");
if (ret)
return ret;
@@ -4516,6 +4900,42 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
}
+int hostapd_force_channel_switch(struct hostapd_iface *iface,
+ struct csa_settings *settings)
+{
+ int ret = 0;
+
+ if (!settings->freq_params.channel) {
+ /* Check if the new channel is supported */
+ settings->freq_params.channel = hostapd_hw_get_channel(
+ iface->bss[0], settings->freq_params.freq);
+ if (!settings->freq_params.channel)
+ return -1;
+ }
+
+ ret = hostapd_disable_iface(iface);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to disable the interface");
+ return ret;
+ }
+
+ hostapd_chan_switch_config(iface->bss[0], &settings->freq_params);
+ ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &settings->freq_params, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to set the new channel in config");
+ return ret;
+ }
+
+ ret = hostapd_enable_iface(iface);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "Failed to enable the interface");
+
+ return ret;
+}
+
+
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params)
@@ -4606,9 +5026,10 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd,
{
struct hostapd_iface *iface = hapd->iface;
u8 old_color;
+ u8 old_disabled;
int ret;
- if (!iface || iface->conf->he_op.he_bss_color_disabled)
+ if (!iface)
return -1;
settings->link_id = -1;
@@ -4618,12 +5039,16 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211BE */
old_color = iface->conf->he_op.he_bss_color;
+ old_disabled = iface->conf->he_op.he_bss_color_disabled;
+
iface->conf->he_op.he_bss_color = hapd->cca_color;
+ iface->conf->he_op.he_bss_color_disabled = 0;
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
- if (ret)
- return ret;
iface->conf->he_op.he_bss_color = old_color;
+ iface->conf->he_op.he_bss_color_disabled = old_disabled;
+ if (ret)
+ return ret;
settings->cca_count = hapd->cca_count;
settings->cca_color = hapd->cca_color,
@@ -4645,8 +5070,7 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd,
}
-static void hostapd_switch_color_timeout_handler(void *eloop_data,
- void *user_ctx)
+void hostapd_switch_color_timeout_handler(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
os_time_t delta_t;
@@ -4661,6 +5085,14 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data,
if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD)
return;
+ /* IEEE Std 802.11-2024, 26.17.3.5.1: once the BSS color collision has
+ * persisted for dot11BSSColorCollisionAPPeriod the AP shall set BSS
+ * Color Disabled to 1. Set it now so that beacon_cca (built inside
+ * hostapd_fill_cca_settings()) and any incidental beacon rebuild
+ * during the CCA countdown carry the correct Disabled=1 state.
+ */
+ hapd->iface->conf->he_op.he_bss_color_disabled = 1;
+
r = os_random() % HE_OPERATION_BSS_COLOR_MAX;
for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
if (r && !(hapd->color_collision_bitmap & (1ULL << r)))
@@ -4685,6 +5117,7 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data,
struct cca_settings settings;
int ret;
+ os_memset(&settings, 0, sizeof(settings));
hostapd_cleanup_cca_params(bss);
bss->cca_color = r;
bss->cca_count = 10;
@@ -4705,11 +5138,24 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data,
}
+bool hostapd_is_cca_in_progress(struct hostapd_iface *iface)
+{
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->cca_in_progress)
+ return true;
+ }
+
+ return false;
+}
+
+
void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
{
struct os_reltime now;
- if (hapd->cca_in_progress)
+ if (hostapd_is_cca_in_progress(hapd->iface))
return;
if (os_get_reltime(&now))
@@ -4843,10 +5289,7 @@ u8 hostapd_get_mld_id(struct hostapd_data *hapd)
if (!hapd->conf->mld_ap)
return 255;
- /* MLD ID 0 represents self */
- return 0;
-
- /* TODO: MLD ID for Multiple BSS cases */
+ return hostapd_mbssid_get_bss_index(hapd);
}
@@ -4902,8 +5345,16 @@ int hostapd_mld_remove_link(struct hostapd_data *hapd)
if (dl_list_empty(&mld->links)) {
mld->fbss = NULL;
} else {
+ struct hostapd_data *last_link_bss;
+
next_fbss = dl_list_entry(mld->links.next, struct hostapd_data,
link);
+ last_link_bss = dl_list_last(&mld->links, struct hostapd_data,
+ link);
+
+ if (next_fbss != last_link_bss)
+ hostapd_mld_move_vlan_list(hapd, next_fbss);
+
mld->fbss = next_fbss;
}
@@ -4947,6 +5398,40 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd)
return mld->fbss;
}
+
+void hostapd_mld_interface_freed(struct hostapd_data *hapd)
+{
+ struct hostapd_data *link_bss = NULL;
+
+ if (!hapd || !hapd->conf->mld_ap)
+ return;
+
+ for_each_mld_link(link_bss, hapd)
+ link_bss->drv_priv = NULL;
+}
+
+
+/* Return the number of currently active links, not counting the calling link
+ * (i.e., a value that is suitable to be used as-is in fields that use encoding
+ * of the value minus 1). */
+u8 hostapd_get_active_links(struct hostapd_data *hapd)
+{
+ struct hostapd_data *link_bss;
+ u8 active_links = 0;
+
+ if (!hapd || !hapd->conf->mld_ap)
+ return 0;
+
+ for_each_mld_link(link_bss, hapd) {
+ if (link_bss == hapd || !link_bss->started)
+ continue;
+
+ active_links++;
+ }
+
+ return active_links;
+}
+
#endif /* CONFIG_IEEE80211BE */
@@ -4964,3 +5449,172 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
return punct_bitmap;
}
+
+
+struct hostapd_data *
+hostapd_get_mbssid_bss_by_idx(struct hostapd_data *hapd, size_t idx)
+{
+ if (idx < hapd->iface->num_bss)
+ return hapd->iface->bss[idx];
+
+ return NULL;
+}
+
+
+static bool hostapd_acceptable_sta_addr_bss(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *addr_ml,
+ bool mld)
+{
+ if (ether_addr_equal(addr, hapd->own_addr))
+ return false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->mld && mld &&
+ (ether_addr_equal(addr_ml, hapd->own_addr) ||
+ ether_addr_equal(addr_ml, hapd->mld->mld_addr)))
+ return false;
+ if (hapd->mld && ether_addr_equal(addr, hapd->mld->mld_addr))
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+
+ return true;
+}
+
+
+struct hostapd_acceptable_sta_add_ctx {
+ const u8 *addr;
+ const u8 *addr_ml;
+ bool mld;
+};
+
+
+static int hostapd_acceptable_sta_addr_iface(struct hostapd_iface *iface,
+ void *_ctx)
+{
+ struct hostapd_acceptable_sta_add_ctx *ctx = _ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (!hostapd_acceptable_sta_addr_bss(hapd, ctx->addr,
+ ctx->addr_ml, ctx->mld))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+bool hostapd_acceptable_sta_addr(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *addr_ml, bool mld)
+{
+ struct hostapd_acceptable_sta_add_ctx ctx;
+
+ if (!hostapd_acceptable_sta_addr_bss(hapd, addr, addr_ml, mld))
+ return false;
+
+ ctx.addr = addr;
+ ctx.addr_ml = addr_ml;
+ ctx.mld = mld;
+ if (hapd->iface->interfaces &&
+ hostapd_for_each_interface(hapd->iface->interfaces,
+ hostapd_acceptable_sta_addr_iface,
+ &ctx) < 0)
+ return false;
+
+ return true;
+}
+
+
+void hostapd_get_oper_chan_info_of_bss(struct hostapd_data *hapd,
+ enum oper_chan_width *width,
+ u8 *seg0, u8 *seg1)
+{
+ *width = hostapd_get_oper_chwidth(hapd->iconf);
+ *seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
+ *seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
+
+#ifdef CONFIG_IEEE80211BE
+ /* Re-derive the legacy channel width for EHT disabled BSS on an EHT
+ * capable interface for punctured channel and 320 MHz bandwidth cases.
+ */
+ if (hapd->iconf->ieee80211be && !hostapd_is_eht_enabled(hapd)) {
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+
+ if (punct_bitmap)
+ punct_update_legacy_bw(punct_bitmap,
+ hapd->iconf->channel,
+ width, seg0, seg1);
+
+ if (*width == CONF_OPER_CHWIDTH_320MHZ)
+ *width = CONF_OPER_CHWIDTH_160MHZ;
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+enum oper_chan_width
+hostapd_get_oper_chan_width_of_bss(struct hostapd_data *hapd)
+{
+ enum oper_chan_width width;
+ u8 seg0, seg1;
+
+ hostapd_get_oper_chan_info_of_bss(hapd, &width, &seg0, &seg1);
+
+ return width;
+}
+
+
+u8 hostapd_get_oper_class_of_bss(struct hostapd_data *hapd)
+{
+ u8 op_class = hapd->iconf->op_class;
+
+#ifdef CONFIG_IEEE80211BE
+ /* For EHT disabled BSS, re-derive the legacy operating class from
+ * operating frequency parameters as the bandwidth that it advertises
+ * in VHT/HE Operation element can be lower than EHT enabled BSS of the
+ * interface.
+ *
+ * If EHT is not disabled on the BSS, interface's operating class is
+ * returned without recomputation against operating frequency parameters
+ * as operating channel information is expected to be aligned with the
+ * interface's operating class configuration.
+ */
+ if (hapd->iconf->ieee80211be && !hostapd_is_eht_enabled(hapd) &&
+ hapd->iface->freq) {
+ enum oper_chan_width bss_chwidth, iface_chwidth;
+ u8 seg0, seg1;
+ u8 chan;
+
+ iface_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ hostapd_get_oper_chan_info_of_bss(hapd, &bss_chwidth,
+ &seg0, &seg1);
+
+ /* If the BSS channel width is different from the interface
+ * channel width, re-compute the operating class as per the BSS
+ * channel width.
+ */
+ if (bss_chwidth != iface_chwidth) {
+ int secondary_channel = hapd->iconf->secondary_channel;
+
+ /* Check if legacy bandwidth is downgraded to 20 MHz
+ * and derive operating class accordingly.
+ */
+ if (seg0 == hapd->iconf->channel &&
+ bss_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ secondary_channel = 0;
+
+ if (ieee80211_freq_to_channel_ext(
+ hapd->iface->freq,
+ secondary_channel,
+ bss_chwidth, &op_class,
+ &chan) == NUM_HOSTAPD_MODES ||
+ chan != hapd->iconf->channel)
+ return hapd->iconf->op_class;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return op_class;
+}
diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h
index dcf395ca5ee8..8d4f3462a31a 100644
--- a/contrib/wpa/src/ap/hostapd.h
+++ b/contrib/wpa/src/ap/hostapd.h
@@ -35,6 +35,7 @@ struct ieee80211_ht_capabilities;
struct full_dynamic_vlan;
enum wps_event;
union wps_event_data;
+struct rsn_pmksa_cache_entry;
#ifdef CONFIG_MESH
struct mesh_conf;
#endif /* CONFIG_MESH */
@@ -97,7 +98,13 @@ 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 */
+
+#ifdef CONFIG_PROCESS_COORDINATION
+ struct proc_coord *pc;
+#endif /* CONFIG_PROCESS_COORDINATION */
};
enum hostapd_chan_status {
@@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue {
u8 msg[];
};
+struct mld_link_info {
+ u8 valid:1;
+ u8 nstr_bitmap_len:2;
+ u8 local_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+
+ u8 nstr_bitmap[2];
+
+ u16 capability;
+
+ u16 status;
+ u16 resp_sta_profile_len;
+ u8 *resp_sta_profile;
+};
+
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
@@ -204,6 +226,10 @@ struct hostapd_data {
void *msg_ctx; /* ctx for wpa_msg() calls */
void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+ int num_rates;
+ struct hostapd_rate_data *current_rates;
+ int *basic_rates;
+
struct radius_client_data *radius;
u64 acct_session_id;
struct radius_das_data *radius_das;
@@ -304,6 +330,13 @@ struct hostapd_data {
size_t psk_len);
void *new_psk_cb_ctx;
+#ifdef CONFIG_IEEE8021X_AUTH
+ void (*send_eap_req)(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 type, u16 auth_transaction, u16 status,
+ struct rsn_pmksa_cache_entry *cached_pmk,
+ const u8 *eap_req, size_t eap_req_len);
+#endif /* CONFIG_IEEE8021X_AUTH */
+
/* channel switch parameters */
struct hostapd_freq_params cs_freq_params;
u8 cs_count;
@@ -476,6 +509,14 @@ struct hostapd_data {
struct hostapd_mld *mld;
struct dl_list link;
u8 mld_link_id;
+
+ /* Cached partner info for ML probe response */
+ struct mld_link_info partner_links[MAX_NUM_MLD_LINKS];
+
+ /* 5 characters for "_link", up to 2 characters for <link ID>, so in
+ * total, additional 7 characters required. */
+ char ctrl_sock_iface[IFNAMSIZ + 7 + 1];
+
#ifdef CONFIG_TESTING_OPTIONS
u8 eht_mld_link_removal_count;
#endif /* CONFIG_TESTING_OPTIONS */
@@ -483,6 +524,8 @@ struct hostapd_data {
#ifdef CONFIG_NAN_USD
struct nan_de *nan_de;
+ /* Whether nan_de should be freed on deinit */
+ bool nan_de_is_owned;
#endif /* CONFIG_NAN_USD */
u64 scan_cookie; /* Scan instance identifier for the ongoing HT40 scan
@@ -518,6 +561,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
@@ -598,11 +645,6 @@ struct hostapd_iface {
struct hostapd_hw_modes *hw_features;
int num_hw_features;
struct hostapd_hw_modes *current_mode;
- /* Rates that are currently used (i.e., filtered copy of
- * current_mode->channels */
- int num_rates;
- struct hostapd_rate_data *current_rates;
- int *basic_rates;
int freq;
bool radar_detected;
@@ -711,6 +753,43 @@ struct hostapd_iface {
bool is_no_ir;
bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
+
+ struct hostapd_multi_hw_info *multi_hw_info;
+ unsigned int num_multi_hws;
+ struct hostapd_multi_hw_info *current_hw_info;
+
+ /* Use assisted DFS functionality from the driver. This is used only
+ * in wpa_supplicant builds for P2P GO functionality. */
+ bool assisted_dfs;
+
+#ifdef CONFIG_AFC
+ struct wpabuf *afc_request;
+ char *afc_response;
+ struct {
+ unsigned int request_id;
+ int timeout;
+ unsigned int num_freq_range;
+ struct afc_freq_range_elem {
+ int low_freq;
+ int high_freq;
+ /**
+ * Maximum EIRP power spectral density received from
+ * the AFC coordinator for this band
+ */
+ double max_psd;
+ } *freq_range;
+ unsigned int num_chan_info;
+ struct afc_chan_info_elem {
+ int chan;
+ /**
+ * Maximum EIRP power received from the AFC coordinator
+ * for this channel for each op_class
+ */
+ double power[5];
+ } *chan_info_list;
+ bool data_valid;
+ } afc;
+#endif /* CONFIG_AFC */
};
/* hostapd.c */
@@ -733,6 +812,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
struct hostapd_iface *
hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
const char *config_fname, int debug);
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+ struct hapd_interfaces *interfaces);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -753,6 +834,8 @@ void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
+int hostapd_force_channel_switch(struct hostapd_iface *iface,
+ struct csa_settings *settings);
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params);
@@ -763,6 +846,38 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+#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_send_request(struct hostapd_iface *iface);
+void hostapd_afc_stop(struct hostapd_iface *iface);
+void hostap_afc_disable_channels(struct hostapd_iface *iface);
+#else /* CONFIG_AFC */
+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;
+}
+
+static inline void hostapd_afc_send_request(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_afc_stop(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
+{
+}
+#endif /* CONFIG_AFC */
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -781,6 +896,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *req_ie, size_t req_ielen, const u8 *resp_ie,
size_t resp_ielen, const u8 *link_addr, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+ struct sta_info *sta, const u8 *addr);
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
const u8 *addr, int reason_code);
@@ -806,6 +923,10 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
enum smps_mode smps_mode,
enum chan_width chan_width, u8 rx_nss);
+int hostapd_change_config_freq(struct hostapd_data *hapd,
+ struct hostapd_config *conf,
+ struct hostapd_freq_params *params,
+ struct hostapd_freq_params *old_params);
#ifdef CONFIG_FST
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
struct fst_wpa_obj *iface_obj);
@@ -822,15 +943,22 @@ bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
u8 hostapd_get_mld_id(struct hostapd_data *hapd);
int hostapd_mld_add_link(struct hostapd_data *hapd);
int hostapd_mld_remove_link(struct hostapd_data *hapd);
+u8 hostapd_get_active_links(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+ struct beacon_data *beacon);
void free_beacon_data(struct beacon_data *beacon);
int hostapd_fill_cca_settings(struct hostapd_data *hapd,
struct cca_settings *settings);
+void hostapd_switch_color_timeout_handler(void *eloop_data, void *user_ctx);
+bool hostapd_is_cca_in_progress(struct hostapd_iface *iface);
+void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface);
#ifdef CONFIG_IEEE80211BE
bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+void hostapd_mld_interface_freed(struct hostapd_data *hapd);
#define for_each_mld_link(partner, self) \
dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
@@ -849,4 +977,53 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
+void hostapd_get_oper_chan_info_of_bss(struct hostapd_data *hapd,
+ enum oper_chan_width *width,
+ u8 *seg0, u8 *seg1);
+
+u8 hostapd_get_oper_class_of_bss(struct hostapd_data *hapd);
+
+enum oper_chan_width
+hostapd_get_oper_chan_width_of_bss(struct hostapd_data *hapd);
+
+static inline bool ap_pmf_enabled(struct hostapd_bss_config *conf)
+{
+ return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+ conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+ conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
+enum oper_chan_width
+hostapd_chan_width_from_freq_params(struct hostapd_freq_params *freq_params);
+
+struct hostapd_data *
+hostapd_get_mbssid_bss_by_idx(struct hostapd_data *hapd, size_t idx);
+
+bool hostapd_acceptable_sta_addr(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *addr_ml, bool mld);
+
+static inline bool
+hostapd_is_ht_enabled(struct hostapd_data *hapd)
+{
+ return hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+}
+
+static inline bool
+hostapd_is_vht_enabled(struct hostapd_data *hapd)
+{
+ return hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+}
+
+static inline bool
+hostapd_is_he_enabled(struct hostapd_data *hapd)
+{
+ return hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
+}
+
+static inline bool
+hostapd_is_eht_enabled(struct hostapd_data *hapd)
+{
+ return hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
+}
+
#endif /* HOSTAPD_H */
diff --git a/contrib/wpa/src/ap/hs20.c b/contrib/wpa/src/ap/hs20.c
index 05e9b9d20fd7..4ae3b6b49b0a 100644
--- a/contrib/wpa/src/ap/hs20.c
+++ b/contrib/wpa/src/ap/hs20.c
@@ -44,113 +44,6 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
}
-u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
-{
- u8 *len;
- u16 capab;
-
- if (!hapd->conf->osen)
- return eid;
-
- *eid++ = WLAN_EID_VENDOR_SPECIFIC;
- len = eid++; /* to be filled */
- WPA_PUT_BE24(eid, OUI_WFA);
- eid += 3;
- *eid++ = HS20_OSEN_OUI_TYPE;
-
- /* Group Data Cipher Suite */
- RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
- eid += RSN_SELECTOR_LEN;
-
- /* Pairwise Cipher Suite Count and List */
- WPA_PUT_LE16(eid, 1);
- eid += 2;
- RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
- eid += RSN_SELECTOR_LEN;
-
- /* AKM Suite Count and List */
- WPA_PUT_LE16(eid, 1);
- eid += 2;
- RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
- eid += RSN_SELECTOR_LEN;
-
- /* RSN Capabilities */
- capab = 0;
- if (hapd->conf->wmm_enabled) {
- /* 4 PTKSA replay counters when using WMM */
- capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
- }
- if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- capab |= WPA_CAPABILITY_MFPC;
- if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
- capab |= WPA_CAPABILITY_MFPR;
- }
-#ifdef CONFIG_OCV
- if (hapd->conf->ocv &&
- (hapd->iface->drv_flags2 &
- (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
- capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
- WPA_PUT_LE16(eid, capab);
- eid += 2;
-
- *len = eid - len - 1;
-
- return eid;
-}
-
-
-int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
- u8 osu_method, const char *url)
-{
- struct wpabuf *buf;
- size_t len = 0;
- int ret;
-
- /* TODO: should refuse to send notification if the STA is not associated
- * or if the STA did not indicate support for WNM-Notification */
-
- if (url) {
- len = 1 + os_strlen(url);
- if (5 + len > 255) {
- wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
- "WNM-Notification: '%s'", url);
- return -1;
- }
- }
-
- buf = wpabuf_alloc(4 + 7 + len);
- if (buf == NULL)
- return -1;
-
- wpabuf_put_u8(buf, WLAN_ACTION_WNM);
- wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
- wpabuf_put_u8(buf, 1); /* Dialog token */
- wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
-
- /* Subscription Remediation subelement */
- wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(buf, 5 + len);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
- if (url) {
- wpabuf_put_u8(buf, len - 1);
- wpabuf_put_data(buf, url, len - 1);
- wpabuf_put_u8(buf, osu_method);
- } else {
- /* Server URL and Server Method fields not included */
- wpabuf_put_u8(buf, 0);
- }
-
- ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
- wpabuf_head(buf), wpabuf_len(buf));
-
- wpabuf_free(buf);
-
- return ret;
-}
-
-
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
const u8 *addr,
const struct wpabuf *payload)
diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c
index c4556603d3da..9fe84f70add2 100644
--- a/contrib/wpa/src/ap/hw_features.c
+++ b/contrib/wpa/src/ap/hw_features.c
@@ -72,16 +72,62 @@ static char * dfs_info(struct hostapd_channel_data *chan)
#endif /* CONFIG_NO_STDOUT_DEBUG */
+static void move_survey_data(struct hostapd_channel_data *new_chan,
+ struct hostapd_channel_data *old_chan)
+{
+ struct freq_survey *survey, *tmp;
+
+ /* Copy the survey list from the old to new channel */
+ if (old_chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) {
+ dl_list_init(&new_chan->survey_list);
+ dl_list_for_each_safe(survey, tmp, &old_chan->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ dl_list_add(&new_chan->survey_list,
+ &survey->list);
+ }
+ new_chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+ }
+}
+
+
+static void move_hw_mode_data(struct hostapd_hw_modes *new_feature,
+ struct hostapd_hw_modes *old_feature)
+{
+ int i, j;
+
+ for (i = 0; i < new_feature->num_channels; i++) {
+ struct hostapd_channel_data *new_chan;
+ struct hostapd_channel_data *old_chan;
+
+ new_chan = &new_feature->channels[i];
+
+ for (j = 0; j < old_feature->num_channels; j++) {
+ old_chan = &old_feature->channels[j];
+
+ if (new_chan->freq != old_chan->freq)
+ continue;
+
+ move_survey_data(new_chan, old_chan);
+ break;
+ }
+ }
+}
+
+
int hostapd_get_hw_features(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
int i, j;
+ unsigned int k;
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
u8 dfs_domain;
enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY;
bool is_6ghz = false;
bool orig_mode_valid = false;
+ struct hostapd_multi_hw_info *multi_hw_info;
+ unsigned int num_multi_hws;
if (hostapd_drv_none(hapd))
return -1;
@@ -110,9 +156,6 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
is_6ghz = iface->current_mode->is_6ghz;
iface->current_mode = NULL;
}
- hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
- iface->hw_features = modes;
- iface->num_hw_features = num_modes;
for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *feature = &modes[i];
@@ -142,7 +185,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
} else if (((feature->channels[j].flag &
HOSTAPD_CHAN_RADAR) &&
!(iface->drv_flags &
- WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+ WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !iface->assisted_dfs) ||
(feature->channels[j].flag &
HOSTAPD_CHAN_NO_IR)) {
feature->channels[j].flag |=
@@ -160,29 +204,66 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
feature->channels[j].max_tx_power,
dfs ? dfs_info(&feature->channels[j]) : "");
}
+
+ /* Move any old data that should be kept */
+ for (j = 0; j < iface->num_hw_features; j++) {
+ struct hostapd_hw_modes *old_feature =
+ &iface->hw_features[j];
+
+ if (feature->mode != old_feature->mode)
+ continue;
+
+ move_hw_mode_data(feature, old_feature);
+ }
}
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = modes;
+ iface->num_hw_features = num_modes;
+
+ hostap_afc_disable_channels(iface);
+
if (orig_mode_valid && !iface->current_mode) {
wpa_printf(MSG_ERROR,
"%s: Could not update iface->current_mode",
__func__);
}
+ multi_hw_info = hostapd_get_multi_hw_info(hapd, &num_multi_hws);
+ if (!multi_hw_info)
+ return 0;
+
+ hostapd_free_multi_hw_info(iface->multi_hw_info);
+ iface->multi_hw_info = multi_hw_info;
+ iface->num_multi_hws = num_multi_hws;
+
+ wpa_printf(MSG_DEBUG, "Multiple underlying hardwares info:");
+
+ for (k = 0; k < num_multi_hws; k++) {
+ struct hostapd_multi_hw_info *hw_info = &multi_hw_info[k];
+
+ wpa_printf(MSG_DEBUG,
+ " %d. hw_idx=%u, frequency range: %d-%d MHz",
+ k + 1, hw_info->hw_idx, hw_info->start_freq,
+ hw_info->end_freq);
+ }
+
return 0;
}
-int hostapd_prepare_rates(struct hostapd_iface *iface,
+int hostapd_prepare_rates(struct hostapd_data *hapd,
struct hostapd_hw_modes *mode)
{
+ struct hostapd_bss_config *conf = hapd->conf;
int i, num_basic_rates = 0;
- int basic_rates_a[] = { 60, 120, 240, -1 };
- int basic_rates_b[] = { 10, 20, -1 };
- int basic_rates_g[] = { 10, 20, 55, 110, -1 };
- int *basic_rates;
+ int basic_rates_a[] = { 60, 120, 240, 0 };
+ int basic_rates_b[] = { 10, 20, 0 };
+ int basic_rates_g[] = { 10, 20, 55, 110, 0 };
+ const int *basic_rates;
- if (iface->conf->basic_rates)
- basic_rates = iface->conf->basic_rates;
+ if (conf->basic_rates)
+ basic_rates = conf->basic_rates;
else switch (mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
basic_rates = basic_rates_a;
@@ -199,22 +280,15 @@ int hostapd_prepare_rates(struct hostapd_iface *iface,
return -1;
}
- i = 0;
- while (basic_rates[i] >= 0)
- i++;
- if (i)
- i++; /* -1 termination */
- os_free(iface->basic_rates);
- iface->basic_rates = os_malloc(i * sizeof(int));
- if (iface->basic_rates)
- os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
+ os_free(hapd->basic_rates);
+ hapd->basic_rates = int_array_dup(basic_rates);
- os_free(iface->current_rates);
- iface->num_rates = 0;
+ os_free(hapd->current_rates);
+ hapd->num_rates = 0;
- iface->current_rates =
+ hapd->current_rates =
os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
- if (!iface->current_rates) {
+ if (!hapd->current_rates) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
"table.");
return -1;
@@ -223,27 +297,26 @@ int hostapd_prepare_rates(struct hostapd_iface *iface,
for (i = 0; i < mode->num_rates; i++) {
struct hostapd_rate_data *rate;
- if (iface->conf->supported_rates &&
- !hostapd_rate_found(iface->conf->supported_rates,
- mode->rates[i]))
+ if (conf->supported_rates &&
+ !int_array_includes(conf->supported_rates, mode->rates[i]))
continue;
- rate = &iface->current_rates[iface->num_rates];
+ rate = &hapd->current_rates[hapd->num_rates];
rate->rate = mode->rates[i];
- if (hostapd_rate_found(basic_rates, rate->rate)) {
+ if (int_array_includes(basic_rates, rate->rate)) {
rate->flags |= HOSTAPD_RATE_BASIC;
num_basic_rates++;
}
wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
- iface->num_rates, rate->rate, rate->flags);
- iface->num_rates++;
+ hapd->num_rates, rate->rate, rate->flags);
+ hapd->num_rates++;
}
- if ((iface->num_rates == 0 || num_basic_rates == 0) &&
- (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
+ if ((hapd->num_rates == 0 || num_basic_rates == 0) &&
+ (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) {
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
"rate sets (%d,%d).",
- iface->num_rates, num_basic_rates);
+ hapd->num_rates, num_basic_rates);
return -1;
}
@@ -506,12 +579,6 @@ static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
else
ieee80211n_scan_channels_5g(iface, &params);
- params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
- if (iface->bss[0]->conf->mld_ap)
- params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
-
ret = hostapd_driver_scan(iface->bss[0], &params);
iface->num_ht40_scan_tries++;
os_free(params.freqs);
@@ -533,6 +600,9 @@ static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
wpa_printf(MSG_DEBUG,
"Failed to request a scan in device, bringing up in HT20 mode");
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
+ hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT);
iface->conf->secondary_channel = 0;
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
hostapd_setup_interface_complete(iface, 0);
@@ -563,11 +633,6 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
else
ieee80211n_scan_channels_5g(iface, &params);
- params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
- if (iface->bss[0]->conf->mld_ap)
- params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
ret = hostapd_driver_scan(iface->bss[0], &params);
os_free(params.freqs);
@@ -716,10 +781,18 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211BE
+static int ieee80211be_supported_eht_capab(struct hostapd_iface *iface)
+{
+ return iface->current_mode->eht_capab[IEEE80211_MODE_AP].eht_supported;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
#ifdef CONFIG_IEEE80211AX
static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
{
- return 1;
+ return iface->current_mode->he_capab[IEEE80211_MODE_AP].he_supported;
}
#endif /* CONFIG_IEEE80211AX */
@@ -743,10 +816,19 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
if (!ieee80211n_supported_ht_capab(iface))
return -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be &&
+ !ieee80211be_supported_eht_capab(iface)) {
+ wpa_printf(MSG_ERROR, "Driver does not support EHT");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
if (iface->conf->ieee80211ax &&
- !ieee80211ax_supported_he_capab(iface))
+ !ieee80211ax_supported_he_capab(iface)) {
+ wpa_printf(MSG_ERROR, "Driver does not support HE");
return -1;
+ }
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
if (iface->conf->ieee80211ac &&
@@ -1003,16 +1085,87 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
}
+static int hostapd_is_usable_chan_seg(struct hostapd_iface *iface)
+{
+ int central, oper_chwidth, err;
+ int start_chan, start_freq, chan_num, i;
+
+ oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ central = hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+
+ switch (oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ chan_num = 4;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ chan_num = 8;
+ break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ chan_num = 16;
+ break;
+ default:
+ return 0;
+ }
+ start_chan = central - chan_num * 2 + 2;
+ start_freq = hw_get_freq(iface->current_mode, start_chan);
+
+ if (!start_freq) {
+ wpa_printf(MSG_ERROR, "Frequency not present (seg0)");
+ return 0;
+ }
+
+ for (i = 0; i < chan_num; i++) {
+ int freq = start_freq + i * 20;
+
+ err = hostapd_is_usable_chan(iface, freq, freq == iface->freq);
+ if (err <= 0) {
+ wpa_printf(MSG_ERROR,
+ "Frequency %d is not allowed (seg0)",
+ freq);
+ return err;
+ }
+ }
+
+ if (oper_chwidth != CONF_OPER_CHWIDTH_80P80MHZ)
+ return 1;
+
+ central = hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+ start_chan = central - chan_num * 2 + 2;
+ start_freq = hw_get_freq(iface->current_mode, start_chan);
+
+ if (!start_freq) {
+ wpa_printf(MSG_ERROR, "Frequency not present (seg1)");
+ return 0;
+ }
+
+ for (i = 0; i < chan_num; i++) {
+ int freq = start_freq + i * 20;
+
+ err = hostapd_is_usable_chan(iface, freq, freq == iface->freq);
+ if (err <= 0) {
+ wpa_printf(MSG_ERROR,
+ "Frequency %d is not allowed (seg1)",
+ freq);
+ return err;
+ }
+ }
+
+ return 1;
+}
+
+
/* Returns:
* 1 = usable
* 0 = not usable
* -1 = not currently usable due to 6 GHz NO-IR
*/
-static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
- int secondary_freq;
+ int secondary_freq, new_sec;
+ enum hostapd_chan_width_attr bw_flag;
struct hostapd_channel_data *pri_chan;
- int err, err2;
+ int err, err2, central, oper_chwidth;
if (!iface->current_mode)
return 0;
@@ -1025,11 +1178,6 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
return 0;
}
- err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
- if (err <= 0) {
- wpa_printf(MSG_ERROR, "Primary frequency not allowed");
- return err;
- }
err = hostapd_is_usable_edmg(iface);
if (err <= 0)
return err;
@@ -1037,6 +1185,24 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
if (!hostapd_is_usable_punct_bitmap(iface))
return 0;
+ oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+ /*
+ * The central channel is not filled in acs_adjust_center_freq() after
+ * ACS of 80+80 MHz. In that case we only check the primary 40 MHz.
+ */
+ central = hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ oper_chwidth != CONF_OPER_CHWIDTH_USE_HT && central)
+ return hostapd_is_usable_chan_seg(iface);
+
+ err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
+ if (err <= 0) {
+ wpa_printf(MSG_ERROR, "Primary frequency not allowed");
+ return err;
+ }
+
if (!iface->conf->secondary_channel)
return 1;
@@ -1053,18 +1219,19 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
if (!iface->conf->ht40_plus_minus_allowed)
return err;
- /* Both HT40+ and HT40- are set, pick a valid secondary channel */
- secondary_freq = iface->freq + 20;
- err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
- iface->conf->secondary_channel = 1;
- return 1;
- }
+ /* Both HT40+ and HT40- are set, check the swapped pri/sec channel
+ * option for a 40 MHz channel */
+ new_sec = -iface->conf->secondary_channel;
+ bw_flag = new_sec == 1 ?
+ HOSTAPD_CHAN_WIDTH_40P : HOSTAPD_CHAN_WIDTH_40M;
- secondary_freq = iface->freq - 20;
+ secondary_freq = iface->freq + new_sec * 20;
err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
- iface->conf->secondary_channel = -1;
+ if (err2 <= 0)
+ return err;
+
+ if (pri_chan->allowed_bw & bw_flag) {
+ iface->conf->secondary_channel = new_sec;
return 1;
}
@@ -1391,3 +1558,34 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface,
}
return 0;
}
+
+
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info)
+{
+ os_free(multi_hw_info);
+}
+
+
+int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq)
+{
+ struct hostapd_multi_hw_info *hw_info;
+ unsigned int i;
+
+ if (!iface->num_multi_hws)
+ return 0;
+
+ for (i = 0; i < iface->num_multi_hws; i++) {
+ hw_info = &iface->multi_hw_info[i];
+
+ if (hw_info->start_freq <= oper_freq &&
+ hw_info->end_freq >= oper_freq) {
+ iface->current_hw_info = hw_info;
+ wpa_printf(MSG_DEBUG,
+ "Mode: Selected underlying hardware: hw_idx=%u",
+ iface->current_hw_info->hw_idx);
+ return 0;
+ }
+ }
+
+ return -1;
+}
diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h
index c682c6d20b81..4993e68afd62 100644
--- a/contrib/wpa/src/ap/hw_features.h
+++ b/contrib/wpa/src/ap/hw_features.h
@@ -24,12 +24,15 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
int hostapd_check_ht_capab(struct hostapd_iface *iface);
int hostapd_check_edmg_capab(struct hostapd_iface *iface);
int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface);
-int hostapd_prepare_rates(struct hostapd_iface *iface,
+int hostapd_prepare_rates(struct hostapd_data *hapd,
struct hostapd_hw_modes *mode);
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);
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info);
+int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq);
+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,
@@ -77,7 +80,7 @@ static inline int hostapd_check_edmg_capab(struct hostapd_iface *iface)
return 0;
}
-static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
+static inline int hostapd_prepare_rates(struct hostapd_data *hapd,
struct hostapd_hw_modes *mode)
{
return 0;
@@ -103,6 +106,21 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface)
return 0;
}
+static inline
+void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info)
+{
+}
+
+static inline int hostapd_set_current_hw_info(struct hostapd_iface *iface,
+ u32 oper_freq)
+{
+ return 0;
+}
+
+static inline int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+ return 1;
+}
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c
index 6c516bc8a93d..dbba14cd55be 100644
--- a/contrib/wpa/src/ap/ieee802_11.c
+++ b/contrib/wpa/src/ap/ieee802_11.c
@@ -1,6 +1,7 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -17,6 +18,8 @@
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
@@ -26,6 +29,7 @@
#include "common/wpa_common.h"
#include "common/wpa_ctrl.h"
#include "common/ptksa_cache.h"
+#include "common/nan_de.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -86,6 +90,10 @@ static void handle_auth(struct hostapd_data *hapd,
int rssi, int from_queue);
static int add_associated_sta(struct hostapd_data *hapd,
struct sta_info *sta, int reassoc);
+#ifdef CONFIG_IEEE8021X_AUTH
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_search(void *ctx, const u8 *spa, const u8 *pmkid, bool is_ml);
+#endif /* CONFIG_IEEE8021X_AUTH */
static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len)
@@ -116,142 +124,88 @@ static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len)
}
-u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
{
- u8 *pos = eid;
- int i, num, count;
- int h2e_required;
-
- if (hapd->iface->current_rates == NULL)
- return eid;
+ u8 *pos = buf;
+ int i;
- *pos++ = WLAN_EID_SUPP_RATES;
- num = hapd->iface->num_rates;
- if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
- num++;
- if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
- num++;
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
- num++;
-#endif /* CONFIG_IEEE80211AX */
- h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
- hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
- wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
- if (h2e_required)
- num++;
- if (num > 8) {
- /* rest of the rates are encoded in Extended supported
- * rates element */
- num = 8;
- }
+ if (!hapd->current_rates)
+ return 0;
- *pos++ = num;
- for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
- i++) {
- count++;
- *pos = hapd->iface->current_rates[i].rate / 5;
- if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+ for (i = 0; i < hapd->num_rates; i++) {
+ *pos = hapd->current_rates[i].rate / 5;
+ if (hapd->current_rates[i].flags & HOSTAPD_RATE_BASIC)
*pos |= 0x80;
pos++;
}
- if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
- count++;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
- }
- if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
- count++;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
- }
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
- count++;
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
- }
#endif /* CONFIG_IEEE80211AX */
- if (h2e_required && count < 8) {
- count++;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be &&
+ (hapd->iconf->require_eht || hapd->conf->bss_require_eht))
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY;
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_SAE
+ if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+ wpa_key_mgmt_only_sae(hapd->conf->wpa_key_mgmt))
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
- }
+#endif /* CONFIG_SAE */
- return pos;
+ return pos - buf;
}
-u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
- int i, num, count;
- int h2e_required;
+ u8 buf[100];
+ size_t len;
- hapd->conf->xrates_supported = false;
- if (hapd->iface->current_rates == NULL)
+ len = hostapd_supp_rates(hapd, buf);
+ if (len == 0)
return eid;
+ /* Only up to first eight values in this element */
+ if (len > 8)
+ len = 8;
- num = hapd->iface->num_rates;
- if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
- num++;
- if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
- num++;
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
- num++;
-#endif /* CONFIG_IEEE80211AX */
- h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
- hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
- hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
- wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
- if (h2e_required)
- num++;
- if (num <= 8)
- return eid;
- num -= 8;
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = len;
+ os_memcpy(pos, buf, len);
+ pos += len;
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = num;
- for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
- i++) {
- count++;
- if (count <= 8)
- continue; /* already in SuppRates IE */
- *pos = hapd->iface->current_rates[i].rate / 5;
- if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
- *pos |= 0x80;
- pos++;
- }
+ return pos;
+}
- if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
- count++;
- if (count > 8)
- *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
- }
- if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
- count++;
- if (count > 8)
- *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
- }
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 buf[100];
+ size_t len;
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
- count++;
- if (count > 8)
- *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
- }
-#endif /* CONFIG_IEEE80211AX */
+ len = hostapd_supp_rates(hapd, buf);
+ /* Starting from the 9th value for this element */
+ if (len <= 8)
+ return eid;
- if (h2e_required) {
- count++;
- if (count > 8)
- *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
- }
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = len - 8;
+ os_memcpy(pos, &buf[8], len - 8);
+ pos += len - 8;
- hapd->conf->xrates_supported = true;
return pos;
}
@@ -308,11 +262,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
if (hapd->conf->wpa)
privacy = 1;
-#ifdef CONFIG_HS20
- if (hapd->conf->osen)
- privacy = 1;
-#endif /* CONFIG_HS20 */
-
if (privacy)
capab |= WLAN_CAPABILITY_PRIVACY;
@@ -414,18 +363,36 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
const u8 *sa = hapd->own_addr;
struct wpabuf *ml_resp = NULL;
+ size_t ml_resp_len = 0;
+#ifdef CONFIG_IEEE8021X_AUTH
+ size_t mic_len = 0;
+#endif /* CONFIG_IEEE8021X_AUTH */
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta)) {
ml_resp = hostapd_ml_auth_resp(hapd);
if (!ml_resp)
return -1;
+ ml_resp_len = wpabuf_len(ml_resp);
}
#endif /* CONFIG_IEEE80211BE */
- rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
- if (ml_resp)
- rlen += wpabuf_len(ml_resp);
+ rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len + ml_resp_len;
+#ifdef CONFIG_IEEE8021X_AUTH
+ /* Add MIC element for an Authentication frame carrying an EAP-Success
+ * message and for an Authentication frame with transaction sequence
+ * frame 2, if PMKSA caching was used.
+ */
+ if (auth_alg == WLAN_AUTH_802_1X &&
+ (resp == WLAN_STATUS_802_1_X_AUTH_SUCCESS ||
+ sta->eap_auth_data.add_mic)) {
+ mic_len = wpa_mic_len(sta->eap_auth_data.akm,
+ sta->eap_auth_data.pmk_len,
+ RSN_HASH_NOT_SPECIFIED);
+ rlen += 2 + mic_len;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
buf = os_zalloc(rlen);
if (!buf) {
wpabuf_free(ml_resp);
@@ -462,7 +429,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
#ifdef CONFIG_SAE
if (hapd->conf->sae_confirm_immediate == 2 &&
auth_alg == WLAN_AUTH_SAE) {
- if (auth_transaction == 1 && sta &&
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && sta &&
(resp == WLAN_STATUS_SUCCESS ||
resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
resp == WLAN_STATUS_SAE_PK)) {
@@ -474,7 +441,8 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_SUCCESS;
}
- if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM &&
+ sta && sta->sae_postponed_commit) {
wpa_printf(MSG_DEBUG,
"TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
if (hostapd_drv_send_mlme(hapd,
@@ -489,6 +457,59 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (auth_alg == WLAN_AUTH_802_1X &&
+ (resp == WLAN_STATUS_802_1_X_AUTH_SUCCESS ||
+ sta->eap_auth_data.add_mic)) {
+ const u8 *frame, *data, *rsne, *rsnxe;
+ u8 data_buf[500], mic[WPA_1X_MAX_MIC_LEN];
+ size_t frame_len, data_len;
+ const u8 *aa = sa;
+ u8 *ptr = reply->u.auth.variable + ies_len + ml_resp_len;
+
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ aa = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ if (!rsne) {
+ wpa_printf(MSG_INFO, "No AP RSNE");
+ return -1;
+ }
+ os_memcpy(data_buf, rsne, 2 + rsne[1]);
+ data_len = 2 + rsne[1];
+
+ rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ if (rsnxe) {
+ wpa_printf(MSG_DEBUG, "Found AP RSNXE");
+ os_memcpy(&data_buf[data_len], rsnxe, 2 + rsnxe[1]);
+ data_len += 2 + rsnxe[1];
+ } else {
+ wpa_printf(MSG_DEBUG, "No AP RSNXE");
+ }
+ data = data_buf;
+
+ /* MIC element */
+ ptr[0] = WLAN_EID_MIC;
+ ptr[1] = mic_len;
+ os_memset(ptr + 2, 0, mic_len);
+
+ frame = (const u8 *) &reply->u.auth.auth_alg;
+ frame_len = rlen - IEEE80211_HDRLEN;
+ if (wpa_auth_8021x_mic(sta->eap_auth_data.akm,
+ sta->eap_auth_data.ptk.kck,
+ sta->eap_auth_data.ptk.kck_len,
+ aa, sta->addr, data, data_len,
+ frame, frame_len, mic)) {
+ wpa_printf(MSG_INFO, "Failed to derive MIC");
+ return -1;
+ }
+ os_memcpy(ptr + 2, mic, mic_len);
+ }
+#endif /* CONFIF_IEEE8021X_AUTH */
+
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
wpa_printf(MSG_INFO, "send_auth_reply: send failed");
else
@@ -500,6 +521,26 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
}
+#ifdef CONFIG_IEEE8021X_AUTH
+static void send_8021x_auth_reply(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u16 auth_transaction, u16 resp,
+ struct wpabuf *ies)
+{
+ send_auth_reply(hapd, sta, sta->addr, WLAN_AUTH_802_1X,
+ auth_transaction, resp, wpabuf_head(ies),
+ wpabuf_len(ies), "send-8021x-auth-reply");
+
+ if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS &&
+ resp != WLAN_STATUS_802_1_X_AUTH_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+ wpabuf_free(ies);
+}
+#endif /* IEEE8021X_AUTH */
+
+
#ifdef CONFIG_IEEE80211R_AP
static void handle_auth_ft_finish(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
@@ -547,9 +588,178 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state,
}
+static bool in_mac_addr_list(const u8 *list, unsigned int num, const u8 *addr)
+{
+ unsigned int i;
+
+ for (i = 0; list && i < num; i++) {
+ if (ether_addr_equal(&list[i * ETH_ALEN], addr))
+ return true;
+ }
+
+ return false;
+}
+
+
+static struct sae_password_entry *
+sae_password_find_pw(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sae_password_entry *pw = NULL;
+
+ if (!sta->sae || !sta->sae->tmp || !sta->sae->tmp->used_pw)
+ return NULL;
+
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (pw == sta->sae->tmp->used_pw)
+ return pw;
+ }
+
+ return NULL;
+}
+
+
+static bool is_other_sae_password(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct sae_password_entry *used_pw)
+{
+ struct sae_password_entry *pw;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (pw == used_pw ||
+ pw->identifier ||
+ !is_broadcast_ether_addr(pw->peer_addr))
+ continue;
+
+ if (in_mac_addr_list(pw->success_mac,
+ pw->num_success_mac,
+ sta->addr))
+ return true;
+
+ if (!in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+ sta->addr))
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool has_sae_success_seen(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct sae_password_entry *pw;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier ||
+ !is_broadcast_ether_addr(pw->peer_addr))
+ continue;
+
+ if (in_mac_addr_list(pw->success_mac,
+ pw->num_success_mac,
+ sta->addr))
+ return true;
+ }
+
+ return false;
+}
+
+
+static int sae_password_mark_success(struct hostapd_data *hapd,
+ struct sae_password_entry *pw,
+ const u8 *addr)
+{
+ if (in_mac_addr_list(pw->success_mac, pw->num_success_mac, addr))
+ return 0;
+
+ if (!pw->success_mac) {
+ pw->success_mac = os_zalloc(hapd->conf->sae_track_password *
+ ETH_ALEN);
+ if (!pw->success_mac)
+ return -1;
+ pw->num_success_mac = hapd->conf->sae_track_password;
+ }
+
+ os_memcpy(&pw->success_mac[pw->next_success_mac * ETH_ALEN], addr,
+ ETH_ALEN);
+ pw->next_success_mac = (pw->next_success_mac + 1) % pw->num_success_mac;
+ return 0;
+}
+
+
+static void sae_password_track_success(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct sae_password_entry *pw;
+
+ if (!hapd->conf->sae_track_password)
+ return;
+
+ pw = sae_password_find_pw(hapd, sta);
+ if (!pw)
+ return;
+
+ sae_password_mark_success(hapd, pw, sta->addr);
+}
+
+
+static bool sae_password_track_fail(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct sae_password_entry *pw;
+
+ if (!hapd->conf->sae_track_password)
+ return false;
+
+ pw = sae_password_find_pw(hapd, sta);
+ if (!pw)
+ return false;
+
+ if (in_mac_addr_list(pw->fail_mac,
+ pw->num_fail_mac,
+ sta->addr))
+ return is_other_sae_password(hapd, sta, pw);
+
+ if (!pw->fail_mac) {
+ pw->fail_mac = os_zalloc(hapd->conf->sae_track_password *
+ ETH_ALEN);
+ if (!pw->fail_mac)
+ return false;
+ pw->num_fail_mac = hapd->conf->sae_track_password;
+ }
+
+ os_memcpy(&pw->fail_mac[pw->next_fail_mac * ETH_ALEN], sta->addr,
+ ETH_ALEN);
+ pw->next_fail_mac = (pw->next_fail_mac + 1) % pw->num_fail_mac;
+
+ return is_other_sae_password(hapd, sta, pw);
+}
+
+
+int sae_password_bind(struct hostapd_data *hapd, const u8 *addr,
+ const char *password)
+{
+ struct sae_password_entry *pw;
+
+ if (!hapd->conf->sae_track_password)
+ return -1;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier ||
+ !is_broadcast_ether_addr(pw->peer_addr) ||
+ os_strcmp(password, pw->password) != 0)
+ continue;
+
+ return sae_password_mark_success(hapd, pw, addr);
+ }
+
+ return -1;
+}
+
+
const char * sae_get_password(struct hostapd_data *hapd,
struct sta_info *sta,
- const char *rx_id,
+ const u8 *rx_id, size_t rx_id_len,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt,
const struct sae_pk **s_pk)
@@ -560,6 +770,45 @@ const char * sae_get_password(struct hostapd_data *hapd,
const struct sae_pk *pk = NULL;
struct hostapd_sta_wpa_psk_short *psk = NULL;
+ /* With sae_track_password functionality enabled, try to first find the
+ * next viable wildcard-address password if a password identifier was
+ * not used. Select an wildcard-addr entry if the STA is known to have
+ * used it successfully before. If no such entry exists, pick a
+ * wildcard-addr entry that does not have a failed entry tracked for the
+ * STA. */
+ if (!rx_id && sta && hapd->conf->sae_track_password) {
+ struct sae_password_entry *success = NULL, *no_fail = NULL;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier ||
+ !is_broadcast_ether_addr(pw->peer_addr))
+ continue;
+ if (in_mac_addr_list(pw->success_mac,
+ pw->num_success_mac,
+ sta->addr)) {
+ success = pw;
+ break;
+ }
+
+ if (!no_fail &&
+ !in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+ sta->addr))
+ no_fail = pw;
+ }
+
+ pw = success ? success : no_fail;
+ if (pw) {
+ password = pw->password;
+ pt = pw->pt;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ pk = pw->pk;
+ goto found;
+ }
+ }
+
+ /* If sae_track_password functionality is not enabled or no suitable
+ * password entry was found with it, pick the first entry that matches
+ * the STA MAC address and password identifier (if used). */
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
(!sta ||
@@ -568,7 +817,8 @@ const char * sae_get_password(struct hostapd_data *hapd,
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
if (rx_id && pw->identifier &&
- os_strcmp(rx_id, pw->identifier) != 0)
+ (rx_id_len != os_strlen(pw->identifier) ||
+ os_memcmp(rx_id, pw->identifier, rx_id_len) != 0))
continue;
password = pw->password;
pt = pw->pt;
@@ -576,12 +826,12 @@ const char * sae_get_password(struct hostapd_data *hapd,
pk = pw->pk;
break;
}
- if (!password) {
+ if (!password && !rx_id && !hapd->conf->sae_password_psk) {
password = hapd->conf->ssid.wpa_passphrase;
pt = hapd->conf->ssid.pt;
}
- if (!password && sta) {
+ if (!password && sta && !rx_id) {
for (psk = sta->psk; psk; psk = psk->next) {
if (psk->is_passphrase) {
password = psk->passphrase;
@@ -590,6 +840,74 @@ const char * sae_get_password(struct hostapd_data *hapd,
}
}
+ /* Try to decrypt the received password identifier if no plaintext
+ * identifier match was found. */
+ if (!password && rx_id && rx_id_len > 4 + 4 + AES_BLOCK_SIZE &&
+ hapd->conf->sae_pw_id_key) {
+ u8 *plain, *pos, *counter;
+ size_t plain_len;
+ const u8 *id;
+ size_t id_len;
+
+ plain = os_malloc(rx_id_len);
+ if (!plain)
+ goto fail;
+ if (aes_siv_decrypt(
+ wpabuf_head(hapd->conf->sae_pw_id_key),
+ wpabuf_len(hapd->conf->sae_pw_id_key),
+ rx_id, rx_id_len, 0, NULL, NULL, plain) < 0)
+ goto fail;
+ plain_len = rx_id_len - AES_BLOCK_SIZE;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "SAE: Decrypted password identifier info",
+ plain, plain_len);
+ /* 4 octet date | Password ID | <padding> | 4 octet counter */
+ counter = plain + plain_len - 4;
+ wpa_printf(MSG_DEBUG, "SAE: Generation time %u counter %u",
+ WPA_GET_BE32(plain), WPA_GET_BE32(counter));
+ id = pos = plain + 4;
+ while (pos < counter) {
+ if (*pos == 0x00)
+ break;
+ pos++;
+ }
+ id_len = pos - id;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "SAE: Decrypted password identifier",
+ id, id_len);
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ (!sta ||
+ !ether_addr_equal(pw->peer_addr, sta->addr)))
+ continue;
+ if (!pw->identifier ||
+ os_strlen(pw->identifier) != id_len ||
+ os_memcmp(id, pw->identifier, id_len) != 0)
+ continue;
+ password = pw->password;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ pk = pw->pk;
+ if (sta && sta->sae && sta->sae->tmp) {
+ os_free(sta->sae->tmp->dec_pw_id);
+ sta->sae->tmp->dec_pw_id =
+ os_zalloc(id_len + 1);
+ if (sta->sae->tmp->dec_pw_id) {
+ os_memcpy(sta->sae->tmp->dec_pw_id,
+ id, id_len);
+ sta->sae->tmp->dec_pw_id_len = id_len;
+ sta->sae->tmp->pw_id_counter =
+ WPA_GET_BE32(counter);
+ wpa_printf(MSG_DEBUG,
+ "SAE: Bound decrypted password identifier to STA");
+ }
+ }
+ break;
+ }
+ fail:
+ os_free(plain);
+ }
+
+found:
if (pw_entry)
*pw_entry = pw;
if (s_pt)
@@ -608,7 +926,8 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
struct wpabuf *buf;
const char *password = NULL;
struct sae_password_entry *pw;
- const char *rx_id = NULL;
+ const u8 *rx_id = NULL;
+ size_t rx_id_len = 0;
int use_pt = 0;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
@@ -620,7 +939,11 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211BE */
if (sta->sae->tmp) {
- rx_id = sta->sae->tmp->pw_id;
+ rx_id = sta->sae->tmp->parsed_pw_id ?
+ sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
+ rx_id_len = sta->sae->tmp->parsed_pw_id ?
+ sta->sae->tmp->parsed_pw_id_len :
+ sta->sae->tmp->pw_id_len;
use_pt = sta->sae->h2e;
#ifdef CONFIG_SAE_PK
os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
@@ -636,16 +959,49 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
status_code == WLAN_STATUS_SAE_PK)
use_pt = 1;
- password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
- if (!password || (use_pt && !pt)) {
+ password = sae_get_password(hapd, sta, rx_id, rx_id_len, &pw, &pt, &pk);
+ if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
- if (update && use_pt &&
- sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
- NULL, pk) < 0)
- return NULL;
+ if (use_pt) {
+ struct sae_pt *tmp_pt = NULL;
+ bool failed = false;
+
+ if (!pt && pw) {
+ int groups[2] = { sta->sae->group, 0 };
+
+ wpa_printf(MSG_DEBUG,
+ "SAE: Derive PT for encrypted PW ID");
+ tmp_pt = sae_derive_pt(groups, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len,
+ (const u8 *) pw->password,
+ os_strlen(pw->password),
+ rx_id, rx_id_len);
+ if (!tmp_pt) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Could not derive PT");
+ return NULL;
+ }
+ pt = tmp_pt;
+ update = 1;
+ }
+
+ if (!pt) {
+ wpa_printf(MSG_DEBUG, "SAE: No PT available");
+ return NULL;
+ }
+
+ if (update &&
+ sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
+ NULL, pk) < 0)
+ failed = true;
+
+ sae_deinit_pt(tmp_pt);
+ if (failed)
+ return NULL;
+ }
if (update && !use_pt &&
sae_prepare_commit(own_addr, sta->addr,
@@ -655,6 +1011,9 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
return NULL;
}
+ if (pw && sta->sae->tmp)
+ sta->sae->tmp->used_pw = pw;
+
if (pw && pw->vlan_id) {
if (!sta->sae->tmp) {
wpa_printf(MSG_INFO,
@@ -665,11 +1024,11 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
}
buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
- (rx_id ? 3 + os_strlen(rx_id) : 0));
+ (rx_id ? 3 + rx_id_len : 0));
if (buf &&
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
sta->sae->tmp->anti_clogging_token : NULL,
- rx_id) < 0) {
+ rx_id, rx_id_len) < 0) {
wpabuf_free(buf);
buf = NULL;
}
@@ -712,7 +1071,8 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
u16 status;
data = auth_build_sae_commit(hapd, sta, update, status_code);
- if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+ if (!data && sta->sae->tmp &&
+ (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -927,41 +1287,52 @@ static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
}
-void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+static int sae_assign_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ int vlan_id)
{
#ifndef CONFIG_NO_VLAN
struct vlan_description vlan_desc;
- if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
+ if (vlan_id > 0) {
wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
" to VLAN ID %d",
- MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
+ MAC2STR(sta->addr), vlan_id);
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
os_memset(&vlan_desc, 0, sizeof(vlan_desc));
vlan_desc.notempty = 1;
- vlan_desc.untagged = sta->sae->tmp->vlan_id;
+ vlan_desc.untagged = vlan_id;
if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
wpa_printf(MSG_INFO,
"Invalid VLAN ID %d in sae_password",
- sta->sae->tmp->vlan_id);
- return;
+ vlan_id);
+ return -1;
}
if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
ap_sta_bind_vlan(hapd, sta) < 0) {
wpa_printf(MSG_INFO,
"Failed to assign VLAN ID %d from sae_password to "
- MACSTR, sta->sae->tmp->vlan_id,
+ MACSTR, vlan_id,
MAC2STR(sta->addr));
- return;
+ return -1;
}
} else {
- sta->vlan_id = sta->sae->tmp->vlan_id;
+ sta->vlan_id = vlan_id;
}
}
#endif /* CONFIG_NO_VLAN */
+ return 0;
+}
+
+
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->sae->tmp &&
+ sae_assign_vlan(hapd, sta, sta->sae->tmp->vlan_id) < 0)
+ return;
+
sta->flags |= WLAN_STA_AUTH;
sta->auth_alg = WLAN_AUTH_SAE;
mlme_authenticate_indication(hapd, sta);
@@ -972,8 +1343,23 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
sta->sae->peer_commit_scalar = NULL;
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmk_len,
- sta->sae->pmkid, sta->sae->akmp);
+ sta->sae->pmkid, sta->sae->akmp,
+ ap_sta_is_mld(hapd, sta), sta->vlan_id);
sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+ if (sta->sae->tmp) {
+ struct sae_temporary_data *tmp = sta->sae->tmp;
+
+ wpabuf_free(sta->sae_pw_id);
+ sta->sae_pw_id = NULL;
+ if (tmp->dec_pw_id) {
+ sta->sae_pw_id = wpabuf_alloc_copy(
+ tmp->dec_pw_id, tmp->dec_pw_id_len);
+ sta->sae_pw_id_counter = tmp->pw_id_counter;
+ } else if (tmp->pw_id) {
+ sta->sae_pw_id = wpabuf_alloc_copy(
+ tmp->pw_id, tmp->pw_id_len);
+ }
+ }
}
@@ -985,14 +1371,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
*sta_removed = 0;
- if (auth_transaction != 1 && auth_transaction != 2)
+ if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT &&
+ auth_transaction != WLAN_AUTH_TR_SEQ_SAE_CONFIRM)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
auth_transaction);
- if (auth_transaction == 1 && sae_proto_instance_disabled(sta)) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT &&
+ sae_proto_instance_disabled(sta)) {
wpa_printf(MSG_DEBUG,
"SAE: Protocol instance temporarily disabled - discard received SAE commit");
return WLAN_STATUS_SUCCESS;
@@ -1000,8 +1388,11 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
switch (sta->sae->state) {
case SAE_NOTHING:
- if (auth_transaction == 1) {
- if (sta->sae->tmp) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
+ struct sae_temporary_data *tmp = sta->sae->tmp;
+ bool immediate_confirm;
+
+ if (tmp) {
sta->sae->h2e =
(status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
@@ -1011,8 +1402,23 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
}
ret = auth_sae_send_commit(hapd, sta,
!allow_reuse, status_code);
+ if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER)
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+ MACSTR, MAC2STR(sta->addr));
if (ret)
return ret;
+
+ if (tmp && tmp->parsed_pw_id && !tmp->pw_id) {
+ tmp->pw_id = tmp->parsed_pw_id;
+ tmp->pw_id_len = tmp->parsed_pw_id_len;
+ tmp->parsed_pw_id = NULL;
+ tmp->parsed_pw_id_len = 0;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "SAE: Known Password Identifier bound to this STA",
+ tmp->pw_id, tmp->pw_id_len);
+ }
+
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0)
@@ -1028,8 +1434,22 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
* overridden with explicit configuration so that the
* infrastructure BSS case sends both frames together.
*/
- if ((hapd->conf->mesh & MESH_ENABLED) ||
- hapd->conf->sae_confirm_immediate) {
+ immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) ||
+ hapd->conf->sae_confirm_immediate;
+
+ /* If sae_track_password is enabled and the STA has not
+ * yet been tracked to having successfully completed
+ * SAE authentication with the password that the AP
+ * tries to use, do not send Confirm immediately to
+ * avoid an explicit indication on the STA side on
+ * password mismatch. */
+ if (immediate_confirm &&
+ hapd->conf->sae_track_password &&
+ (!sta->sae->tmp || !sta->sae->tmp->parsed_pw_id) &&
+ !has_sae_success_seen(hapd, sta))
+ immediate_confirm = false;
+
+ if (immediate_confirm) {
/*
* Send both Commit and Confirm immediately
* based on SAE finite state machine
@@ -1061,7 +1481,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
break;
case SAE_COMMITTED:
sae_clear_retransmit_timer(hapd, sta);
- if (auth_transaction == 1) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1108,7 +1528,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
break;
case SAE_CONFIRMED:
sae_clear_retransmit_timer(hapd, sta);
- if (auth_transaction == 1) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -1131,7 +1551,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
}
break;
case SAE_ACCEPTED:
- if (auth_transaction == 1 &&
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT &&
(hapd->conf->mesh & MESH_ENABLED)) {
wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
") doing reauthentication",
@@ -1139,7 +1559,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
ap_free_sta(hapd, sta);
*sta_removed = 1;
- } else if (auth_transaction == 1) {
+ } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
ret = auth_sae_send_commit(hapd, sta, 1, status_code);
if (ret)
@@ -1173,16 +1593,23 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
{
struct sae_data *sae = sta->sae;
- int i, *groups = hapd->conf->sae_groups;
- int default_groups[] = { 19, 0 };
+ struct hostapd_bss_config *conf = hapd->conf;
+ int i, *groups = conf->sae_groups;
+ int default_groups[] = { 19, 0, 0 };
if (sae->state != SAE_COMMITTED)
return;
wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
- if (!groups)
+ if (!groups) {
groups = default_groups;
+ if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
+ default_groups[1] = 20;
+ }
+
for (i = 0; groups[i] > 0; i++) {
if (sae->group == groups[i])
break;
@@ -1247,12 +1674,18 @@ static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
{
- int *groups = hapd->conf->sae_groups;
- int default_groups[] = { 19, 0 };
+ struct hostapd_bss_config *conf = hapd->conf;
+ int *groups = conf->sae_groups;
+ int default_groups[] = { 19, 0, 0 };
int i;
- if (!groups)
+ if (!groups) {
groups = default_groups;
+ if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
+ default_groups[1] = 20;
+ }
for (i = 0; groups[i] > 0; i++) {
if (groups[i] == group)
@@ -1309,17 +1742,24 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
{
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
- int *groups = hapd->conf->sae_groups;
- int default_groups[] = { 19, 0 };
+ struct hostapd_bss_config *conf = hapd->conf;
+ int *groups = conf->sae_groups;
+ int default_groups[] = { 19, 0, 0 };
const u8 *pos, *end;
int sta_removed = 0;
bool success_status;
- if (!groups)
+ if (!groups) {
groups = default_groups;
+ if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
+ default_groups[1] = 20;
+ }
#ifdef CONFIG_TESTING_OPTIONS
- if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
+ if (hapd->conf->sae_reflection_attack &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
@@ -1331,7 +1771,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
goto remove_sta;
}
- if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+ if (hapd->conf->sae_commit_override &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE,
@@ -1343,7 +1784,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!sta->sae) {
- if (auth_transaction != 1 ||
+ if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT ||
!sae_status_success(hapd, status_code)) {
wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
status_code);
@@ -1355,6 +1796,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
resp = -1;
goto remove_sta;
}
+ if (!hostapd_sae_pw_id_in_use(hapd->conf))
+ sta->sae->no_pw_id = 1;
sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
@@ -1366,7 +1809,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
sta->mesh_sae_pmksa_caching = 0;
}
- if (auth_transaction == 1) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
const u8 *token = NULL;
size_t token_len = 0;
int allow_reuse = 0;
@@ -1456,7 +1899,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
* previously set parameters. */
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
- if (end - pos >= (int) sizeof(le16) &&
+ if ((!sta->sae->tmp ||
+ !sta->sae->tmp->try_other_password) &&
+ end - pos >= (int) sizeof(le16) &&
sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos)) ==
WLAN_STATUS_SUCCESS) {
@@ -1493,6 +1938,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
sae_clear_retransmit_timer(hapd, sta);
sae_set_state(sta, SAE_NOTHING,
"Unknown Password Identifier");
+ if (sta->sae->state == SAE_NOTHING)
+ goto reply;
goto remove_sta;
}
@@ -1544,7 +1991,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
resp = sae_sm_step(hapd, sta, auth_transaction,
status_code, allow_reuse, &sta_removed);
- } else if (auth_transaction == 2) {
+ } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"SAE authentication (RX confirm, status=%u (%s))",
@@ -1580,9 +2027,21 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
if (sae_check_confirm(sta->sae, var, var_len,
NULL) < 0) {
+ if (sae_password_track_fail(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Reject mismatching Confirm so that another password can be attempted by "
+ MACSTR,
+ MAC2STR(sta->addr));
+ if (sta->sae->tmp)
+ sta->sae->tmp->
+ try_other_password = 1;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
resp = WLAN_STATUS_CHALLENGE_FAIL;
goto reply;
}
+ sae_password_track_success(hapd, sta);
sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, auth_transaction,
@@ -1609,24 +2068,16 @@ reply:
!data && end - pos >= 2)
data = wpabuf_alloc_copy(pos, 2);
- sae_sme_send_external_auth_status(hapd, sta, resp);
send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-sae");
- if (sta->sae && sta->sae->tmp && sta->sae->tmp->pw_id &&
- resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER &&
- auth_transaction == 1) {
- wpa_printf(MSG_DEBUG,
- "SAE: Clear stored password identifier since this SAE commit was not accepted");
- os_free(sta->sae->tmp->pw_id);
- sta->sae->tmp->pw_id = NULL;
- }
+ sae_sme_send_external_auth_status(hapd, sta, resp);
}
remove_sta:
- if (auth_transaction == 1)
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT)
success_status = sae_status_success(hapd, status_code);
else
success_status = status_code == WLAN_STATUS_SUCCESS;
@@ -1778,34 +2229,650 @@ static u16 wpa_res_to_status_code(enum wpa_validate_result res)
case WPA_IE_OK:
return WLAN_STATUS_SUCCESS;
case WPA_INVALID_IE:
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
case WPA_INVALID_GROUP:
- return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ return WLAN_STATUS_INVALID_GROUP_CIPHER;
case WPA_INVALID_PAIRWISE:
- return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ return WLAN_STATUS_INVALID_PAIRWISE_CIPHER;
case WPA_INVALID_AKMP:
- return WLAN_STATUS_AKMP_NOT_VALID;
+ return WLAN_STATUS_INVALID_AKMP;
case WPA_NOT_ENABLED:
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
case WPA_ALLOC_FAIL:
return WLAN_STATUS_UNSPECIFIED_FAILURE;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
case WPA_INVALID_MGMT_GROUP_CIPHER:
- return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ return WLAN_STATUS_CIPHER_OUT_OF_POLICY;
case WPA_INVALID_MDIE:
- return WLAN_STATUS_INVALID_MDIE;
+ return WLAN_STATUS_INVALID_MDE;
case WPA_INVALID_PROTO:
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
case WPA_INVALID_PMKID:
return WLAN_STATUS_INVALID_PMKID;
case WPA_DENIED_OTHER_REASON:
return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
+}
+
+
+#ifdef CONFIG_IEEE8021X_AUTH
+
+static struct wpabuf *
+prepare_802_1x_auth_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 auth_transaction, u16 status,
+ struct rsn_pmksa_cache_entry *cached_pmk,
+ const u8 *eap_req, size_t eap_req_len)
+{
+ struct wpabuf *pub = NULL, *data;
+ bool enc_assoc = ap_sta_support_enc_assoc(hapd,
+ sta->eap_auth_data.rsnxe,
+ sta->eap_auth_data.rsnxe_len);
+
+ data = wpabuf_alloc(1000 + eap_req_len);
+ if (!data) {
+ wpa_printf(MSG_INFO,
+ "Authentication frame buffer allocation failed");
+ return NULL;
+ }
+
+ /* Encapsulation Length field */
+ wpabuf_put_le16(data, eap_req_len);
+ /* Encapsulation field */
+ wpabuf_put_data(data, eap_req, eap_req_len);
+
+ if (status != WLAN_STATUS_SUCCESS &&
+ status != WLAN_STATUS_802_1_X_AUTH_SUCCESS)
+ goto reply;
+
+ /* Authentication frames with transaction sequence greater than or
+ * equal to 3 contain Authentication fields only.
+ */
+ if (auth_transaction == 2) {
+ /* Per IEEE 802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X), a responder
+ * that sets
+ * dot11EPPReAssociationFrameEncryptionSupportActivated
+ * to false or does not receive the RSNXE in the first
+ * Authentication frame with the (Re)Association Frame
+ * Encryption Support field set to 1 shall not include
+ * a Diffie-Hellman Parameter element nor a Nonce element
+ * nor an RSNE in the second Authentication frame for
+ * IEEE 802.1X authentication.
+ */
+ if (enc_assoc) {
+ u8 a_nonce[WPA_NONCE_LEN];
+ struct hostapd_bss_config *conf = hapd->conf;
+ int res;
+
+ /* Derive own public key */
+ if (sta->eap_auth_data.ecdh) {
+ pub = crypto_ecdh_get_pubkey(
+ sta->eap_auth_data.ecdh, 1);
+ if (!pub) {
+ status =
+ WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ }
+
+ /* ANonce generation */
+ if (random_get_bytes(a_nonce, WPA_NONCE_LEN) < 0) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ os_memcpy(sta->eap_auth_data.anonce, a_nonce,
+ WPA_NONCE_LEN);
+
+
+ if (pub && wpabuf_resize(&data, wpabuf_len(pub)) < 0) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+
+ /* Per IEEE 802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X),
+ * responder shall include an RSNE with the AKM and
+ * pairwise cipher suite as indicated in the first
+ * Authentication frame.
+ */
+ res = wpa_write_802_1x_rsne(
+ hapd->wpa_auth,
+ wpabuf_mhead_u8(data) + wpabuf_len(data),
+ (wpabuf_size(data) - wpabuf_len(data)),
+ cached_pmk ? cached_pmk->pmkid : NULL,
+ sta->eap_auth_data.akm,
+ sta->eap_auth_data.cipher,
+ conf->wpa_group,
+ conf->group_mgmt_cipher,
+ conf->ieee80211w);
+ if (res < 0) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ wpabuf_put(data, res);
+
+ /* DH Parameter element */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(data, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(data, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(data, sta->eap_auth_data.group);
+ wpabuf_put_buf(data, pub);
+
+ /* ANonce in Nonce element */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(data, 1 + WPA_NONCE_LEN);
+ wpabuf_put_u8(data, WLAN_EID_EXT_NONCE);
+ wpabuf_put_data(data, a_nonce, WPA_NONCE_LEN);
+ } else {
+ /* Per IEEE 802.11bi/D4.0, 12.16.5 (IEEE 802.1X
+ * authentication utilizing Authentication frames), the
+ * Responser shall construct the second Authentication
+ * frame with an AKM Suite Selector element indicating
+ * the same IEEE 802.1X AKM indicated in the first
+ * Authentication frame.
+ */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(data, 1 + RSN_SELECTOR_LEN);
+ wpabuf_put_u8(data, WLAN_EID_EXT_AKM_SUITE_SELECTOR);
+ RSN_SELECTOR_PUT(wpabuf_put(data, RSN_SELECTOR_LEN),
+ wpa_akm_to_suite(
+ sta->eap_auth_data.akm));
+ }
+ } /* if (auth_transaction == 2) */
+reply:
+ wpabuf_free(pub);
+ return data;
}
+u16 wpa_auth_validate_802_1x_frame(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ struct wpa_ie_data rsn;
+ const int default_groups[] = { 19, 0 };
+ bool enc_assoc = ap_sta_support_enc_assoc(hapd,
+ elems->rsnxe,
+ elems->rsnxe_len);
+
+ /* Per IEEE P802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X), an originator that
+ * sets dot11EPPReAssociationFrameEncryptionSupportActivated to false or
+ * does not receive the RSNXE from the responder with the
+ * (Re)Association Frame Encryption Support field set to 1 shall not
+ * include a Diffie-Hellman Parameter element nor an RSNE nor an RSNXE
+ * nor a Nonce element in the first Authentication frame for IEEE 802.1X
+ * authentication.
+ */
+ if (!enc_assoc &&
+ (elems->rsn_ie || elems->nonce || elems->owe_dh)) {
+ wpa_printf(MSG_INFO,
+ "Invalid inclusion of RSNE/Nonce/DHE when (Re)Association frame encryption is not supported");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (enc_assoc &&
+ (!elems->rsn_ie || !elems->nonce ||
+ elems->nonce_len != WPA_NONCE_LEN || !elems->owe_dh)) {
+ wpa_printf(MSG_ERROR, "Missing RSNE/DHIE/Nonce");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* Both RSNE and AKM Suite Selector element shall not be present at the
+ * same time. */
+ if (elems->rsn_ie && elems->akm_suite_selector) {
+ wpa_printf(MSG_INFO,
+ "Incorrect inclusion of both RSNE and AKM Suite Selector element");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (enc_assoc &&
+ (!elems->rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems->rsn_ie - 2, elems->rsn_ie_len + 2,
+ &rsn) < 0)) {
+ wpa_printf(MSG_INFO, "No valid RSNE");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (enc_assoc && elems->rsn_ie) {
+ if (!(rsn.pairwise_cipher & hapd->conf->rsn_pairwise)) {
+ wpa_printf(MSG_INFO,
+ "Invalid pairwise cipher (0x%x) in RSNE",
+ rsn.pairwise_cipher);
+ return WPA_INVALID_PAIRWISE;
+ }
+ sta->eap_auth_data.cipher = rsn.pairwise_cipher;
+ wpa_printf(MSG_DEBUG, "Received pairwise cipher (0x%x) in RSNE",
+ rsn.pairwise_cipher);
+
+ if (!(rsn.key_mgmt & hapd->conf->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO, "Invalid key mgmt (0x%x) in RSNE",
+ rsn.key_mgmt);
+ return WPA_INVALID_AKMP;
+ }
+ sta->eap_auth_data.akm = rsn.key_mgmt;
+ wpa_printf(MSG_DEBUG, "Received keymgmt (0x%x) in RSNE",
+ rsn.key_mgmt);
+ }
+
+ /* Validate AKM Suite Selector element */
+ if (elems->akm_suite_selector) {
+ sta->eap_auth_data.akm = rsn_key_mgmt_to_wpa_akm(
+ RSN_SELECTOR_GET(elems->akm_suite_selector));
+ if (!(sta->eap_auth_data.akm & hapd->conf->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO,
+ "Invalid key mgmt (0x%x) in AKM Suite Selector element",
+ sta->eap_auth_data.akm);
+ return WPA_INVALID_AKMP;
+ }
+ wpa_printf(MSG_DEBUG,
+ "Received keymgmt (0x%x) in AKM Suite Selector element",
+ sta->eap_auth_data.akm);
+ }
+
+ if (elems->rsnxe) {
+ os_free(sta->eap_auth_data.rsnxe);
+ sta->eap_auth_data.rsnxe =
+ os_memdup(elems->rsnxe, elems->rsnxe_len);
+ sta->eap_auth_data.rsnxe_len = elems->rsnxe_len;
+ }
+
+ /* Store SNonce */
+ if (elems->nonce && elems->nonce_len == WPA_NONCE_LEN) {
+ os_memcpy(sta->eap_auth_data.snonce, elems->nonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "SNonce", elems->nonce, WPA_NONCE_LEN);
+ }
+
+ /* Validate DH Parameter element */
+ if (elems->owe_dh) {
+ u16 group;
+ u8 pubkey_len;
+ const u8 *pubkey;
+ struct wpabuf *secret;
+
+ group = WPA_GET_LE16(elems->owe_dh);
+ if (!int_array_includes(default_groups, group)) {
+ wpa_printf(MSG_INFO,
+ "Received unsupported group value %u",
+ group);
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+ sta->eap_auth_data.group = group;
+ pubkey = elems->owe_dh + 2;
+ pubkey_len = elems->owe_dh_len - 2;
+
+ /* TODO: Any more validation of peer public key needed? */
+ if (!pubkey_len) {
+ wpa_printf(MSG_INFO, "Missing DH public key");
+ return WLAN_STATUS_INVALID_PUBLIC_KEY;
+ }
+
+ /* Setup ECDH context */
+ crypto_ecdh_deinit(sta->eap_auth_data.ecdh);
+ sta->eap_auth_data.ecdh = crypto_ecdh_init(group);
+ if (!sta->eap_auth_data.ecdh) {
+ wpa_printf(MSG_INFO, "Failed to setup ECDH context");
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+
+ /* Generate shared secret */
+ wpabuf_clear_free(sta->eap_auth_data.dhss);
+ sta->eap_auth_data.dhss = NULL;
+ secret = crypto_ecdh_set_peerkey(sta->eap_auth_data.ecdh, 0,
+ pubkey, pubkey_len);
+ if (!secret) {
+ wpa_printf(MSG_INFO, "Invalid peer public key");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DH shared secret", secret);
+ sta->eap_auth_data.dhss = secret;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+/**
+ * ieee80211_send_eap_req - Callback function to send EAP-Request message in an
+ * Authentication frame
+ *
+ * This function is called from ieee802_1x_eapol_send() using the
+ * hapd->send_eap_req callback. Its main purpose is to prepend the EAP-Request
+ * data with an IEEE 802.1X header and call prepare_802_1x_auth_resp() to send
+ * out the next IEEE 802.1X Authentication frame to the station. If this is an
+ * EAP-Success frame, it also fetches the MSK derived from the successful EAP
+ * authentication to derive PMK and PTK and configure the TK to the driver.
+ */
+void ieee80211_send_eap_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 type, u16 auth_transaction, u16 status,
+ struct rsn_pmksa_cache_entry *cached_pmk,
+ const u8 *eap_req, size_t eap_req_len)
+{
+ bool enc_assoc = ap_sta_support_enc_assoc(hapd,
+ sta->eap_auth_data.rsnxe,
+ sta->eap_auth_data.rsnxe_len);
+ struct ieee802_1x_hdr *xhdr;
+ u8 *data;
+ size_t len = eap_req_len + sizeof(struct ieee802_1x_hdr);
+ struct wpabuf *reply;
+
+ wpa_printf(MSG_DEBUG,
+ "Process EAP-Request data for TX using an Authentication frame");
+
+ data = os_malloc(len);
+ if (!data) {
+ wpa_printf(MSG_ERROR, "malloc() failed for %s", __func__);
+ return;
+ }
+
+ xhdr = (struct ieee802_1x_hdr *) data;
+ xhdr->version = hapd->conf->eapol_version;
+ xhdr->type = type;
+ xhdr->length = host_to_be16(eap_req_len);
+
+ if (eap_req && eap_req_len > 0)
+ os_memcpy(xhdr + 1, eap_req, eap_req_len);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-Request", eap_req, eap_req_len);
+
+ /* EAP-Success */
+ if (enc_assoc && eap_req_len > 0 && eap_req[0] == 3) {
+ u8 msk[2 * PMK_LEN] = { 0 };
+ size_t _len = 2 * PMK_LEN;
+ size_t pmk_len, kdk_len;
+ bool is_ml = ap_sta_is_mld(hapd, sta);
+ enum wpa_alg alg =
+ wpa_cipher_to_alg(sta->eap_auth_data.cipher);
+ size_t key_len =
+ wpa_cipher_key_len(sta->eap_auth_data.cipher);
+ const u8 *aa = hapd->own_addr;
+ struct rsn_pmksa_cache *pmksa =
+ wpa_auth_get_pmksa_cache(hapd->wpa_auth, is_ml);
+ struct rsn_pmksa_cache_entry *entry;
+#ifdef CONFIG_TESTING_OPTIONS
+ bool force_kdk = hapd->conf->force_kdk_derivation;
+#else /* CONFIG_TESTING_OPTIONS */
+ bool force_kdk = false;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+ if (is_ml)
+ aa = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ /* Per IEEE 802.11bi/D4.0, 12.16.5 (IEEE 802.1X authentication
+ * utilizing Authentication frames), if the IEEE 802.1X
+ * authentication is successful, the Status Code field
+ * is set to 802_1_X_AUTH_SUCCESS. */
+ status = WLAN_STATUS_802_1_X_AUTH_SUCCESS;
+ /* TODO: If the IEEE 802.1X authentication fails,
+ * the status code is set to 802_1_X_AUTH_FAILED. */
+ os_memset(&sta->eap_auth_data.ptk, 0, sizeof(struct wpa_ptk));
+ if (wpa_auth_802_1x_get_msk(hapd->wpa_auth, sta->addr,
+ msk, &_len)) {
+ wpa_printf(MSG_INFO, "Failed to get MSK");
+ os_free(data);
+ return;
+ }
+
+ if (wpa_key_mgmt_sha384(sta->eap_auth_data.akm))
+ pmk_len = PMK_LEN_SUITE_B_192;
+ else
+ pmk_len = PMK_LEN;
+
+ sta->eap_auth_data.pmk_len = pmk_len;
+ os_memcpy(sta->eap_auth_data.pmk, msk, pmk_len);
+
+ if (force_kdk ||
+ (wpa_auth_ap_support_secure_ltf(hapd->wpa_auth) &&
+ ieee802_11_rsnx_capab(sta->eap_auth_data.rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF)))
+ kdk_len = WPA_KDK_MAX_LEN;
+ else
+ kdk_len = 0;
+ if (wpa_auth_802_1x_pmk_to_ptk(
+ msk, sta->eap_auth_data.pmk_len,
+ sta->addr, aa,
+ sta->eap_auth_data.snonce,
+ sta->eap_auth_data.anonce,
+ sta->eap_auth_data.akm,
+ sta->eap_auth_data.cipher,
+ wpabuf_head_u8(sta->eap_auth_data.dhss),
+ wpabuf_len(sta->eap_auth_data.dhss),
+ &sta->eap_auth_data.ptk, kdk_len)) {
+ wpa_printf(MSG_INFO, "Failed to derive the PTK");
+ os_free(data);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "PTK derived successfully");
+
+ if (wpa_auth_802_1x_set_key(hapd->wpa_auth,
+ alg, sta->addr,
+ sta->eap_auth_data.ptk.tk,
+ key_len)) {
+ wpa_printf(MSG_INFO,
+ "Failed to set the TK to the driver");
+ os_free(data);
+ return;
+ }
+
+ /* Delete DHss after successful PTK derivation */
+ wpabuf_clear_free(sta->eap_auth_data.dhss);
+ sta->eap_auth_data.dhss = NULL;
+
+ /* TODO: Fill session_timeout? */
+ wpa_hexdump_key(MSG_DEBUG, "IEEE802.1X: Cache PMK",
+ msk, pmk_len);
+
+ entry = pmksa_cache_auth_add(pmksa, msk, pmk_len, NULL,
+ sta->eap_auth_data.ptk.kck,
+ sta->eap_auth_data.ptk.kck_len,
+ aa, sta->addr, 0, sta->eapol_sm,
+ sta->eap_auth_data.akm);
+ if (!entry) {
+ wpa_printf(MSG_INFO, "Failed to add PMKSA entry");
+ return;
+ }
+ os_memcpy(sta->eap_auth_data.epp_pmkid_cur, entry->pmkid,
+ PMKID_LEN);
+ }
+
+ reply = prepare_802_1x_auth_resp(hapd, sta, auth_transaction, status,
+ cached_pmk, data, len);
+ if (reply)
+ send_8021x_auth_reply(hapd, sta, auth_transaction, status,
+ reply);
+ os_free(data);
+}
+
+
+static void handle_auth_802_1x(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction)
+{
+ struct ieee802_1x_hdr *eapol_pdu;
+ u16 encap_len, resp = WLAN_STATUS_SUCCESS;
+ const u8 *end;
+ struct wpabuf *reply;
+
+ if (len < 2) {
+ wpa_printf(MSG_INFO, "Missing Encapsulation Length field");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ end = pos + len;
+ encap_len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (encap_len > end - pos) {
+ wpa_printf(MSG_INFO, "Truncated Encapsulation field");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* Start of Encapsulation field */
+ eapol_pdu = (struct ieee802_1x_hdr *) pos;
+
+ if (auth_transaction == 1 &&
+ eapol_pdu->type != IEEE802_1X_TYPE_EAPOL_START) {
+ wpa_printf(MSG_INFO,
+ "Received unexpected EAPOL PDU type %u in the first Authentication frame",
+ eapol_pdu->type);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ pos += encap_len;
+ sta->eap_auth_data.auth_transaction = auth_transaction;
+
+ /* Process Authentication frame elements
+ * Authentication frames with transaction sequence greater
+ * than or equal to 3 contain Authentication fields only.
+ */
+ if (auth_transaction == 1) {
+ struct wpa_ie_data data;
+ struct ieee802_11_elems elems;
+ struct rsn_pmksa_cache_entry *cached_pmk = NULL;
+ bool is_ml = ap_sta_is_mld(hapd, sta);
+ bool enc_assoc, pmkid_privacy;
+ size_t i;
+
+ if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_INFO, "Could not parse elements");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ resp = wpa_auth_validate_802_1x_frame(hapd, sta, &elems);
+ if (resp)
+ goto fail;
+
+ enc_assoc = ap_sta_support_enc_assoc(hapd, elems.rsnxe,
+ elems.rsnxe_len);
+
+ pmkid_privacy = hapd->conf->pmksa_caching_privacy &&
+ ieee802_11_rsnx_capab_len(
+ elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY);
+
+ os_memset(&data, 0, sizeof(data));
+ if (enc_assoc &&
+ (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2,
+ elems.rsn_ie_len + 2, &data) < 0)) {
+ wpa_printf(MSG_INFO, "No valid RSNE");
+ goto fail;
+ }
+
+ for (i = 0; i < data.num_pmkid; i++) {
+ const u8 *aa;
+ enum wpa_alg alg;
+ size_t key_len, kdk_len;
+#ifdef CONFIG_TESTING_OPTIONS
+ bool force_kdk = hapd->conf->force_kdk_derivation;
+#else /* CONFIG_TESTING_OPTIONS */
+ bool force_kdk = false;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump(MSG_DEBUG, "RSNE: STA PMKID",
+ &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+
+ cached_pmk = pmksa_cache_search(
+ hapd, pmkid_privacy ? NULL : sta->addr,
+ &data.pmkid[i * PMKID_LEN], is_ml);
+ if (!cached_pmk)
+ continue;
+
+ aa = hapd->own_addr;
+ alg = wpa_cipher_to_alg(sta->eap_auth_data.cipher);
+ key_len = wpa_cipher_key_len(sta->eap_auth_data.cipher);
+
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ aa = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+ wpa_printf(MSG_DEBUG,
+ "Found a matching PMKSA cache entry");
+ os_memcpy(sta->eap_auth_data.epp_pmkid_cur,
+ cached_pmk->pmkid, PMKID_LEN);
+ reply = prepare_802_1x_auth_resp(
+ hapd, sta, auth_transaction + 1,
+ WLAN_STATUS_SUCCESS, cached_pmk, NULL, 0);
+ if (!reply) {
+ wpa_printf(MSG_INFO,
+ "Failed to prepare IEEE 802.1X Authentication frame");
+ return;
+ }
+
+ if (force_kdk ||
+ (wpa_auth_ap_support_secure_ltf(hapd->wpa_auth) &&
+ ieee802_11_rsnx_capab(sta->eap_auth_data.rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF)))
+ kdk_len = WPA_KDK_MAX_LEN;
+ else
+ kdk_len = 0;
+
+ if (wpa_auth_802_1x_pmk_to_ptk(
+ cached_pmk->pmk, cached_pmk->pmk_len,
+ sta->addr, aa,
+ sta->eap_auth_data.snonce,
+ sta->eap_auth_data.anonce,
+ sta->eap_auth_data.akm,
+ sta->eap_auth_data.cipher,
+ wpabuf_head_u8(sta->eap_auth_data.dhss),
+ wpabuf_len(sta->eap_auth_data.dhss),
+ &sta->eap_auth_data.ptk, kdk_len)) {
+ wpa_printf(MSG_INFO, "Failed to derive PTK");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "PTK derived successfully");
+
+ if (wpa_auth_802_1x_set_key(hapd->wpa_auth, alg,
+ sta->addr,
+ sta->eap_auth_data.ptk.tk,
+ key_len)) {
+ wpa_printf(MSG_INFO,
+ "Failed to set TK to driver");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ sta->flags |= WLAN_STA_AUTH;
+ sta->auth_alg = WLAN_AUTH_802_1X;
+ sta->eap_auth_data.add_mic = true;
+ send_8021x_auth_reply(hapd, sta, auth_transaction + 1,
+ WLAN_STATUS_SUCCESS, reply);
+ /* Delete DHss after successful PTK derivation */
+ wpabuf_clear_free(sta->eap_auth_data.dhss);
+ sta->eap_auth_data.dhss = NULL;
+ return;
+ }
+
+ /* Start EAPOL SM to process EAPOL PDU */
+ if (!sta->eapol_sm) {
+ sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+ if (!sta->eapol_sm)
+ return;
+ }
+
+ ieee802_1x_eapol_sm_set_port_enabled(sta->eapol_sm, true);
+ }
+
+ /* Forward the extracted EAP PDU to AS */
+ ieee802_1x_receive(hapd, sta->addr, (const u8 *) eapol_pdu,
+ encap_len, FRAME_NOT_ENCRYPTED);
+ return;
+
+fail:
+ reply = prepare_802_1x_auth_resp(hapd, sta, auth_transaction + 1,
+ resp, NULL, NULL, 0);
+ if (reply)
+ send_8021x_auth_reply(hapd, sta, auth_transaction + 1, resp,
+ reply);
+}
+
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+
#ifdef CONFIG_FILS
static void handle_auth_fils_finish(struct hostapd_data *hapd,
@@ -1826,7 +2893,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
struct wpa_ie_data rsn;
struct rsn_pmksa_cache_entry *pmksa = NULL;
- if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+ if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT ||
+ status_code != WLAN_STATUS_SUCCESS)
return;
end = pos + len;
@@ -1935,24 +3003,26 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
goto fail;
}
+ wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection,
+ elems.rsn_selection_len);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
- elems.mdie, elems.mdie_len, NULL, 0, NULL);
+ elems.mdie, elems.mdie_len, NULL, 0, NULL,
+ ap_sta_is_mld(hapd, sta));
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
- if (!elems.fils_nonce) {
+ if (!elems.nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
- FILS_NONCE_LEN);
- os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.nonce, NONCE_LEN);
+ os_memcpy(sta->fils_snonce, elems.nonce, NONCE_LEN);
/* PMKID List */
if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -2060,7 +3130,7 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
const u8 *msk, size_t msk_len,
int *is_pub)
{
- u8 fils_nonce[FILS_NONCE_LEN];
+ u8 fils_nonce[NONCE_LEN];
size_t ielen;
struct wpabuf *data = NULL;
const u8 *ie;
@@ -2095,12 +3165,12 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
ie = ie_buf;
}
- if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+ if (random_get_bytes(fils_nonce, NONCE_LEN) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
- fils_nonce, FILS_NONCE_LEN);
+ fils_nonce, NONCE_LEN);
#ifdef CONFIG_FILS_SK_PFS
if (sta->fils_dh_ss && sta->fils_ecdh) {
@@ -2152,10 +3222,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
/* FILS Nonce */
wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
- wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+ wpabuf_put_u8(data, 1 + NONCE_LEN); /* Length */
/* Element ID Extension */
- wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
- wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+ wpabuf_put_u8(data, WLAN_EID_EXT_NONCE);
+ wpabuf_put_data(data, fils_nonce, NONCE_LEN);
/* FILS Session */
wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
@@ -2217,7 +3287,7 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
sta->fils_erp_pmkid,
session_timeout,
wpa_auth_sta_key_mgmt(sta->wpa_sm),
- NULL) < 0) {
+ NULL, ap_sta_is_mld(hapd, sta)) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
}
@@ -2441,13 +3511,13 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
goto fail;
}
- if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
+ if (random_get_bytes(fils->anonce, NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
- fils->anonce, FILS_NONCE_LEN);
+ fils->anonce, NONCE_LEN);
ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce,
fils->anonce, NULL, 0, pmk, &pmk_len);
@@ -2460,7 +3530,9 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
wpabuf_head(pasn->secret),
wpabuf_len(pasn->secret),
pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
- pasn_get_cipher(sta->pasn), sta->pasn->kdk_len);
+ pasn_get_cipher(sta->pasn), sta->pasn->kdk_len,
+ sta->pasn->kek_len, &sta->pasn->hash_alg,
+ pasn->auth_alg == WLAN_AUTH_EPPKE);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
goto fail;
@@ -2484,6 +3556,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
fils->erp_resp = erp_resp;
ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
WLAN_STATUS_SUCCESS);
+ wpabuf_free(pasn->frame);
+ pasn->frame = NULL;
fils->erp_resp = NULL;
if (ret) {
@@ -2556,7 +3630,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
return -1;
}
- if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+ if (!elems.rsn_ie || !elems.nonce || !elems.nonce ||
!elems.wrapped_data || !elems.fils_session) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
return -1;
@@ -2569,7 +3643,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
return -1;
}
- ret = wpa_pasn_validate_rsne(&rsne_data);
+ ret = wpa_pasn_validate_rsne(&rsne_data, false);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
return -1;
@@ -2581,9 +3655,8 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
return -1;
}
- wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
- FILS_NONCE_LEN);
- os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.nonce, NONCE_LEN);
+ os_memcpy(fils->nonce, elems.nonce, NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
FILS_SESSION_LEN);
@@ -2635,35 +3708,243 @@ static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
}
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_search(void *ctx, const u8 *spa, const u8 *pmkid, bool is_ml)
+{
+ struct hostapd_data *hapd = ctx;
+ struct rsn_pmksa_cache_entry *entry;
+ struct rsn_pmksa_cache *pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth,
+ is_ml);
+
+ entry = pmksa_cache_auth_get(pmksa, spa, pmkid);
+ if (entry)
+ return entry;
+
+#ifdef CONFIG_IEEE80211BE
+ if (is_ml) {
+ struct hostapd_data *tmp_hapd;
+
+ /* Search in link caches of each affiliated AP MLD link */
+ for_each_mld_link(tmp_hapd, hapd) {
+ pmksa = wpa_auth_get_pmksa_cache(tmp_hapd->wpa_auth,
+ false);
+ entry = pmksa_cache_auth_get(pmksa, spa, pmkid);
+ if (entry)
+ return entry;
+ }
+ } else if (hapd->conf->mld_ap) {
+ /* Search in the MLD cache */
+ pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth, true);
+ entry = pmksa_cache_auth_get(pmksa, spa, pmkid);
+ if (entry)
+ return entry;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_ENC_ASSOC
+static int eppke_set_key(void *ctx, enum wpa_alg alg, const u8 *addr,
+ int vlan_id, const u8 *key, size_t key_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr,
+ 0, vlan_id, 1, NULL, 0, key, key_len,
+ KEY_FLAG_PAIRWISE_RX_TX);
+}
+#else /* CONFIG_ENC_ASSOC */
+#define eppke_set_key NULL
+#endif /* CONFIG_ENC_ASSOC */
+
+
+#ifdef CONFIG_SAE
+/**
+ * hapd_pasn_get_pt_for_pw_id - Look up SAE PT for a password identifier
+ *
+ * Called by the PASN responder when an SAE commit frame contains a password
+ * identifier that was not known at PASN-setup time (e.g., for EPPKE where
+ * the PT cannot be pre-selected before the commit is received).
+ *
+ * For plaintext identifiers sae_get_password() returns the pre-computed PT
+ * (pw_entry->pt). We must NOT return that pointer directly because the
+ * caller will free it; clone it so the caller always owns the returned PT.
+ * counter and dec_pw_id are set to 0/NULL for plaintext identifiers.
+ *
+ * For encrypted password identifiers the PT is not pre-computed (the
+ * pre-computed PT is keyed to the decrypted identifier, not the encrypted
+ * one). Derive the PT on-the-fly using the raw (encrypted) identifier as
+ * the salt, mirroring what auth_build_sae_commit() does for regular SAE.
+ * The decrypted blob is parsed to extract the real password identifier
+ * (dec_pw_id) and the counter, both returned to the caller. dec_pw_id is
+ * heap-allocated and the caller takes ownership (must free with os_free()).
+ */
+static struct sae_pt *
+hapd_pasn_get_pt_for_pw_id(void *ctx, const u8 *pw_id, size_t pw_id_len,
+ int group, const char **password,
+ unsigned int *counter,
+ u8 **dec_pw_id, size_t *dec_pw_id_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sae_password_entry *pw_entry = NULL;
+ struct sae_pt *pt = NULL;
+ int groups[2] = { group, 0 };
+
+ *counter = 0;
+ *dec_pw_id = NULL;
+ *dec_pw_id_len = 0;
+
+ *password = sae_get_password(hapd, NULL, pw_id, pw_id_len, &pw_entry,
+ &pt, NULL);
+ if (!*password)
+ return NULL;
+
+ if (pt) {
+ /* Plaintext identifier: sae_get_password() found a
+ * pre-computed PT. Clone it so the caller can free it
+ * without affecting the password entry's own PT.
+ * counter and dec_pw_id stay 0/NULL for plaintext. */
+ return sae_derive_pt(groups, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len,
+ (const u8 *) pw_entry->password,
+ os_strlen(pw_entry->password),
+ pw_id, pw_id_len);
+ }
+
+ if (pw_entry) {
+ /* Encrypted identifier: no pre-computed PT exists for the
+ * raw (encrypted) identifier. Decrypt the blob once to
+ * extract the real password identifier (dec_pw_id) and the
+ * counter, then derive the PT using the encrypted bytes as
+ * the salt (matching auth_build_sae_commit()).
+ *
+ * Decrypted format:
+ * 4-byte date | Password ID | NUL padding | 4-byte counter
+ */
+ if (hapd->conf->sae_pw_id_key &&
+ pw_id_len > 4 + 4 + AES_BLOCK_SIZE) {
+ u8 *plain;
+ size_t plain_len;
+
+ plain = os_malloc(pw_id_len);
+ if (plain &&
+ aes_siv_decrypt(
+ wpabuf_head(hapd->conf->sae_pw_id_key),
+ wpabuf_len(hapd->conf->sae_pw_id_key),
+ pw_id, pw_id_len,
+ 0, NULL, NULL, plain) == 0) {
+ const u8 *id, *pos;
+
+ plain_len = pw_id_len - AES_BLOCK_SIZE;
+ /* Counter is the last 4 bytes */
+ *counter = WPA_GET_BE32(plain + plain_len - 4);
+ wpa_printf(MSG_DEBUG,
+ "SAE: Generation time %u counter %u",
+ WPA_GET_BE32(plain), *counter);
+ /* Real password ID starts at byte 4,
+ * NUL-terminated before the counter */
+ id = plain + 4;
+ pos = id;
+ while (pos < plain + plain_len - 4) {
+ if (*pos == 0x00)
+ break;
+ pos++;
+ }
+ *dec_pw_id_len = pos - id;
+ wpa_hexdump_ascii(
+ MSG_DEBUG,
+ "SAE: Decrypted password identifier",
+ id, *dec_pw_id_len);
+ *dec_pw_id = os_memdup(id, *dec_pw_id_len);
+ if (!*dec_pw_id)
+ *dec_pw_id_len = 0;
+ }
+ os_free(plain);
+ }
+
+ pt = sae_derive_pt(groups, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len,
+ (const u8 *) pw_entry->password,
+ os_strlen(pw_entry->password),
+ pw_id, pw_id_len);
+ if (!pt)
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive PT for encrypted password identifier");
+ return pt;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_SAE */
+
+
static void hapd_initialize_pasn(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct pasn_data *pasn = sta->pasn;
- pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL);
+ pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme,
+ NULL, eppke_set_key, pmksa_cache_search);
pasn_set_bssid(pasn, hapd->own_addr);
pasn_set_own_addr(pasn, hapd->own_addr);
+#ifdef CONFIG_PMKSA_PRIVACY
+ pasn->pmksa_caching_privacy = hapd->conf->pmksa_caching_privacy;
+#endif /* CONFIG_PMKSA_PRIVACY */
+#if defined(CONFIG_IEEE80211BE) && defined(CONFIG_ENC_ASSOC)
+ /* Per IEEE802.11bi/D4.0, 12.16.9 (Enhanced privacy
+ * protection key exchange), if (Re)Association frame
+ * Encryption is activated, KEK in PASN shall be true.
+ */
+ if (hapd->conf->assoc_frame_encryption)
+ pasn->derive_kek = true;
+ if (hapd->conf->mld_ap)
+ pasn_set_own_mld_addr(pasn, hapd->mld->mld_addr);
+#endif /* CONFIG_IEEE80211BE && CONFIG_ENC_ASSOC */
pasn_set_peer_addr(pasn, sta->addr);
- pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt);
- pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise);
- pasn->pasn_groups = hapd->conf->pasn_groups;
+ pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2);
+ pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise |
+ hapd->conf->rsn_override_pairwise);
+ pasn_set_mfp(pasn, hapd->conf->ieee80211w);
+ os_free(pasn->pasn_groups);
+ pasn->pasn_groups = int_array_dup(hapd->conf->pasn_groups);
pasn->noauth = hapd->conf->pasn_noauth;
+#ifdef CONFIG_ENC_ASSOC
+ pasn->eppke_unauth = hapd->conf->eppke_unauth;
+#endif /* CONFIG_ENC_ASSOC */
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
pasn_enable_kdk_derivation(pasn);
#ifdef CONFIG_TESTING_OPTIONS
pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+ pasn->pasn_test_groups = hapd->conf->pasn_test_groups;
if (hapd->conf->force_kdk_derivation)
pasn_enable_kdk_derivation(pasn);
#endif /* CONFIG_TESTING_OPTIONS */
pasn->use_anti_clogging = use_anti_clogging(hapd);
- pasn_set_password(pasn, sae_get_password(hapd, sta, NULL, NULL,
- &pasn->pt, NULL));
- pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+ sae_get_password(hapd, sta, NULL, 0, NULL, &pasn->pt, NULL);
+#ifdef CONFIG_SAE
+ /* Register a callback so the PASN responder can look up the correct
+ * SAE PT when the STA's commit frame contains a password identifier
+ * that was not known at setup time (EPPKE cases).
+ */
+ pasn->get_pt_for_pw_id = hapd_pasn_get_pt_for_pw_id;
+#endif /* CONFIG_SAE */
+ pasn_set_rsne(pasn, wpa_auth_get_wpa_ie(hapd->wpa_auth,
+ &pasn->rsn_ie_len));
pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX));
pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
- pasn_set_responder_pmksa(pasn,
- wpa_auth_get_pmksa_cache(hapd->wpa_auth));
+#ifdef CONFIG_ENC_ASSOC
+ pasn->tk_configured = false;
+#endif /* CONFIG_ENC_ASSOC */
+ pasn_set_responder_pmksa(
+ pasn,
+ wpa_auth_get_pmksa_cache(hapd->wpa_auth,
+ ap_sta_is_epp(sta) ?
+ ap_sta_is_mld(hapd, sta) : false));
pasn->comeback_after = hapd->conf->pasn_comeback_after;
pasn->comeback_idx = hapd->comeback_idx;
@@ -2740,6 +4021,14 @@ static void hapd_pasn_update_params(struct hostapd_data *hapd,
return;
}
+#ifdef CONFIG_ENC_ASSOC
+ pasn->auth_alg = mgmt->u.auth.auth_alg;
+ pasn->authorized = ap_sta_is_authorized(sta);
+#ifdef CONFIG_IEEE80211BE
+ pasn->is_ml_peer = sta->mld_info.mld_sta;
+#endif /* CONFIG_IEEE80211BE */
+#endif /* CONFIG_ENC_ASSOC */
+
pasn_set_akmp(pasn, rsn_data.key_mgmt);
pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
@@ -2797,6 +4086,32 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len,
u16 trans_seq, u16 status)
{
+ int ret;
+#ifdef CONFIG_P2P
+ struct ieee802_11_elems elems;
+
+ if (len < 24) {
+ wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
+ return;
+ }
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed parsing Authentication frame");
+ return;
+ }
+
+ if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ (P2P_ENABLED | P2P_GROUP_OWNER) &&
+ hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
+ p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq);
+ return;
+ }
+#endif /* CONFIG_P2P */
+
if (hapd->conf->wpa != WPA_PROTO_RSN) {
wpa_printf(MSG_INFO, "PASN: RSN is not configured");
return;
@@ -2805,8 +4120,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
MAC2STR(sta->addr));
- if (trans_seq == 1) {
- if (sta->pasn) {
+ if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
+ if (sta->pasn && sta->auth_alg != WLAN_AUTH_EPPKE) {
wpa_printf(MSG_DEBUG,
"PASN: Not expecting transaction == 1");
return;
@@ -2818,7 +4133,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
return;
}
- sta->pasn = pasn_data_init();
+ if (!sta->pasn)
+ sta->pasn = pasn_data_init();
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to allocate PASN context");
@@ -2828,10 +4144,17 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
hapd_initialize_pasn(hapd, sta);
hapd_pasn_update_params(hapd, sta, mgmt, len);
- if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
- sta->addr, mgmt, len) < 0)
+ ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
+ mgmt, len, false);
+ wpabuf_free(sta->pasn->frame);
+ sta->pasn->frame = NULL;
+ if (ret < 0) {
+ hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+ sta->addr, 0, 0,
+ NULL, 0, NULL, 1);
ap_free_sta(hapd, sta);
- } else if (trans_seq == 3) {
+ }
+ } else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) {
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Not expecting transaction == 3");
@@ -2845,19 +4168,44 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
return;
}
- if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
- sta->addr, mgmt, len) == 0) {
+ ret = handle_auth_pasn_3(sta->pasn, hapd->own_addr, sta->addr,
+ mgmt, len);
+ if (ret == 0) {
+#ifdef CONFIG_ENC_ASSOC
+ if (ap_sta_is_epp(sta)) {
+ sta->auth_alg = WLAN_AUTH_EPPKE;
+ sta->flags |= WLAN_STA_AUTH;
+ }
+#endif /* CONFIG_ENC_ASSOC */
ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
pasn_get_cipher(sta->pasn), 43200,
pasn_get_ptk(sta->pasn), NULL, NULL,
+ pasn_get_akmp(sta->pasn),
+ sta->pasn->auth_alg);
+#ifdef CONFIG_ENC_ASSOC
+ /* TODO: Support VLAN ID assignment based on configured
+ * SAE passwords. */
+ if (ap_sta_is_epp(sta) && !sta->pasn->tk_configured &&
+ sta->pasn->eppke_set_key)
+ sta->pasn->eppke_set_key(
+ sta->pasn->cb_ctx,
+ wpa_cipher_to_alg(sta->pasn->cipher),
+ sta->addr, 0,
+ sta->pasn->ptk.tk,
+ sta->pasn->ptk.tk_len);
+#endif /* CONFIG_ENC_ASSOC */
+ if (!ap_sta_is_epp(sta))
+ pasn_set_keys_from_cache(
+ hapd, hapd->own_addr,
+ sta->addr,
+ pasn_get_cipher(sta->pasn),
pasn_get_akmp(sta->pasn));
-
- pasn_set_keys_from_cache(hapd, hapd->own_addr,
- sta->addr,
- pasn_get_cipher(sta->pasn),
- pasn_get_akmp(sta->pasn));
}
- ap_free_sta(hapd, sta);
+ if (!ap_sta_is_epp(sta) ||
+ (ret < 0 &&
+ ap_sta_is_epp(sta) && !ap_sta_is_authorized(sta)))
+ ap_free_sta(hapd, sta);
+
} else {
wpa_printf(MSG_DEBUG,
"PASN: Invalid transaction %u - ignore", trans_seq);
@@ -2882,9 +4230,7 @@ static void handle_auth(struct hostapd_data *hapd,
u16 seq_ctrl;
struct radius_sta rad_info;
const u8 *dst, *sa;
-#ifdef CONFIG_IEEE80211BE
bool mld_sta = false;
-#endif /* CONFIG_IEEE80211BE */
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -2929,10 +4275,10 @@ static void handle_auth(struct hostapd_data *hapd,
challenge = &mgmt->u.auth.variable[2];
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
- "auth_transaction=%d status_code=%d wep=%d%s "
+ "auth_transaction=%d status_code=%d protected=%d%s "
"seq_ctrl=0x%x%s%s",
MAC2STR(sa), auth_alg, auth_transaction,
- status_code, !!(fc & WLAN_FC_ISWEP),
+ status_code, !!(fc & WLAN_FC_PROTECTED),
challenge ? " challenge" : "",
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
from_queue ? " (from queue)" : "");
@@ -2947,6 +4293,17 @@ static void handle_auth(struct hostapd_data *hapd,
}
#endif /* CONFIG_NO_RC4 */
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (auth_alg == WLAN_AUTH_802_1X &&
+ !hapd->conf->eap_using_authentication_frames) {
+ wpa_printf(MSG_INFO,
+ "Unsupported authentication algorithm (%d)",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
if (hapd->tkip_countermeasures) {
wpa_printf(MSG_DEBUG,
"Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
@@ -2961,7 +4318,10 @@ static void handle_auth(struct hostapd_data *hapd,
auth_alg == WLAN_AUTH_FT) ||
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
- (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &&
auth_alg == WLAN_AUTH_SAE) ||
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
@@ -2976,6 +4336,19 @@ static void handle_auth(struct hostapd_data *hapd,
(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
auth_alg == WLAN_AUTH_PASN) ||
#endif /* CONFIG_PASN */
+#ifdef CONFIG_ENC_ASSOC
+ (hapd->conf->wpa &&
+ ((hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_EPPKE) &&
+ hapd->conf->assoc_frame_encryption &&
+ auth_alg == WLAN_AUTH_EPPKE) ||
+#endif /* CONFIG_ENC_ASSOC */
+#ifdef CONFIG_IEEE8021X_AUTH
+ (hapd->conf->wpa &&
+ wpa_key_mgmt_wpa_ieee8021x(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_802_1X) ||
+#endif /* CONFIG_IEEE8021X_AUTH */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
@@ -2984,10 +4357,24 @@ static void handle_auth(struct hostapd_data *hapd,
goto fail;
}
- if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
+ if (!(auth_transaction == 1 ||
+#ifdef CONFIG_SAE
+ (auth_alg == WLAN_AUTH_SAE &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) ||
+#endif /* CONFIG_SAE */
#ifdef CONFIG_PASN
- (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
+ (auth_alg == WLAN_AUTH_PASN &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) ||
#endif /* CONFIG_PASN */
+#ifdef CONFIG_ENC_ASSOC
+ (auth_alg == WLAN_AUTH_EPPKE &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) ||
+#endif /* CONFIG_ENC_ASSOC */
+#ifdef CONFIG_IEEE8021X_AUTH
+ /* EAP over Auth involves variable number of frames depending
+ * on the EAP authentication method */
+ auth_alg == WLAN_AUTH_802_1X ||
+#endif /* CONFIG_IEEE8021X_AUTH */
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
auth_transaction);
@@ -2995,25 +4382,13 @@ static void handle_auth(struct hostapd_data *hapd,
goto fail;
}
- if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
+ if (!hostapd_acceptable_sta_addr(hapd, mgmt->sa, sa, mld_sta)) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
-#ifdef CONFIG_IEEE80211BE
- if (mld_sta &&
- (ether_addr_equal(sa, hapd->own_addr) ||
- ether_addr_equal(sa, hapd->mld->mld_addr))) {
- wpa_printf(MSG_INFO,
- "Station " MACSTR " not allowed to authenticate",
- MAC2STR(sa));
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
-#endif /* CONFIG_IEEE80211BE */
-
if (hapd->conf->no_auth_if_seen_on) {
struct hostapd_data *other;
@@ -3081,10 +4456,28 @@ static void handle_auth(struct hostapd_data *hapd,
if (res == HOSTAPD_ACL_PENDING)
return;
+#ifdef CONFIG_IEEE80211BE
+ if (mld_sta) {
+ res = ieee802_11_allowed_address(hapd, mgmt->sa,
+ (const u8 *) mgmt, len,
+ &rad_info);
+ if (res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Authentication frame from " MACSTR
+ " due to ACL reject", MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (res == HOSTAPD_ACL_PENDING)
+ return;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_SAE
if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
- (auth_transaction == 1 ||
- (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
+ (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT ||
+ (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM &&
+ auth_sae_queued_addr(hapd, sa)))) {
/* Handle SAE Authentication commit message through a queue to
* provide more control for postponing the needed heavy
* processing under a possible DoS attack scenario. In addition,
@@ -3148,6 +4541,13 @@ static void handle_auth(struct hostapd_data *hapd,
}
}
+#if defined(CONFIG_ENC_ASSOC) || defined(CONFIG_IEEE8021X_AUTH)
+ if (auth_alg == WLAN_AUTH_EPPKE || auth_alg == WLAN_AUTH_802_1X) {
+ wpa_printf(MSG_DEBUG, "Mark the station as an EPP peer");
+ sta->epp_sta = true;
+ }
+#endif /* CONFIG_ENC_ASSOC || CONFIG_IEEE8021X_AUTH */
+
#ifdef CONFIG_IEEE80211BE
/* Set the non-AP MLD information based on the initial Authentication
* frame. Once the STA entry has been added to the driver, the driver
@@ -3175,6 +4575,16 @@ static void handle_auth(struct hostapd_data *hapd,
os_memcpy(sta->mld_info.links[link_id].local_addr,
hapd->own_addr, ETH_ALEN);
}
+
+ if ((!(sta->flags & WLAN_STA_MFP) ||
+ !ap_sta_is_authorized(sta)) && sta->wpa_sm) {
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+ clear_wpa_sm_for_all_sta(hapd, sm);
+ wpa_auth_sta_deinit(sm);
+ sta->wpa_sm = NULL;
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -3240,7 +4650,7 @@ static void handle_auth(struct hostapd_data *hapd,
#ifndef CONFIG_NO_RC4
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
- fc & WLAN_FC_ISWEP);
+ fc & WLAN_FC_PROTECTED);
if (resp != 0)
wpa_printf(MSG_DEBUG,
"auth_shared_key() failed: status=%d", resp);
@@ -3306,6 +4716,17 @@ static void handle_auth(struct hostapd_data *hapd,
handle_auth_fils_finish);
return;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X:
+ handle_auth_802_1x(hapd, sta, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN -
+ sizeof(mgmt->u.auth),
+ auth_alg, auth_transaction);
+ return;
+#endif /* CONFIG_IEEE8021X_AUTH */
+#ifdef CONFIG_ENC_ASSOC
+ case WLAN_AUTH_EPPKE:
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_PASN
case WLAN_AUTH_PASN:
handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
@@ -3344,7 +4765,10 @@ static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
return 0;
- num_bss_nontx = hapd->iface->num_bss - 1;
+ if (hapd->iface->conf->mbssid_max > 0)
+ num_bss_nontx = hapd->iface->conf->mbssid_max - 1;
+ else
+ num_bss_nontx = hapd->iface->conf->num_bss - 1;
while (num_bss_nontx > 0) {
max_bssid_ind++;
num_bss_nontx >>= 1;
@@ -3756,7 +5180,8 @@ static u16 owe_process_assoc_req(struct hostapd_data *hapd,
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
- sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE,
+ NULL, ap_sta_is_mld(hapd, sta));
return WLAN_STATUS_SUCCESS;
}
@@ -3772,7 +5197,7 @@ u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
if (!rsn_ie || rsn_ie_len < 2) {
wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
MAC2STR(peer));
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
rsn_ie -= 2;
rsn_ie_len += 2;
@@ -3788,13 +5213,13 @@ u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
wpa_printf(MSG_DEBUG,
"OWE: Unexpected key mgmt 0x%x from " MACSTR,
(unsigned int) data.key_mgmt, MAC2STR(peer));
- return WLAN_STATUS_AKMP_NOT_VALID;
+ return WLAN_STATUS_INVALID_AKMP;
}
if (!owe_dh) {
wpa_printf(MSG_DEBUG,
"OWE: No Diffie-Hellman Parameter element from "
MACSTR, MAC2STR(peer));
- return WLAN_STATUS_AKMP_NOT_VALID;
+ return WLAN_STATUS_INVALID_AKMP;
}
return WLAN_STATUS_SUCCESS;
@@ -3814,7 +5239,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
if (!rsn_ie || rsn_ie_len < 2) {
wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
- status = WLAN_STATUS_INVALID_IE;
+ status = WLAN_STATUS_INVALID_ELEMENT;
goto end;
}
@@ -3836,15 +5261,15 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd,
rsn_ie_len += 2;
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq, rsn_ie, rsn_ie_len,
- NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
+ NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL,
+ ap_sta_is_mld(hapd, sta));
status = wpa_res_to_status_code(res);
if (status != WLAN_STATUS_SUCCESS)
goto end;
status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
if (status != WLAN_STATUS_SUCCESS)
goto end;
- owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
- NULL, 0);
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie));
if (!owe_buf) {
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto end;
@@ -3888,19 +5313,84 @@ end:
#endif /* CONFIG_OWE */
+static bool hapd_is_known_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ies, size_t ies_len)
+{
+ const u8 *ie, *pos, *end, *timestamp_pos, *mic;
+ u64 timestamp;
+ u8 mic_len;
+
+ if (!hapd->conf->known_sta_identification)
+ return false;
+
+ ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION);
+ if (!ie)
+ return false;
+
+ pos = ie + 3;
+ end = &ie[2 + ie[1]];
+ if (end - pos < 8 + 1)
+ return false; /* truncated element */
+ timestamp_pos = pos;
+ timestamp = WPA_GET_LE64(pos);
+ pos += 8;
+ mic_len = *pos++;
+ if (mic_len > end - pos)
+ return false; /* truncated element */
+ mic = pos;
+
+ wpa_printf(MSG_DEBUG, "RSN: STA " MACSTR
+ " included Known STA Identification element: Timestamp=0x%llx mic_len=%u",
+ MAC2STR(sta->addr), (unsigned long long) timestamp, mic_len);
+
+ if (timestamp <= sta->last_known_sta_id_timestamp) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Ignore reused or old Known STA Identification");
+ return false;
+ }
+
+ if (!wpa_auth_sm_known_sta_identification(sta->wpa_sm, timestamp_pos,
+ mic, mic_len)) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Ignore Known STA Identification with invalid MIC or due to KCK not available");
+ return false;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: Valid Known STA Identification");
+ sta->last_known_sta_id_timestamp = timestamp;
+
+ return true;
+}
+
+
static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
- int reassoc)
+ int reassoc, const u8 *ies, size_t ies_len,
+ bool enc_assoc)
{
if ((sta->flags &
(WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
(WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
return false;
+#ifdef CONFIG_ENC_ASSOC
+ if (enc_assoc && sta->epp_sta) {
+ /* Skip SA Query since either the STA knows the PTK that is in
+ * use in the existing association or a new EPPKE authentication
+ * has already authenticated the STA and has replaced the TK and
+ * there is not really any point in starting SA Query procedure.
+ */
+ return false;
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
ap_check_sa_query_timeout(hapd, sta);
if (!sta->sa_query_timed_out &&
(!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+ if (hapd_is_known_sta(hapd, sta, ies, ies_len))
+ return false;
+
/*
* STA has already been associated with MFP and SA Query timeout
* has not been reached. Reject the association attempt
@@ -3918,54 +5408,70 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len,
- struct ieee802_11_elems *elems, int reassoc,
- bool link)
+ struct ieee802_11_elems *elems,
+ enum link_parse_type type,
+ struct wpa_state_machine *assoc_wpa_sm)
{
int resp;
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *p2p_dev_addr = NULL;
- struct hostapd_data *assoc_hapd;
- struct sta_info *assoc_sta = NULL;
+#ifdef CONFIG_PMKSA_PRIVACY
+ bool derive_next_pmkid = true;
+#endif /* CONFIG_PMKSA_PRIVACY */
+#ifdef CONFIG_IEEE8021X_AUTH
+ bool mic_check = true;
+#endif /* CONFIG_IEEE8021X_AUTH */
+#ifdef CONFIG_SAE
+ bool epp_sta = false;
+
+#ifdef CONFIG_ENC_ASSOC
+ epp_sta = sta->epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
+#endif /* CONFIG_SAE */
+
+ if (type != LINK_PARSE_RECONF) {
+ resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto out;
+ }
- resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
- if (resp != WLAN_STATUS_SUCCESS)
- return resp;
resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = check_ext_capab(hapd, sta, elems->ext_capab,
elems->ext_capab_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = copy_supp_rates(hapd, sta, elems);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
!(sta->flags & WLAN_STA_HT)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory HT PHY - reject association");
- return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ resp = WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ goto out;
}
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac) {
resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
@@ -3973,30 +5479,32 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory VHT PHY - reject association");
- return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ resp = WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ goto out;
}
if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
elems->vendor_vht_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
elems->he_capabilities,
elems->he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station does not support mandatory HE PHY - reject association");
- return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ goto out;
}
if (is_6ghz_op_class(hapd->iconf->op_class)) {
@@ -4005,29 +5513,39 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station does not support mandatory HE PHY - reject association");
- return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ goto out;
}
resp = copy_sta_he_6ghz_capab(hapd, sta,
elems->he_6ghz_band_cap);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
elems->he_capabilities,
elems->he_capabilities_len,
elems->eht_capabilities,
elems->eht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
- if (!link) {
+ if ((hapd->iconf->require_eht || hapd->conf->bss_require_eht) &&
+ !(sta->flags & WLAN_STA_EHT)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory EHT PHY - reject association");
+ return WLAN_STATUS_DENIED_EHT_NOT_SUPPORTED;
+ }
+
+ if (!assoc_wpa_sm) {
resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -4045,6 +5563,80 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE8021X_AUTH
+ /* Per IEEE 802.11bi/D4.0, 12.16.6 ((Re)Association Request/Response
+ * frame encryption), if IEEE 802.1X is used and FT protocol is not
+ * used, the EPP non-AP STA shall include a MIC element in the
+ * (Re)Association Request frame.
+ * Skip MIC validation on partner AP MLD links.
+ */
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ hapd->mld_link_id != sta->mld_assoc_link_id)
+ mic_check = false;
+#endif /* CONFIG_IEEE80211BE */
+ if (ap_sta_is_epp(sta) && sta->auth_alg == WLAN_AUTH_802_1X &&
+ mic_check) {
+ const u8 *data;
+ u8 mic_len, data_buf[(255 + 2) * 2], mic[WPA_1X_MAX_MIC_LEN];
+ const u8 *aa = hapd->own_addr;
+ size_t data_len = 0;
+ int ret;
+
+ if (!elems->mic) {
+ wpa_printf(MSG_DEBUG, "802.1X: Missing MIC element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+ if (wpa_key_mgmt_sha384(sta->eap_auth_data.akm))
+ mic_len = SHA384_MAC_LEN / 2;
+ else
+ mic_len = SHA256_MAC_LEN / 2;
+
+ if (mic_len != elems->mic_len) {
+ wpa_printf(MSG_DEBUG, "802.1X: Invalid MIC len: %u",
+ elems->mic_len);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ aa = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ os_memcpy(data_buf, elems->rsn_ie - 2, elems->rsn_ie_len + 2);
+ data_len += 2 + elems->rsn_ie_len;
+ os_memcpy(data_buf + data_len, elems->rsnxe - 2,
+ elems->rsnxe_len + 2);
+ data_len += 2 + elems->rsnxe_len;
+
+ data = data_buf;
+ ret = wpa_auth_8021x_mic(sta->eap_auth_data.akm,
+ sta->eap_auth_data.ptk.kck,
+ sta->eap_auth_data.ptk.kck_len, aa,
+ sta->addr, data, data_len,
+ NULL, 0, mic);
+ wpa_hexdump_key(MSG_DEBUG, "802.1X: Frame MIC",
+ elems->mic, elems->mic_len);
+ if (ret || os_memcmp(mic, elems->mic, mic_len) != 0) {
+ wpa_printf(MSG_INFO, "802.1X: Failed MIC verification");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+ /* Link Reconfiguration Request frame for add link operation will not
+ * have RSN and other security IEs. So, skip the checks.
+ */
+ if (type == LINK_PARSE_RECONF) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip security IE checks for Link Reconfiguration request");
+ goto skip_wpa_ies;
+ }
+
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
wpa_ie = elems->rsn_ie;
wpa_ie_len = elems->rsn_ie_len;
@@ -4075,7 +5667,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
"(Re)Association Request - reject");
- return WLAN_STATUS_INVALID_IE;
+ resp = WLAN_STATUS_INVALID_ELEMENT;
+ goto out;
}
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
@@ -4087,7 +5680,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"No WPA/RSN IE in association request");
- return WLAN_STATUS_INVALID_IE;
+ resp = WLAN_STATUS_INVALID_ELEMENT;
+ goto out;
}
if (hapd->conf->wpa && wpa_ie) {
@@ -4101,10 +5695,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_ie_len += 2;
if (!sta->wpa_sm) {
- if (!link)
- assoc_sta = hostapd_ml_get_assoc_sta(
- hapd, sta, &assoc_hapd);
-
+ /* NOTE: For links other than the assoc-link the
+ * separate wpa_sm is only allocated internally to this
+ * function.
+ */
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
p2p_dev_addr);
@@ -4112,8 +5706,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (!sta->wpa_sm) {
wpa_printf(MSG_WARNING,
"Failed to initialize RSN state machine");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
+#ifdef CONFIG_SAE
+ if (sta->sae && sta->sae->state == SAE_ACCEPTED &&
+ wpa_key_mgmt_sae_ext_key(sta->sae->akmp))
+ wpa_auth_set_hash_alg_sae_ext_key(
+ sta->wpa_sm, sta->sae->pmk_len);
+#endif /* CONFIG_SAE */
}
#ifdef CONFIG_IEEE80211BE
@@ -4128,6 +5729,11 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211BE */
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ if (sta->auth_alg == WLAN_AUTH_SAE)
+ wpa_auth_set_sae_pw_id(sta->wpa_sm, sta->sae_pw_id,
+ sta->sae_pw_id_counter);
+ wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection,
+ elems->rsn_selection_len);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
wpa_ie, wpa_ie_len,
@@ -4137,39 +5743,130 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
0,
elems->mdie, elems->mdie_len,
elems->owe_dh, elems->owe_dh_len,
- assoc_sta ? assoc_sta->wpa_sm : NULL);
+ assoc_wpa_sm,
+ ap_sta_is_mld(hapd, sta));
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
if (wpa_auth_uses_mfp(sta->wpa_sm))
sta->flags |= WLAN_STA_MFP;
else
sta->flags &= ~WLAN_STA_MFP;
+ if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+ sta->flags |= WLAN_STA_SPP_AMSDU;
+ else
+ sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
+#ifdef CONFIG_PMKSA_PRIVACY
+ /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy), when both
+ * the AP and non-AP STA support PMKSA caching privacy, the non-AP STA
+ * shall include a Nonce element in the (Re)Association Request frame.
+ * Skip Nonce element processing for partner AP MLD links. */
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ hapd->mld_link_id != sta->mld_assoc_link_id)
+ derive_next_pmkid = false;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (derive_next_pmkid && ap_sta_is_epp(sta) &&
+ hapd->conf->pmksa_caching_privacy &&
+ ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
+ WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) {
+ int akmp;
+ size_t pmk_len;
+ u8 *pmkid_next;
+
+ if (!elems->nonce) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR
+ " did not include Nonce element to compute next PMKID",
+ MAC2STR(sta->addr));
+ goto skip_pmkid_update;
+ }
+ os_memcpy(sta->snonce, elems->nonce, NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Received SNonce to compute next PMKID",
+ sta->snonce, NONCE_LEN);
+
+ switch (sta->auth_alg) {
+ case WLAN_AUTH_EPPKE:
+ if (!sta->pasn) {
+ wpa_printf(MSG_INFO,
+ "EPPKE: Missing PASN data - cannot derive a new PMKID");
+ goto skip_pmkid_update;
+ }
+ pmk_len = sta->pasn->pmk_len;
+ pmkid_next = sta->epp_pmkid_next;
+ break;
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X:
+ pmk_len = sta->eap_auth_data.pmk_len;
+ pmkid_next = sta->eap_auth_data.epp_pmkid_next;
+ break;
+#endif /* CONFIG_IEEE8021X_AUTH */
+ default:
+ wpa_printf(MSG_INFO,
+ "EPP: Unsupported auth alg %u for PMKID privacy",
+ sta->auth_alg);
+ goto skip_pmkid_update;
+ }
+
+ if (random_get_bytes(sta->anonce, NONCE_LEN) < 0)
+ goto skip_pmkid_update;
+ wpa_hexdump_key(MSG_DEBUG,
+ "EPP: Generated ANonce to compute next PMKID",
+ sta->anonce, NONCE_LEN);
+
+ akmp = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (akmp < 0 ||
+ wpa_auth_epp_derive_new_pmkid(sta->anonce, sta->snonce,
+ akmp, pmk_len,
+ pmkid_next) < 0) {
+ wpa_printf(MSG_INFO,
+ "EPP: Failed to generate new PMKID");
+ goto skip_pmkid_update;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EPP: New PMKID",
+ pmkid_next, PMKID_LEN);
+ }
+skip_pmkid_update:
+#endif /* CONFIG_PMKSA_PRIVACY */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
- if (!reassoc) {
+ if (type != LINK_PARSE_REASSOC) {
wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
"to use association (not "
"re-association) with FT auth_alg",
MAC2STR(sta->addr));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
ies_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_IEEE80211R_AP */
- if (link)
+ if (assoc_wpa_sm)
goto skip_sae_owe;
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
- sta->sae->state == SAE_ACCEPTED)
+ sta->sae->state == SAE_ACCEPTED) {
+ if (!sta->sae->h2e &&
+ wpa_key_mgmt_sae_ext_key(wpa_auth_sta_key_mgmt(
+ sta->wpa_sm))) {
+ wpa_printf(MSG_DEBUG, "SAE: STA " MACSTR
+ " tried to use EXT-KEY AKM without H2E",
+ MAC2STR(sta->addr));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+ }
if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg == WLAN_AUTH_OPEN) {
@@ -4179,18 +5876,24 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG,
"SAE: No PMKSA cache entry found for "
MACSTR, MAC2STR(sta->addr));
- return WLAN_STATUS_INVALID_PMKID;
+ resp = WLAN_STATUS_INVALID_PMKID;
+ goto out;
}
wpa_printf(MSG_DEBUG, "SAE: " MACSTR
" using PMKSA caching", MAC2STR(sta->addr));
- } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sae_assign_vlan(hapd, sta, sa->sae_vlan_id);
+ if (wpa_key_mgmt_sae_ext_key(sa->akmp))
+ wpa_auth_set_hash_alg_sae_ext_key(
+ sta->wpa_sm, sa->pmk_len);
+ } else if (!epp_sta && wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg != WLAN_AUTH_SAE &&
!(sta->auth_alg == WLAN_AUTH_FT &&
wpa_auth_uses_ft_sae(sta->wpa_sm))) {
wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
"SAE AKM after non-SAE auth_alg %u",
MAC2STR(sta->addr), sta->auth_alg);
- return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto out;
}
if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
@@ -4198,21 +5901,31 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
WLAN_RSNX_CAPAB_SAE_H2E)) {
- wpa_printf(MSG_INFO, "SAE: " MACSTR
- " indicates support for SAE H2E, but did not use it",
- MAC2STR(sta->addr));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (hapd->conf->sae_accept_h2e_without_use) {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it - accepting due to sae_accept_h2e_without_use",
+ MAC2STR(sta->addr));
+ } else {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it",
+ MAC2STR(sta->addr));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_OWE
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ if (((hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &
+ WPA_KEY_MGMT_OWE) &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
elems->owe_dh) {
resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
elems->owe_dh_len);
if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ goto out;
}
#endif /* CONFIG_OWE */
skip_sae_owe:
@@ -4225,7 +5938,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
- elems->owe_dh) {
+ elems->owe_dh && !assoc_wpa_sm) {
sta->dpp_pfs = dpp_pfs_init(
wpabuf_head(hapd->conf->dpp_netaccesskey),
wpabuf_len(hapd->conf->dpp_netaccesskey));
@@ -4240,12 +5953,13 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems->owe_dh_len) < 0) {
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
}
-
- wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
- sta->dpp_pfs->secret : NULL);
+ if (!assoc_wpa_sm)
+ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+ sta->dpp_pfs->secret : NULL);
pfs_fail:
#endif /* CONFIG_DPP2 */
@@ -4256,7 +5970,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_LEVEL_INFO,
"Station tried to use TKIP with HT "
"association");
- return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ resp = WLAN_STATUS_CIPHER_OUT_OF_POLICY;
+ goto out;
}
wpa_auth_set_ssid_protection(
@@ -4265,34 +5980,14 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
ieee802_11_rsnx_capab_len(
elems->rsnxe, elems->rsnxe_len,
WLAN_RSNX_CAPAB_SSID_PROTECTION));
-#ifdef CONFIG_HS20
- } else if (hapd->conf->osen) {
- if (!elems->osen) {
- hostapd_logger(
- hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "No HS 2.0 OSEN element in association request");
- return WLAN_STATUS_INVALID_IE;
- }
-
- wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
- if (sta->wpa_sm == NULL)
- sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr, NULL);
- if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_WARNING, "Failed to initialize WPA "
- "state machine");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
- if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
- elems->osen - 2, elems->osen_len + 2) < 0)
- return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
+skip_wpa_ies:
+
#ifdef CONFIG_P2P
- p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+ if (ies && ies_len)
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
@@ -4308,7 +6003,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG,
"HS 2.0: PMF not negotiated by release %d station "
MACSTR, release, MAC2STR(sta->addr));
- return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ goto out;
}
} else {
sta->hs20_ie = NULL;
@@ -4339,12 +6035,14 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_INFO,
"MBO: Reject WPA2 association without PMF");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
#endif /* CONFIG_MBO */
#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
- if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ if (type != LINK_PARSE_RECONF &&
+ wpa_auth_uses_ocv(sta->wpa_sm) &&
(sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)) {
@@ -4356,14 +6054,17 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
if (get_sta_tx_parameters(sta->wpa_sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
- &tx_seg1_idx) < 0)
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ &tx_seg1_idx) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
@@ -4379,7 +6080,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
MACSTR " frame=fils-reassoc-req error=%s",
MAC2STR(sta->addr), ocv_errorstr);
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
}
}
#endif /* CONFIG_FILS && CONFIG_OCV */
@@ -4414,12 +6116,46 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
hapd->conf->max_acceptable_idle_period;
}
- return WLAN_STATUS_SUCCESS;
+ if (elems->wfa_capab)
+ hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
+ elems->wfa_capab + elems->wfa_capab_len);
+
+out:
+ if (resp != WLAN_STATUS_SUCCESS || assoc_wpa_sm) {
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
+#ifdef CONFIG_IEEE80211BE
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+ clear_wpa_sm_for_all_sta(hapd, sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_auth_sta_deinit(sm);
+
+ /* Only keep a reference to the main wpa_sm and drop the
+ * per-link instance.
+ * This reference is needed during group rekey handling.
+ */
+ if (resp == WLAN_STATUS_SUCCESS) {
+ sta->wpa_sm = assoc_wpa_sm;
+#ifdef CONFIG_IEEE80211BE
+ set_wpa_sm_for_each_partner_link(hapd, sta,
+ assoc_wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+ } else {
+ sta->wpa_sm = NULL;
+#ifdef CONFIG_IEEE80211BE
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+#endif /* CONFIG_IEEE80211BE */
+ }
+ }
+
+ return resp;
}
static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *ies, size_t ies_len, int reassoc)
+ const u8 *ies, size_t ies_len,
+ enum link_parse_type type)
{
struct ieee802_11_elems elems;
@@ -4430,15 +6166,14 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
- false);
+ return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, type, NULL);
}
#ifdef CONFIG_IEEE80211BE
-static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
- struct mld_link_info *link)
+void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ struct mld_link_info *link)
{
u8 buf[EHT_ML_MAX_STA_PROF_LEN];
u8 *p = buf;
@@ -4462,18 +6197,18 @@ static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
p = hostapd_eid_ht_capabilities(hapd, p);
p = hostapd_eid_ht_operation(hapd, p);
- if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ if (hostapd_is_vht_enabled(hapd)) {
p = hostapd_eid_vht_capabilities(hapd, p, 0);
p = hostapd_eid_vht_operation(hapd, p);
}
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
p = hostapd_eid_he_6ghz_band_cap(hapd, p);
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_eht_operation(hapd, p);
}
@@ -4498,11 +6233,12 @@ out:
}
-static int ieee80211_ml_process_link(struct hostapd_data *hapd,
- struct sta_info *origin_sta,
- struct mld_link_info *link,
- const u8 *ies, size_t ies_len,
- bool reassoc, bool offload)
+int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ struct sta_info *origin_sta,
+ struct mld_link_info *link,
+ const u8 *ies, size_t ies_len,
+ enum link_parse_type type, bool offload,
+ bool *set_beacon)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf = NULL;
@@ -4520,7 +6256,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
}
sta = ap_get_sta(hapd, origin_sta->addr);
- if (sta) {
+ if (sta || TEST_FAIL()) {
wpa_printf(MSG_INFO, "MLD: link: Station already exists");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
sta = NULL;
@@ -4534,36 +6270,48 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
goto out;
}
- mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
- if (!mlbuf)
- goto out;
+ if (type != LINK_PARSE_RECONF) {
+ mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len,
+ true);
+ if (!mlbuf)
+ goto out;
- if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
- hapd->mld_link_id, true) ==
- ParseFailed) {
- wpa_printf(MSG_DEBUG,
- "MLD: link: Failed to parse association request Multi-Link element");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto out;
+ if (ieee802_11_parse_link_assoc_req(&elems, mlbuf,
+ hapd->mld_link_id, true) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: link: Failed to parse association request Multi-Link element");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
}
sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+ ap_sta_set_mld(sta, true);
+ sta->auth_alg = origin_sta->auth_alg;
+#ifdef CONFIG_ENC_ASSOC
+ sta->epp_sta = origin_sta->epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
- status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, type,
+ origin_sta->wpa_sm);
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
goto out;
}
- ap_sta_set_mld(sta, true);
-
os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
struct mld_link_info *li = &sta->mld_info.links[i];
li->resp_sta_profile = NULL;
li->resp_sta_profile_len = 0;
+
+ if (type == LINK_PARSE_RECONF && i == hapd->mld_link_id) {
+ os_memcpy(li->local_addr, hapd->own_addr, ETH_ALEN);
+ os_memcpy(li->peer_addr, link->peer_addr, ETH_ALEN);
+ }
}
if (!offload) {
@@ -4579,16 +6327,10 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
}
hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
sta->listen_interval = origin_sta->listen_interval;
- if (update_ht_state(hapd, sta) > 0)
- ieee802_11_update_beacons(hapd->iface);
+ if (update_ht_state(hapd, sta) > 0 && set_beacon)
+ *set_beacon = true;
}
- /* Maintain state machine reference on all link STAs, this is needed
- * during group rekey handling.
- */
- wpa_auth_sta_deinit(sta->wpa_sm);
- sta->wpa_sm = origin_sta->wpa_sm;
-
/*
* Do not initialize the EAPOL state machine.
* TODO: Maybe it is needed?
@@ -4599,20 +6341,24 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd,
hapd->mld_link_id, sta->aid);
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
+ sta->vlan_id = origin_sta->vlan_id;
/* TODO: What other processing is required? */
- if (!offload && add_associated_sta(hapd, sta, reassoc))
+ if (!offload &&
+ add_associated_sta(hapd, sta, type == LINK_PARSE_REASSOC))
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
out:
wpabuf_free(mlbuf);
link->status = status;
- if (!offload)
+ if (!offload && type != LINK_PARSE_RECONF)
ieee80211_ml_build_assoc_resp(hapd, link);
wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
if (status != WLAN_STATUS_SUCCESS) {
+ wpa_release_link_auth_ref(origin_sta->wpa_sm,
+ hapd->mld_link_id, true);
if (sta)
ap_free_sta(hapd, sta);
return -1;
@@ -4622,8 +6368,10 @@ out:
}
-bool hostapd_is_mld_ap(struct hostapd_data *hapd)
+bool hostapd_is_multiple_link_mld(struct hostapd_data *hapd)
{
+ struct hostapd_data *bss;
+
if (!hapd->conf->mld_ap)
return false;
@@ -4631,7 +6379,18 @@ bool hostapd_is_mld_ap(struct hostapd_data *hapd)
hapd->iface->interfaces->count <= 1)
return false;
- return true;
+ /*
+ * Checking for interfaces count above is not sufficient as there
+ * could be non-MLD interfaces or MLD interface that are not affiliated
+ * with the same MLD as the currently processing one. So need to check
+ * if other partner links exist for this the same AP MLD.
+ */
+ for_each_mld_link(bss, hapd) {
+ if (bss != hapd)
+ return true;
+ }
+
+ return false;
}
#endif /* CONFIG_IEEE80211BE */
@@ -4641,12 +6400,14 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ies, size_t ies_len,
bool reassoc, int tx_link_status,
- bool offload)
+ bool offload,
+ bool *set_beacon)
{
+ int ret = 0;
#ifdef CONFIG_IEEE80211BE
unsigned int i;
- if (!hostapd_is_mld_ap(hapd))
+ if (!hostapd_is_multiple_link_mld(hapd))
return 0;
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
@@ -4681,15 +6442,17 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
if (!offload)
ieee80211_ml_build_assoc_resp(hapd, link);
} else {
- if (ieee80211_ml_process_link(bss, sta, link,
- ies, ies_len, reassoc,
- offload))
- return -1;
+ if (ieee80211_ml_process_link(
+ bss, sta, link, ies, ies_len,
+ reassoc ? LINK_PARSE_REASSOC :
+ LINK_PARSE_ASSOC, offload,
+ set_beacon))
+ ret = -1;
}
}
#endif /* CONFIG_IEEE80211BE */
- return 0;
+ return ret;
}
@@ -4724,7 +6487,12 @@ static int add_associated_sta(struct hostapd_data *hapd,
struct ieee80211_eht_capabilities eht_cap;
int set = 1;
const u8 *mld_link_addr = NULL;
- bool mld_link_sta = false;
+ bool mld_link_sta = false, epp_sta = false;
+ u16 eml_cap = 0;
+
+#ifdef CONFIG_ENC_ASSOC
+ epp_sta = sta->epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta)) {
@@ -4735,6 +6503,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
if (hapd->mld_link_id != sta->mld_assoc_link_id)
set = 0;
+ eml_cap = sta->mld_info.common_info.eml_capa;
}
#endif /* CONFIG_IEEE80211BE */
@@ -4815,7 +6584,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
- set, mld_link_addr, mld_link_sta)) {
+ set, mld_link_addr, mld_link_sta, eml_cap,
+ epp_sta)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
"Could not %s STA to kernel driver",
@@ -4838,7 +6608,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
const u8 *ies, size_t ies_len, int rssi,
- int omit_rsnxe, bool allow_mld_addr_trans)
+ int omit_rsnxe)
{
int send_len;
u8 *buf;
@@ -4863,11 +6633,13 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
buflen += 5 + sta->dpp_pfs->curve->prime_len;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
buflen += 3 + sizeof(struct ieee80211_eht_operation);
if (hapd->iconf->punct_bitmap)
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+ if (ap_sta_is_mld(hapd, sta))
+ buflen += hostapd_eid_eht_ml_tid_to_link_map_len(hapd);
}
#endif /* CONFIG_IEEE80211BE */
@@ -4919,7 +6691,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
buf + buflen - p,
sta->auth_alg, ies, ies_len,
- omit_rsnxe);
+ omit_rsnxe, reassoc,
+ sta->vlan_id);
if (!p) {
wpa_printf(MSG_DEBUG,
"FT: Failed to write AssocResp IEs");
@@ -4934,16 +6707,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK))
p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
- buf + buflen - p,
- ies, ies_len);
+ buf + buflen - p);
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (sta && status_code == WLAN_STATUS_SUCCESS &&
- (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ ((hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &
+ WPA_KEY_MGMT_OWE))
p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
- buf + buflen - p,
- ies, ies_len);
+ buf + buflen - p);
#endif /* CONFIG_OWE */
if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
@@ -4953,7 +6727,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_ht_operation(hapd, p);
#ifdef CONFIG_IEEE80211AC
- if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ if (hostapd_is_vht_enabled(hapd) &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
u32 nsts = 0, sta_nsts;
@@ -4977,7 +6751,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ if (hostapd_is_he_enabled(hapd)) {
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_cca(hapd, p);
@@ -4988,7 +6762,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211AX */
p = hostapd_eid_ext_capab(hapd, p, false);
- p = hostapd_eid_bss_max_idle_period(hapd, p, sta->max_idle_period);
+ p = hostapd_eid_bss_max_idle_period(hapd, p,
+ sta ? sta->max_idle_period : 0);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
@@ -5019,16 +6794,19 @@ rsnxe_done:
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hostapd_is_eht_enabled(hapd)) {
if (hapd->conf->mld_ap)
p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_eht_operation(hapd, p);
+ if (ap_sta_is_mld(hapd, sta))
+ p = hostapd_eid_eht_ml_tid_to_link_map(hapd, p);
}
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_OWE
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_OWE) &&
sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
!wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
@@ -5051,6 +6829,95 @@ rsnxe_done:
}
#endif /* CONFIG_OWE */
+#ifdef CONFIG_ENC_ASSOC
+ if (sta &&
+ (sta->auth_alg == WLAN_AUTH_EPPKE ||
+ sta->auth_alg == WLAN_AUTH_802_1X) &&
+ wpa_auth_ap_sta_support_assoc_enc(sta->wpa_sm) &&
+ status_code == WLAN_STATUS_SUCCESS) {
+ /* Ensure GMK/Counter are initialized before the Key Delivery
+ * element is built. For auth-frame STAs the normal 4-way
+ * handshake path (SM_STATE AUTHENTICATION2) is skipped, so
+ * wpa_group_ensure_init() must be called here instead.
+ * Without this, the first non-auth-frame STA that connects
+ * later triggers a GTK rotation that is unknown to this STA,
+ * breaking broadcast frame reception. */
+ wpa_auth_ensure_group_init(sta->wpa_sm);
+ reply->frame_control |= WLAN_FC_PROTECTED;
+
+#ifdef CONFIG_PMKSA_PRIVACY
+ /* Include a Nonce element (ANonce) to compute next PMKID */
+ if (wpa_auth_ap_sta_support_pmkid_privacy(sta->wpa_sm)) {
+ switch (sta->auth_alg) {
+ case WLAN_AUTH_EPPKE:
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X:
+#endif /* CONFIG_IEEE8021X_AUTH */
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "EPP: Unsupported auth alg %u for PMKID privacy support",
+ sta->auth_alg);
+ goto skip_nonce;
+ }
+
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + NONCE_LEN; /* Length */
+ *p++ = WLAN_EID_EXT_NONCE; /* Element ID Extension */
+ os_memcpy(p, sta->anonce, NONCE_LEN);
+ p += NONCE_LEN;
+ }
+ skip_nonce:
+#ifdef CONFIG_SAE
+ /* For EPPKE with SAE, propagate the password identifier from
+ * the PASN wrapped SAE commit to the WPA state machine so that
+ * wpa_auth_eid_key_delivery() can include the SAE PW IDs KDE
+ * in the encrypted (Re)Association Response frame.
+ */
+ if (sta->auth_alg == WLAN_AUTH_EPPKE && sta->pasn &&
+ wpa_key_mgmt_sae(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+ const u8 *pw_id = NULL;
+ size_t pw_id_len = 0;
+
+ /* Use the decrypted (real) identifier when the STA
+ * presented an encrypted alternative identifier.
+ * Fall back to the raw parsed identifier for the
+ * plaintext case (first connection). */
+ if (sta->pasn->dec_pw_id &&
+ sta->pasn->dec_pw_id_len) {
+ pw_id = sta->pasn->dec_pw_id;
+ pw_id_len = sta->pasn->dec_pw_id_len;
+ } else if (sta->pasn->sae.tmp) {
+ if (sta->pasn->sae.tmp->parsed_pw_id) {
+ pw_id = sta->pasn->sae.tmp->parsed_pw_id;
+ pw_id_len = sta->pasn->sae.tmp->parsed_pw_id_len;
+ } else if (sta->pasn->sae.tmp->pw_id) {
+ pw_id = sta->pasn->sae.tmp->pw_id;
+ pw_id_len =
+ sta->pasn->sae.tmp->pw_id_len;
+ }
+ }
+ if (pw_id && pw_id_len) {
+ struct wpabuf *pw_id_buf;
+
+ pw_id_buf = wpabuf_alloc_copy(pw_id, pw_id_len);
+ if (pw_id_buf) {
+ wpa_auth_set_sae_pw_id(
+ sta->wpa_sm, pw_id_buf,
+ sta->pasn->sae_pw_id_counter);
+ wpabuf_free(pw_id_buf);
+ }
+ }
+ }
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_PMKSA_PRIVACY */
+
+ p = wpa_auth_write_assoc_resp_eppke(sta->wpa_sm, p,
+ buf + buflen - p,
+ ap_sta_is_mld(hapd, sta));
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
@@ -5179,14 +7046,14 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "OWE: Using IE override");
*status = WLAN_STATUS_SUCCESS;
return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
- owe_buf_len, NULL, 0);
+ owe_buf_len);
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
- owe_buf_len, NULL, 0);
+ owe_buf_len);
*status = WLAN_STATUS_SUCCESS;
return owe_buf;
}
@@ -5202,7 +7069,7 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
return NULL;
owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
- owe_buf_len, NULL, 0);
+ owe_buf_len);
if (sta->owe_ecdh && owe_buf) {
struct wpabuf *pub;
@@ -5244,8 +7111,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len, 0, 0,
- true);
+ sta->fils_pending_assoc_req_len, 0, 0);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -5291,7 +7157,7 @@ static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
u8 mld_addr[ETH_ALEN];
const u8 *pos;
- if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
+ if (!hostapd_is_eht_enabled(hapd))
return NULL;
if (reassoc) {
@@ -5410,10 +7276,22 @@ static void handle_assoc(struct hostapd_data *hapd,
sta = handle_mlo_translate(hapd, mgmt, len, reassoc,
&assoc_hapd);
if (sta) {
- wpa_printf(MSG_DEBUG,
- "MLD: Switching to assoc hapd/station");
- hapd = assoc_hapd;
- mld_addrs_not_translated = true;
+ if (hapd != assoc_hapd) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Switching to assoc hapd/station");
+ hapd = assoc_hapd;
+ mld_addrs_not_translated = true;
+ }
+
+ /* Allow link address to be changed if an SA query
+ * procedure has expired. */
+ if (sta->sa_query_timed_out) {
+ u8 _link = hapd->mld_link_id;
+
+ os_memcpy(sta->mld_info.links[_link].peer_addr,
+ mgmt->sa, ETH_ALEN);
+ }
+
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -5536,7 +7414,9 @@ static void handle_assoc(struct hostapd_data *hapd,
}
#endif /* CONFIG_MBO */
- if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) {
+ if (hapd->conf->wpa &&
+ check_sa_query(hapd, sta, reassoc, pos, left,
+ fc & WLAN_FC_PROTECTED)) {
resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
goto fail;
}
@@ -5575,10 +7455,16 @@ static void handle_assoc(struct hostapd_data *hapd,
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
- resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+ resp = check_assoc_ies(hapd, sta, pos, left,
+ reassoc ? LINK_PARSE_REASSOC : LINK_PARSE_ASSOC);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
- omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#ifdef CONFIG_IEEE80211R_AP
+ if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
+ omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#endif /* CONFIG_IEEE80211R_AP */
+ if (hapd->conf->rsn_override_omit_rsnxe)
+ omit_rsnxe = 1;
if (hostapd_get_aid(hapd, sta) < 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -5671,9 +7557,6 @@ static void handle_assoc(struct hostapd_data *hapd,
}
#endif /* CONFIG_FILS */
- if (set_beacon)
- ieee802_11_update_beacons(hapd->iface);
-
fail:
/*
@@ -5696,7 +7579,7 @@ static void handle_assoc(struct hostapd_data *hapd,
*/
if (sta)
hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
- resp, false);
+ resp, false, &set_beacon);
if (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
@@ -5706,6 +7589,9 @@ static void handle_assoc(struct hostapd_data *hapd,
if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
sta->fils_pending_assoc_req) {
+ if (set_beacon)
+ ieee802_11_update_beacons(hapd->iface);
+
/* Do not reschedule fils_hlp_timeout in case the station
* retransmits (Re)Association Request frame while waiting for
* the previously started FILS HLP wait, so that the timeout can
@@ -5725,6 +7611,9 @@ static void handle_assoc(struct hostapd_data *hapd,
sta->fils_hlp_resp = NULL;
}
if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+ if (set_beacon)
+ ieee802_11_update_beacons(hapd->iface);
+
sta->fils_pending_assoc_req = tmp;
sta->fils_pending_assoc_req_len = left;
sta->fils_pending_assoc_is_reassoc = reassoc;
@@ -5739,13 +7628,25 @@ static void handle_assoc(struct hostapd_data *hapd,
}
#endif /* CONFIG_FILS */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->association_response_status_code >= 0) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Forcing association response status code to %d",
+ hapd->conf->association_response_status_code);
+ resp = hapd->conf->association_response_status_code;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (resp >= 0)
reply_res = send_assoc_resp(hapd,
mld_addrs_not_translated ?
NULL : sta,
mgmt->sa, resp, reassoc,
- pos, left, rssi, omit_rsnxe,
- !mld_addrs_not_translated);
+ pos, left, rssi, omit_rsnxe);
+
+ if (set_beacon)
+ ieee802_11_update_beacons(hapd->iface);
+
os_free(tmp);
/*
@@ -5846,7 +7747,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
struct sta_info *assoc_sta;
struct sta_info *tmp_sta;
- if (!hostapd_is_mld_ap(hapd))
+ if (!hostapd_is_multiple_link_mld(hapd))
return false;
/*
@@ -5970,10 +7871,51 @@ static void handle_beacon(struct hostapd_data *hapd,
}
+static int hostapd_action_vs(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ unsigned int freq, bool protected)
+{
+ const u8 *pos, *end;
+ u32 oui_type;
+
+ pos = (const u8 *) &mgmt->u.action;
+ end = ((const u8 *) mgmt) + len;
+
+ if (end - pos < 1 + 4)
+ return -1;
+ pos++;
+
+ oui_type = WPA_GET_BE32(pos);
+ pos += 4;
+
+ switch (oui_type) {
+ case WFA_CAPAB_VENDOR_TYPE:
+ hostapd_wfa_capab(hapd, sta, pos, end);
+ return 0;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Ignore unknown Vendor Specific Action frame OUI/type %08x%s",
+ oui_type, protected ? " (protected)" : "");
+ break;
+ }
+
+ return -1;
+}
+
+
static int robust_action_frame(u8 category)
{
return category != WLAN_ACTION_PUBLIC &&
- category != WLAN_ACTION_HT;
+ category != WLAN_ACTION_HT &&
+ category != WLAN_ACTION_UNPROTECTED_WNM &&
+ category != WLAN_ACTION_SELF_PROTECTED &&
+ category != WLAN_ACTION_UNPROTECTED_DMG &&
+ category != WLAN_ACTION_VHT &&
+ category != WLAN_ACTION_UNPROTECTED_S1G &&
+ category != WLAN_ACTION_HE &&
+ category != WLAN_ACTION_EHT &&
+ category != WLAN_ACTION_VENDOR_SPECIFIC;
}
@@ -6009,7 +7951,7 @@ static int handle_action(struct hostapd_data *hapd,
}
if (sta && (sta->flags & WLAN_STA_MFP) &&
- !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+ !(mgmt->frame_control & host_to_le16(WLAN_FC_PROTECTED)) &&
robust_action_frame(mgmt->u.action.category)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -6114,14 +8056,14 @@ static int handle_action(struct hostapd_data *hapd,
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
mgmt->u.action.u.vs_public_action.variable[0] ==
- NAN_OUI_TYPE) {
+ NAN_SDF_OUI_TYPE) {
const u8 *pos, *end;
pos = mgmt->u.action.u.vs_public_action.variable;
end = ((const u8 *) mgmt) + len;
pos++;
- hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
- pos, end - pos);
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+ freq, pos, end - pos);
return 1;
}
#endif /* CONFIG_NAN_USD */
@@ -6142,12 +8084,25 @@ static int handle_action(struct hostapd_data *hapd,
(u8 *) mgmt, len, freq) == 0)
return 1;
}
+ if (sta &&
+ hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0)
+ return 1;
+ break;
+ case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED:
+ if (sta &&
+ hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0)
+ return 1;
break;
#ifndef CONFIG_NO_RRM
case WLAN_ACTION_RADIO_MEASUREMENT:
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
#endif /* CONFIG_NO_RRM */
+#ifdef CONFIG_IEEE80211BE
+ case WLAN_ACTION_PROTECTED_EHT:
+ ieee802_11_rx_protected_eht_action(hapd, mgmt, len);
+ return 1;
+#endif /* CONFIG_IEEE80211BE */
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6155,32 +8110,6 @@ static int handle_action(struct hostapd_data *hapd,
"handle_action - unknown action category %d or invalid "
"frame",
mgmt->u.action.category);
- if (!is_multicast_ether_addr(mgmt->da) &&
- !(mgmt->u.action.category & 0x80) &&
- !is_multicast_ether_addr(mgmt->sa)) {
- struct ieee80211_mgmt *resp;
-
- /*
- * IEEE 802.11-REVma/D9.0 - 7.3.1.11
- * Return the Action frame to the source without change
- * except that MSB of the Category set to 1.
- */
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
- "frame back to sender");
- resp = os_memdup(mgmt, len);
- if (resp == NULL)
- return 0;
- os_memcpy(resp->da, resp->sa, ETH_ALEN);
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
- resp->u.action.category |= 0x80;
-
- if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
- wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
- "Action frame");
- }
- os_free(resp);
- }
return 1;
}
@@ -6232,10 +8161,6 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
int ret = 0;
unsigned int freq;
int ssi_signal = fi ? fi->ssi_signal : 0;
-#ifdef CONFIG_NAN_USD
- static const u8 nan_network_id[ETH_ALEN] =
- { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
-#endif /* CONFIG_NAN_USD */
if (len < 24)
return 0;
@@ -6267,6 +8192,10 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
}
if (!is_broadcast_ether_addr(mgmt->bssid) &&
+#ifdef CONFIG_NAN_USD
+ !nan_de_is_nan_network_id(mgmt->bssid) &&
+ !nan_de_is_p2p_network_id(mgmt->bssid) &&
+#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_P2P
/* Invitation responses can be sent with the peer MAC as BSSID */
!((hapd->conf->p2p & P2P_GROUP_OWNER) &&
@@ -6304,6 +8233,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_NAN_USD
!ether_addr_equal(mgmt->da, nan_network_id) &&
+ !ether_addr_equal(mgmt->da, p2p_network_id) &&
#endif /* CONFIG_NAN_USD */
!ether_addr_equal(mgmt->da, hapd->own_addr)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6410,9 +8340,18 @@ static void handle_auth_cb(struct hostapd_data *hapd,
fail:
success_status = status_code == WLAN_STATUS_SUCCESS;
#ifdef CONFIG_SAE
- if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
+ if (auth_alg == WLAN_AUTH_SAE &&
+ auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT)
success_status = sae_status_success(hapd, status_code);
#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (auth_alg == WLAN_AUTH_802_1X &&
+ status_code == WLAN_STATUS_802_1_X_AUTH_SUCCESS) {
+ sta->flags |= WLAN_STA_AUTH;
+ sta->auth_alg = WLAN_AUTH_802_1X;
+ success_status = true;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
if (!success_status && sta->added_unassoc) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
@@ -6500,7 +8439,7 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *tmp_hapd;
- if (!hostapd_is_mld_ap(hapd))
+ if (!hostapd_is_multiple_link_mld(hapd))
return;
for_each_mld_link(tmp_hapd, hapd) {
@@ -6599,12 +8538,13 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
- !hapd->conf->osen) ||
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK ||
- sta->auth_alg == WLAN_AUTH_FT) {
+ sta->auth_alg == WLAN_AUTH_FT ||
+ sta->auth_alg == WLAN_AUTH_EPPKE ||
+ sta->auth_alg == WLAN_AUTH_802_1X) {
/*
* Open, static WEP, FT protocol, or FILS; no separate
* authorization step.
@@ -6617,7 +8557,86 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
else
mlme_associate_indication(hapd, sta);
- sta->sa_query_timed_out = 0;
+ ap_sta_set_sa_query_timeout(hapd, sta, 0);
+
+#ifdef CONFIG_PMKSA_PRIVACY
+ if (ok && status == WLAN_STATUS_SUCCESS && sta->epp_sta &&
+ wpa_auth_ap_sta_support_pmkid_privacy(sta->wpa_sm)) {
+ bool is_ml = ap_sta_is_mld(hapd, sta);
+ struct rsn_pmksa_cache_entry *entry, *next;
+ struct rsn_pmksa_cache *pmksa, *t_pmksa;
+ const u8 *pmkid_cur, *pmkid_next;
+
+ switch (sta->auth_alg) {
+ case WLAN_AUTH_EPPKE:
+ if (!sta->pasn) {
+ wpa_printf(MSG_INFO, "EPP: Missing PASN data");
+ goto skip_update;
+ }
+ pmkid_cur = sta->pasn->epp_pmkid_cur;
+ pmkid_next = sta->epp_pmkid_next;
+ break;
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X:
+ pmkid_cur = sta->eap_auth_data.epp_pmkid_cur;
+ pmkid_next = sta->eap_auth_data.epp_pmkid_next;
+ break;
+#endif /* CONFIG_IEEE8021X_AUTH */
+ default:
+ wpa_printf(MSG_INFO,
+ "EPP: Unsupported auth alg %u for PMKID privacy support",
+ sta->auth_alg);
+ goto skip_update;
+ }
+
+ pmksa = t_pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth,
+ is_ml);
+
+ entry = pmksa_cache_auth_get(t_pmksa, NULL, pmkid_cur);
+ if (entry)
+ goto update_pmksa_entry;
+
+#ifdef CONFIG_IEEE80211BE
+ if (!entry && is_ml) {
+ struct hostapd_data *tmp_hapd;
+
+ /* Search in link caches of each AP MLD link */
+ for_each_mld_link(tmp_hapd, hapd) {
+ t_pmksa = wpa_auth_get_pmksa_cache(
+ tmp_hapd->wpa_auth, false);
+ entry = pmksa_cache_auth_get(t_pmksa, NULL,
+ pmkid_cur);
+ if (entry)
+ break;
+ }
+ } else if (!entry && !is_ml && hapd->conf->mld_ap) {
+ /* Search in the MLD cache */
+ t_pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth,
+ true);
+ entry = pmksa_cache_auth_get(t_pmksa, NULL, pmkid_cur);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+update_pmksa_entry:
+ if (entry) {
+ wpa_printf(MSG_DEBUG,
+ "EPP: PMKSA caching privacy on - update PMKSA cache entry");
+ next = os_memdup(entry, sizeof(*entry));
+ if (!next)
+ goto skip_update;
+ os_memcpy(next->pmkid, pmkid_next, PMKID_LEN);
+ os_memcpy(next->spa, sta->addr, ETH_ALEN);
+ next->vlan_desc = NULL;
+ next->identity = NULL;
+ next->dpp_pkhash = NULL;
+ next->cui = NULL;
+ pmksa_cache_from_eapol_data(next, sta->eapol_sm);
+ pmksa_cache_free_entry(t_pmksa, entry);
+ pmksa_cache_auth_add_entry(pmksa, next);
+ }
+ }
+skip_update:
+#endif /* CONFIG_PMKSA_PRIVACY */
if (sta->eapol_sm == NULL) {
/*
@@ -6814,6 +8833,19 @@ static void handle_action_cb(struct hostapd_data *hapd,
}
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_IEEE80211BE
+ /* Frame header (24B) + Category (1B) + Action code (1B) +
+ * Dialog token (1B) + Count (1B) + Status list (count * 3B)
+ */
+ if (len >= IEEE80211_HDRLEN + 3 + 1 + 3 &&
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_EHT &&
+ mgmt->u.action.u.link_reconf_resp.action ==
+ WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE) {
+ hostapd_link_reconf_resp_tx_status(hapd, sta, mgmt, len, ok);
+ return;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifndef CONFIG_NO_RRM
if (len < 24 + 5 + sizeof(*report))
return;
@@ -6905,8 +8937,16 @@ int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
- /* TODO */
- return 0;
+ int len = 0, ret;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "auth_alg=%d\n",
+ sta->auth_alg);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
}
@@ -7103,42 +9143,59 @@ 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;
+ int max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+ int max_eirp_power = iconf->reg_def_cli_eirp;
/* 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 */
@@ -7220,37 +9277,160 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
}
-u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+/* Wide Bandwidth Channel Switch subelement */
+static u8 * hostapd_eid_wb_channel_switch(struct hostapd_data *hapd, u8 *eid,
+ u8 chan1, u8 chan2)
{
- u8 bw, chan1 = 0, chan2 = 0;
- int freq1;
-
- if (!hapd->cs_freq_params.channel ||
- (!hapd->cs_freq_params.vht_enabled &&
- !hapd->cs_freq_params.he_enabled &&
- !hapd->cs_freq_params.eht_enabled))
- return eid;
+ u8 bw;
- /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4: 320 as per
- * IEEE P802.11-REVme/D4.0, 9.4.2.159 and Table 9-314. */
+ /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per
+ * IEEE Std 802.11-2024, 9.4.2.156 and Table 9-316 (VHT Operation
+ * Information subfields).
+ */
switch (hapd->cs_freq_params.bandwidth) {
- case 40:
- bw = 0;
- break;
- case 80:
+ case 320:
+ /* As per IEEE Std 802.11be-2024, 35.15.3 (Channel switching
+ * methods for an EHT BSS), for EHT BSS operating channel width
+ * wider than 160 MHz, the announced BSS bandwidth in the Wide
+ * Bandwidth Channel Switch element is less than the BSS
+ * bandwidth in the Bandwidth Indication element
+ */
+
+ /* Modifying the center frequency to 160 MHz */
+ if (hapd->cs_freq_params.channel < chan1)
+ chan1 -= 16;
+ else
+ chan1 += 16;
+
+ /* fallthrough */
+ case 160:
+ /* Update the CCFS0 and CCFS1 values in the element based on
+ * IEEE Std 802.11-2024, Table 9-316 (VHT Operation
+ * Information subfields).
+ */
+
+ /* CCFS1 - The channel center frequency index of the 160 MHz
+ * channel. */
+ chan2 = chan1;
+
+ /* CCFS0 - The channel center frequency index of the 80 MHz
+ * channel segment that contains the primary channel. */
+ if (hapd->cs_freq_params.channel < chan1)
+ chan1 -= 8;
+ else
+ chan1 += 8;
+
bw = 1;
break;
- case 160:
+ case 80:
bw = 1;
break;
- case 320:
- bw = 4;
+ case 40:
+ bw = 0;
break;
default:
/* not valid VHT bandwidth or not in CSA */
return eid;
}
+ *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+ *eid++ = bw; /* New Channel Width */
+ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+ return eid;
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+/* Bandwidth Indication element that is also used as the Bandwidth Indication
+ * For Channel Switch subelement within a Channel Switch Wrapper element. */
+static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
+ u8 chan1, u8 chan2)
+{
+ u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap;
+ struct ieee80211_bw_ind_element *bw_ind_elem;
+ size_t elen = 4;
+
+ if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap)
+ return eid;
+
+ if (punct_bitmap)
+ elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 1 + elen;
+ *eid++ = WLAN_EID_EXT_BANDWIDTH_INDICATION;
+
+ bw_ind_elem = (struct ieee80211_bw_ind_element *) eid;
+ os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element));
+
+ switch (hapd->cs_freq_params.bandwidth) {
+ case 320:
+ bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_320MHZ;
+ chan2 = chan1;
+ if (hapd->cs_freq_params.channel < chan1)
+ chan1 -= 16;
+ else
+ chan1 += 16;
+ break;
+ case 160:
+ bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_160MHZ;
+ chan2 = chan1;
+ if (hapd->cs_freq_params.channel < chan1)
+ chan1 -= 8;
+ else
+ chan1 += 8;
+ break;
+ case 80:
+ bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_80MHZ;
+ break;
+ case 40:
+ if (hapd->cs_freq_params.sec_channel_offset == 1)
+ bw_ind_elem->bw_ind_info.control |=
+ BW_IND_CHANNEL_WIDTH_40MHZ;
+ else
+ bw_ind_elem->bw_ind_info.control |=
+ BW_IND_CHANNEL_WIDTH_20MHZ;
+ break;
+ default:
+ bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_20MHZ;
+ break;
+ }
+
+ bw_ind_elem->bw_ind_info.ccfs0 = chan1;
+ bw_ind_elem->bw_ind_info.ccfs1 = chan2;
+
+ if (punct_bitmap) {
+ bw_ind_elem->bw_ind_params |=
+ BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT;
+ bw_ind_elem->bw_ind_info.disabled_chan_bitmap =
+ host_to_le16(punct_bitmap);
+ }
+
+ return eid + elen;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 chan1 = 0, chan2 = 0;
+ u8 *eid_len_offset;
+ int freq1;
+
+ if (!hostapd_is_vht_enabled(hapd) &&
+ !hostapd_is_he_enabled(hapd) &&
+ !hostapd_is_eht_enabled(hapd))
+ return eid;
+
+ if (!hapd->cs_freq_params.channel ||
+ (!hapd->cs_freq_params.vht_enabled &&
+ !hapd->cs_freq_params.he_enabled &&
+ !hapd->cs_freq_params.eht_enabled))
+ return eid;
+
freq1 = hapd->cs_freq_params.center_freq1 ?
hapd->cs_freq_params.center_freq1 :
hapd->cs_freq_params.freq;
@@ -7264,28 +9444,18 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
return eid;
*eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
- *eid++ = 5; /* Length of Channel Switch Wrapper */
- *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
- *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
- *eid++ = bw; /* New Channel Width */
- if (hapd->cs_freq_params.bandwidth == 160) {
- /* Update the CCFS0 and CCFS1 values in the element based on
- * IEEE P802.11-REVme/D4.0, Table 9-314 */
+ eid_len_offset = eid++; /* Length of Channel Switch Wrapper element */
- /* CCFS1 - The channel center frequency index of the 160 MHz
- * channel. */
- chan2 = chan1;
+ eid = hostapd_eid_wb_channel_switch(hapd, eid, chan1, chan2);
- /* CCFS0 - The channel center frequency index of the 80 MHz
- * channel segment that contains the primary channel. */
- if (hapd->cs_freq_params.channel < chan1)
- chan1 -= 8;
- else
- chan1 += 8;
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_is_eht_enabled(hapd)) {
+ /* Bandwidth Indication For Channel Switch subelement */
+ eid = hostapd_eid_bw_indication(hapd, eid, chan1, chan2);
}
- *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
- *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+#endif /* CONFIG_IEEE80211BE */
+ *eid_len_offset = (eid - eid_len_offset) - 1;
return eid;
}
@@ -7320,6 +9490,44 @@ static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
}
+#ifdef CONFIG_IEEE80211BE
+static bool hostapd_mbssid_mld_match(struct hostapd_data *tx_hapd,
+ struct hostapd_data *ml_hapd,
+ u8 *match_idx)
+{
+ size_t bss_idx;
+
+ if (!ml_hapd->conf->mld_ap)
+ return false;
+
+ if (!tx_hapd->iconf->mbssid || tx_hapd->iface->num_bss <= 1) {
+ if (hostapd_is_ml_partner(tx_hapd, ml_hapd)) {
+ if (match_idx)
+ *match_idx = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ for (bss_idx = 0; bss_idx < tx_hapd->iface->num_bss; bss_idx++) {
+ struct hostapd_data *bss = tx_hapd->iface->bss[bss_idx];
+
+ if (!bss)
+ continue;
+
+ if (hostapd_is_ml_partner(bss, ml_hapd)) {
+ if (match_idx)
+ *match_idx = bss_idx;
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
struct mbssid_ie_profiles {
u8 start;
u8 end;
@@ -7328,15 +9536,22 @@ struct mbssid_ie_profiles {
static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
bool ap_mld, u8 tbtt_info_len, bool mld_update,
struct hostapd_data *reporting_hapd,
- struct hostapd_data *bss)
+ struct hostapd_data *bss, u8 *match_idx)
{
- if (skip_profiles &&
+ bool reporting_ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ reporting_ap_mld = !!reporting_hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!mld_update && skip_profiles &&
i >= skip_profiles->start && i < skip_profiles->end)
return true;
- /* No need to report if length is for normal TBTT and the BSS is
- * affiliated with an AP MLD. MLD TBTT will include this. */
- if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
+ /* No need to report if length is for normal TBTT and both the reporting
+ * AP and neighbor AP are affiliated with an AP MLD. MLD TBTT will
+ * include this. */
+ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld && reporting_ap_mld)
return true;
/* No need to report if length is for MLD TBTT and the BSS is not
@@ -7352,7 +9567,17 @@ static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
/* If building for ML RNR and they are not ML partners, don't include.
*/
- if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
+ if (mld_update &&
+ !hostapd_mbssid_mld_match(reporting_hapd, bss, match_idx))
+ return true;
+
+ /* When MLD parameters are added to beacon RNR and in case of EMA
+ * beacons we report only affiliated APs belonging to the reported
+ * non Tx profiles and TX profile will be reported in every EMA beacon.
+ */
+ if (mld_update && skip_profiles && match_idx &&
+ (*match_idx < skip_profiles->start ||
+ *match_idx >= skip_profiles->end))
return true;
#endif /* CONFIG_IEEE80211BE */
@@ -7360,6 +9585,60 @@ static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
}
+static bool hostapd_rnr_get_bss_info(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t i, u8 tbtt_info_len,
+ bool mld_update,
+ u8 *op_class, u8 *channel, u8 *match_idx)
+{
+ struct hostapd_data *bss;
+ bool ap_mld = false;
+ u8 tmp_match_idx = 255;
+ enum oper_chan_width bss_chwidth;
+ int secondary_channel;
+ u8 seg0, seg1;
+
+ if (!hapd->iface || i >= hapd->iface->num_bss || !op_class || !channel)
+ return false;
+
+ bss = hapd->iface->bss[i];
+ if (!bss || !bss->conf || !bss->started || !bss->beacon_set_done ||
+ bss == reporting_hapd)
+ return false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!bss->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ /* MLD RNR has to be included for the parameter change count */
+ if (bss->conf->ignore_broadcast_ssid && !(ap_mld && mld_update))
+ return false;
+
+ if (!match_idx)
+ match_idx = &tmp_match_idx;
+
+ if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
+ mld_update, reporting_hapd, bss, match_idx))
+ return false;
+
+ hostapd_get_oper_chan_info_of_bss(bss, &bss_chwidth, &seg0, &seg1);
+ secondary_channel = bss->iconf->secondary_channel;
+
+ if (seg0 == bss->iconf->channel &&
+ bss_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ secondary_channel = 0;
+
+ if (ieee80211_freq_to_channel_ext(
+ bss->iface->freq,
+ secondary_channel, bss_chwidth,
+ op_class, channel) == NUM_HOSTAPD_MODES)
+ return false;
+
+ return true;
+}
+
+
static size_t
hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
@@ -7367,84 +9646,121 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
struct mbssid_ie_profiles *skip_profiles,
bool mld_update)
{
+ struct hostapd_iface *iface = hapd->iface;
size_t total_len = 0, len = *current_len;
- int tbtt_count, total_tbtt_count = 0;
- size_t i, start;
+ int total_tbtt_count = 0;
+ size_t i;
u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
RNR_TBTT_INFO_LEN;
+ bool reporting_ap_mld = false;
+ bool have_pending_group;
+ u8 pending_op_class = 0, pending_channel = 0;
+ bool *tbtt_added = NULL;
+
+#ifdef CONFIG_IEEE80211BE
+ reporting_ap_mld = !!reporting_hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
repeat_rnr_len:
- start = 0;
- tbtt_count = 0;
+ os_free(tbtt_added);
+ tbtt_added = os_zalloc(iface->num_bss);
+ if (!tbtt_added)
+ return total_len;
+
+ have_pending_group = false;
+ for (;;) {
+ int tbtt_count = 0;
+ bool group_found = false, group_pending = false;
+ u8 rnr_op_class = 0, rnr_channel = 0;
+
+ if (have_pending_group) {
+ rnr_op_class = pending_op_class;
+ rnr_channel = pending_channel;
+ group_found = true;
+ }
+
+ if (!group_found) {
+ for (i = 0; i < iface->num_bss; i++) {
+ if (tbtt_added[i])
+ continue;
+ if (!hostapd_rnr_get_bss_info(
+ hapd, reporting_hapd, skip_profiles,
+ i, tbtt_info_len, mld_update,
+ &rnr_op_class, &rnr_channel, NULL))
+ continue;
+ group_found = true;
+ break;
+ }
+ }
+
+ if (!group_found)
+ break;
- while (start < hapd->iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255) {
len = RNR_HEADER_LEN;
total_len += RNR_HEADER_LEN;
- tbtt_count = 0;
}
len += RNR_TBTT_HEADER_LEN;
total_len += RNR_TBTT_HEADER_LEN;
- for (i = start; i < hapd->iface->num_bss; i++) {
- struct hostapd_data *bss = hapd->iface->bss[i];
- bool ap_mld = false;
+ for (i = 0; i < iface->num_bss; i++) {
+ u8 bss_op_class, bss_channel;
- if (!bss || !bss->conf || !bss->started)
+ if (tbtt_added[i])
continue;
-#ifdef CONFIG_IEEE80211BE
- ap_mld = bss->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
-
- if (bss == reporting_hapd ||
- bss->conf->ignore_broadcast_ssid)
+ if (!hostapd_rnr_get_bss_info(
+ hapd, reporting_hapd, skip_profiles,
+ i, tbtt_info_len, mld_update,
+ &bss_op_class, &bss_channel, NULL))
continue;
- if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
- tbtt_info_len, mld_update,
- reporting_hapd, bss))
+ if (rnr_op_class != bss_op_class ||
+ rnr_channel != bss_channel)
continue;
if (len + tbtt_info_len > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ group_pending = true;
break;
+ }
len += tbtt_info_len;
total_len += tbtt_info_len;
tbtt_count++;
+ tbtt_added[i] = true;
}
- start = i;
- }
- total_tbtt_count += tbtt_count;
-
- /* If building for co-location, re-build again but this time include
- * ML TBTTs.
- */
- if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
- tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
-
- /* If no TBTT was found, adjust the len and total_len since it
- * would have incremented before we checked all BSSs. */
if (!tbtt_count) {
len -= RNR_TBTT_HEADER_LEN;
total_len -= RNR_TBTT_HEADER_LEN;
+ break;
}
- goto repeat_rnr_len;
+ total_tbtt_count += tbtt_count;
+
+ if (group_pending) {
+ have_pending_group = true;
+ pending_op_class = rnr_op_class;
+ pending_channel = rnr_channel;
+ } else {
+ have_pending_group = false;
+ }
}
- /* This is possible when in the re-built case and no suitable TBTT was
- * found. Adjust the length accordingly. */
- if (!tbtt_count && total_tbtt_count) {
- len -= RNR_TBTT_HEADER_LEN;
- total_len -= RNR_TBTT_HEADER_LEN;
+ /* If building for co-location, re-build again but this time include
+ * ML TBTTs if the reporting AP is affiliated with an AP MLD.
+ */
+ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN &&
+ reporting_ap_mld) {
+ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
+ goto repeat_rnr_len;
}
+ os_free(tbtt_added);
+
if (!total_tbtt_count)
total_len = 0;
else
@@ -7521,6 +9837,7 @@ static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
+ struct mbssid_ie_profiles *skip_profiles,
size_t *current_len)
{
size_t len = 0;
@@ -7528,7 +9845,7 @@ static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
struct hostapd_iface *iface;
size_t i;
- if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ if (!hapd->iface || !hapd->iface->interfaces)
return 0;
/* TODO: Allow for FILS/Action as well */
@@ -7543,7 +9860,8 @@ static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
continue;
len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
- current_len, NULL, true);
+ current_len, skip_profiles,
+ true);
}
#endif /* CONFIG_IEEE80211BE */
@@ -7587,7 +9905,8 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
if (include_mld_params &&
(type != WLAN_FC_STYPE_BEACON ||
hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
- total_len += hostapd_eid_rnr_mlo_len(hapd, type, &current_len);
+ total_len += hostapd_eid_rnr_mlo_len(hapd, type, NULL,
+ &current_len);
return total_len;
}
@@ -7650,30 +9969,27 @@ static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
- struct mbssid_ie_profiles *skip_profiles,
size_t i, u8 *tbtt_count, size_t *len,
u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
- u8 op_class, bool mld_update)
+ u8 op_class, u8 channel,
+ u8 match_idx)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_data *bss = iface->bss[i];
u8 bss_param = 0;
- bool ap_mld = false;
- u8 *eid = *pos;
-
#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
+ bool ap_mld = false;
#endif /* CONFIG_IEEE80211BE */
+ u8 *eid = *pos;
- if (!bss || !bss->conf || !bss->started ||
- bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
+ if (!bss || !bss->conf || bss == reporting_hapd)
return false;
- if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
- mld_update, reporting_hapd, bss))
- return false;
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!bss->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
- if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ if (*len + tbtt_info_len > 255 ||
*tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
return true;
@@ -7683,7 +9999,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
*tbtt_count_pos = eid++;
*eid++ = tbtt_info_len;
*eid++ = op_class;
- *eid++ = bss->iconf->channel;
+ *eid++ = channel;
*len += RNR_TBTT_HEADER_LEN;
}
@@ -7701,7 +10017,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
}
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ if (is_6ghz_op_class(op_class) &&
bss->conf->unsol_bcast_probe_resp_interval)
bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
@@ -7711,24 +10027,24 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
#ifdef CONFIG_IEEE80211BE
- if (ap_mld) {
+ /* Include the MLD parameters only when TBTT length is for ML RNR */
+ if (ap_mld && tbtt_info_len == RNR_TBTT_INFO_MLD_LEN) {
u8 param_ch = bss->eht_mld_bss_param_change;
- bool is_partner;
- /* If BSS is not a partner of the reporting_hapd
+ /* If BSS is not a partner of the reporting_hapd or
+ * it is one of the nontransmitted hapd,
* a) MLD ID advertised shall be 255.
* b) Link ID advertised shall be 15.
* c) BPCC advertised shall be 255 */
- is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
/* MLD ID */
- *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
+ *eid++ = match_idx;
/* Link ID (Bit 3 to Bit 0)
* BPCC (Bit 4 to Bit 7) */
- *eid++ = is_partner ?
+ *eid++ = match_idx < 255 ?
bss->mld_link_id | ((param_ch & 0xF) << 4) :
(MAX_NUM_MLD_LINKS | 0xF0);
/* BPCC (Bit 3 to Bit 0) */
- *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
+ *eid = match_idx < 255 ? ((param_ch & 0xF0) >> 4) : 0x0F;
#ifdef CONFIG_TESTING_OPTIONS
if (bss->conf->mld_indicate_disabled)
*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
@@ -7752,65 +10068,125 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
bool mld_update)
{
struct hostapd_iface *iface = hapd->iface;
- size_t i, start;
+ size_t i;
size_t len = *current_len;
u8 *eid_start = eid, *size_offset = (eid - len) + 1;
u8 *tbtt_count_pos = size_offset + 1;
- u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
+ u8 total_tbtt_count = 0;
u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
RNR_TBTT_INFO_LEN;
+ bool reporting_ap_mld = false;
+ bool have_pending_group;
+ u8 pending_op_class = 0, pending_channel = 0;
+ bool *tbtt_added = NULL;
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
return eid;
- if (ieee80211_freq_to_channel_ext(iface->freq,
- hapd->iconf->secondary_channel,
- hostapd_get_oper_chwidth(hapd->iconf),
- &op_class, &channel) ==
- NUM_HOSTAPD_MODES)
- return eid;
+#ifdef CONFIG_IEEE80211BE
+ reporting_ap_mld = !!reporting_hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
repeat_rnr:
- start = 0;
- tbtt_count = 0;
- while (start < iface->num_bss) {
+ os_free(tbtt_added);
+ tbtt_added = os_zalloc(iface->num_bss);
+ if (!tbtt_added)
+ return eid;
+
+ have_pending_group = false;
+ for (;;) {
+ u8 tbtt_count = 0;
+ bool group_found = false, group_pending = false;
+ u8 rnr_op_class = 0, rnr_channel = 0;
+
+ if (have_pending_group) {
+ rnr_op_class = pending_op_class;
+ rnr_channel = pending_channel;
+ group_found = true;
+ }
+
+ if (!group_found) {
+ for (i = 0; i < iface->num_bss; i++) {
+ if (tbtt_added[i])
+ continue;
+ if (!hostapd_rnr_get_bss_info(
+ hapd, reporting_hapd, skip_profiles,
+ i, tbtt_info_len, mld_update,
+ &rnr_op_class, &rnr_channel, NULL))
+ continue;
+ group_found = true;
+ break;
+ }
+ }
+
+ if (!group_found)
+ break;
+
if (!len ||
- len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255) {
eid_start = eid;
*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
size_offset = eid++;
len = RNR_HEADER_LEN;
- tbtt_count = 0;
}
- for (i = start; i < iface->num_bss; i++) {
- if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
- skip_profiles, i,
+ for (i = 0; i < iface->num_bss; i++) {
+ u8 op_class, channel, match_idx = 255;
+
+ if (tbtt_added[i])
+ continue;
+
+ if (!hostapd_rnr_get_bss_info(
+ hapd, reporting_hapd, skip_profiles,
+ i, tbtt_info_len, mld_update,
+ &op_class, &channel, &match_idx))
+ continue;
+
+ if (rnr_op_class != op_class ||
+ rnr_channel != channel)
+ continue;
+
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd, i,
&tbtt_count, &len, &eid,
&tbtt_count_pos, tbtt_info_len,
- op_class, mld_update))
+ op_class, channel,
+ match_idx)) {
+ group_pending = true;
break;
- }
+ }
- start = i;
+ tbtt_added[i] = true;
+ }
if (tbtt_count) {
*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
*size_offset = (eid - size_offset) - 1;
+ } else {
+ break;
}
- }
- total_tbtt_count += tbtt_count;
+ total_tbtt_count += tbtt_count;
+
+ if (group_pending) {
+ have_pending_group = true;
+ pending_op_class = rnr_op_class;
+ pending_channel = rnr_channel;
+ } else {
+ have_pending_group = false;
+ }
+ }
/* If building for co-location, re-build again but this time include
- * ML TBTTs.
+ * ML TBTTs if the reporting AP is affiliated with an AP MLD.
*/
- if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
+ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN &&
+ reporting_ap_mld) {
tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
goto repeat_rnr;
}
+ os_free(tbtt_added);
+
if (!total_tbtt_count)
return eid_start;
@@ -7819,8 +10195,8 @@ repeat_rnr:
}
-u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
- size_t *current_len)
+static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t i;
@@ -7844,14 +10220,16 @@ u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
}
-u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
- u8 *eid, size_t *current_len)
+static u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ u8 *eid,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t *current_len)
{
#ifdef CONFIG_IEEE80211BE
struct hostapd_iface *iface;
size_t i;
- if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ if (!hapd->iface || !hapd->iface->interfaces)
return eid;
/* TODO: Allow for FILS/Action as well */
@@ -7866,7 +10244,7 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
continue;
eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
- current_len, NULL, true);
+ current_len, skip_profiles, true);
}
#endif /* CONFIG_IEEE80211BE */
@@ -7910,7 +10288,7 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
if (include_mld_params &&
(type != WLAN_FC_STYPE_BEACON ||
hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
- eid = hostapd_eid_rnr_mlo(hapd, type, eid, &current_len);
+ eid = hostapd_eid_rnr_mlo(hapd, type, eid, NULL, &current_len);
if (eid == eid_start + 2)
return eid_start;
@@ -7956,8 +10334,8 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
size_t known_bss_len)
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- size_t len, i;
- u8 ext_capa[20];
+ size_t len, i, tx_xrate_len;
+ u8 ext_capa[20], buf[100];
/* Element ID: 1 octet
* Length: 1 octet
@@ -7970,10 +10348,12 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
*/
len = 1;
+ tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
for (i = *bss_index; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
const u8 *auth, *rsn = NULL, *rsnx = NULL;
- size_t nontx_profile_len, auth_len;
+ size_t nontx_profile_len, auth_len, xrate_len;
u8 ie_count = 0;
if (!bss || !bss->conf || !bss->started ||
@@ -8011,12 +10391,25 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
ie_count++;
if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
ie_count++;
- if (bss->conf->xrates_supported)
- nontx_profile_len += 8;
- else if (hapd->conf->xrates_supported)
+
+ xrate_len = hostapd_eid_ext_supp_rates(bss, buf) - buf;
+
+ if (xrate_len)
+ nontx_profile_len += xrate_len;
+ else if (tx_xrate_len)
ie_count++;
+
+#ifdef CONFIG_IEEE80211BE
+ /* For ML Probe Response frame, the solicited hapd's MLE will
+ * be in the frame body */
+ if (bss->conf->mld_ap &&
+ (bss != hapd || frame_type != WLAN_FC_STYPE_PROBE_RESP))
+ nontx_profile_len += hostapd_eid_eht_basic_ml_len(
+ bss, NULL, true, false);
+#endif /* CONFIG_IEEE80211BE */
+
if (ie_count)
- nontx_profile_len += 4 + ie_count;
+ nontx_profile_len += 4 + ie_count + 1;
if (len + nontx_profile_len > 255)
break;
@@ -8031,22 +10424,34 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
}
-size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd_probed, u32 frame_type,
u8 *elem_count, const u8 *known_bss,
size_t known_bss_len, size_t *rnr_len)
{
+ struct hostapd_data *hapd = hostapd_mbssid_get_tx_bss(hapd_probed);
size_t len = 0, bss_index = 1;
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
- if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ if (!hapd->iconf->mbssid ||
(frame_type != WLAN_FC_STYPE_BEACON &&
frame_type != WLAN_FC_STYPE_PROBE_RESP))
return 0;
+ /*
+ * Include the Multiple BSSID element whenever MBSSID is enabled. The
+ * element may include zero or more nontransmitted BSSID profiles.
+ */
+ if (hapd->iface->num_bss == 1) {
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ if (!elem_count) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return 0;
+ }
+ *elem_count = 1;
+ }
+ return 3;
+ }
+
if (frame_type == WLAN_FC_STYPE_BEACON) {
if (!elem_count) {
wpa_printf(MSG_INFO,
@@ -8059,7 +10464,7 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
while (bss_index < hapd->iface->num_bss) {
size_t rnr_count = bss_index;
- len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+ len += hostapd_eid_mbssid_elem_len(hapd_probed, frame_type,
&bss_index, known_bss,
known_bss_len);
@@ -8073,7 +10478,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
*rnr_len += hostapd_eid_rnr_iface_len(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- &rnr_cur_len, &skip_profiles, ap_mld);
+ &rnr_cur_len, &skip_profiles, false);
+
+ *rnr_len += hostapd_eid_rnr_mlo_len(
+ hostapd_mbssid_get_tx_bss(hapd), frame_type,
+ &skip_profiles, &rnr_cur_len);
}
}
@@ -8090,21 +10499,26 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
const u8 *known_bss, size_t known_bss_len)
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- size_t i;
+ size_t i, tx_xrate_len;
u8 *eid_len_offset, *max_bssid_indicator_offset;
+ u8 buf[100];
*eid++ = WLAN_EID_MULTIPLE_BSSID;
eid_len_offset = eid++;
max_bssid_indicator_offset = eid++;
+ tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
for (i = *bss_index; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
struct hostapd_bss_config *conf;
+ struct hostapd_bss_config *tx_conf = tx_bss->conf;
u8 *eid_len_pos, *nontx_bss_start = eid;
const u8 *auth, *rsn = NULL, *rsnx = NULL;
u8 ie_count = 0, non_inherit_ie[3];
- size_t auth_len = 0;
+ size_t auth_len = 0, xrate_len;
u16 capab_info;
+ u8 mbssindex = i;
if (!bss || !bss->conf || !bss->started ||
mbssid_known_bss(i, known_bss, known_bss_len))
@@ -8125,10 +10539,14 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
eid += conf->ssid.ssid_len;
+ if (conf->mbssid_index &&
+ conf->mbssid_index > tx_conf->mbssid_index)
+ mbssindex = conf->mbssid_index - tx_conf->mbssid_index;
+
*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
if (frame_type == WLAN_FC_STYPE_BEACON) {
*eid++ = 3;
- *eid++ = i; /* BSSID Index */
+ *eid++ = mbssindex; /* BSSID Index */
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
(conf->dtim_period % elem_count))
conf->dtim_period = elem_count;
@@ -8144,7 +10562,7 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
/* Probe Request frame does not include DTIM Period and
* DTIM Count fields. */
*eid++ = 1;
- *eid++ = i; /* BSSID Index */
+ *eid++ = mbssindex; /* BSSID Index */
}
auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
@@ -8163,15 +10581,24 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
}
eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
+ xrate_len = hostapd_eid_ext_supp_rates(bss, eid) - eid;
+ eid += xrate_len;
/* List of Element ID values in increasing order */
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
non_inherit_ie[ie_count++] = WLAN_EID_RSN;
- if (hapd->conf->xrates_supported &&
- !bss->conf->xrates_supported)
+ if (tx_xrate_len && !xrate_len)
non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+#ifdef CONFIG_IEEE80211BE
+ /* For ML Probe Response frame, the solicited hapd's MLE will
+ * be in the frame body */
+ if (bss->conf->mld_ap &&
+ (bss != hapd || frame_type != WLAN_FC_STYPE_PROBE_RESP))
+ eid = hostapd_eid_eht_basic_ml_common(bss, eid, NULL,
+ true, false);
+#endif /* CONFIG_IEEE80211BE */
if (ie_count) {
*eid++ = WLAN_EID_EXTENSION;
*eid++ = 2 + ie_count + 1;
@@ -8199,21 +10626,18 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
}
-u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd_probed, u8 *eid, u8 *end,
unsigned int frame_stype, u8 elem_count,
u8 **elem_offset,
const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
u8 *rnr_count, u8 **rnr_offset, size_t rnr_len)
{
+ struct hostapd_data *hapd = hostapd_mbssid_get_tx_bss(hapd_probed);
size_t bss_index = 1, cur_len = 0;
u8 elem_index = 0, *rnr_start_eid = rnr_eid;
- bool add_rnr, ap_mld = false;
+ bool add_rnr;
-#ifdef CONFIG_IEEE80211BE
- ap_mld = hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
-
- if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ if (!hapd->iconf->mbssid ||
(frame_stype != WLAN_FC_STYPE_BEACON &&
frame_stype != WLAN_FC_STYPE_PROBE_RESP))
return eid;
@@ -8228,6 +10652,20 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
frame_stype == WLAN_FC_STYPE_BEACON &&
rnr_eid && rnr_count && rnr_offset && rnr_len;
+ /*
+ * Include the Multiple BSSID element whenever MBSSID is enabled. The
+ * element may include zero or more nontransmitted BSSID profiles.
+ */
+ if (hapd->iface->num_bss == 1) {
+ if (frame_stype == WLAN_FC_STYPE_BEACON)
+ elem_offset[0] = eid;
+
+ return hostapd_eid_mbssid_elem(
+ hapd_probed, eid, end, frame_stype,
+ hostapd_max_bssid_indicator(hapd),
+ &bss_index, elem_count, known_bss, known_bss_len);
+ }
+
while (bss_index < hapd->iface->num_bss) {
unsigned int rnr_start_count = bss_index;
@@ -8241,7 +10679,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
elem_offset[elem_index] = eid;
elem_index = elem_index + 1;
}
- eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+ eid = hostapd_eid_mbssid_elem(hapd_probed, eid, end,
+ frame_stype,
hostapd_max_bssid_indicator(hapd),
&bss_index, elem_count,
known_bss, known_bss_len);
@@ -8256,7 +10695,10 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
cur_len = 0;
rnr_eid = hostapd_eid_rnr_iface(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- rnr_eid, &cur_len, &skip_profiles, ap_mld);
+ rnr_eid, &cur_len, &skip_profiles, false);
+ rnr_eid = hostapd_eid_rnr_mlo(
+ hostapd_mbssid_get_tx_bss(hapd), frame_stype,
+ rnr_eid, &skip_profiles, &cur_len);
}
}
diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h
index dd4995f3f31a..664d585b994c 100644
--- a/contrib/wpa/src/ap/ieee802_11.h
+++ b/contrib/wpa/src/ap/ieee802_11.h
@@ -9,6 +9,8 @@
#ifndef IEEE802_11_H
#define IEEE802_11_H
+#include "utils/list.h"
+
struct hostapd_iface;
struct hostapd_data;
struct sta_info;
@@ -24,6 +26,36 @@ struct sae_pk;
struct sae_pt;
struct sae_password_entry;
struct mld_info;
+struct mld_link_info;
+struct rsn_pmksa_cache_entry;
+
+enum link_parse_type {
+ LINK_PARSE_ASSOC,
+ LINK_PARSE_REASSOC,
+ LINK_PARSE_RECONF,
+};
+
+#define LINK_RECONF_GROUP_KDE_MAX_LEN 255
+
+struct link_reconf_req_info {
+ struct dl_list list;
+ u16 status;
+ u8 link_id;
+ u8 local_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ size_t sta_prof_len;
+ u8 sta_prof[];
+};
+
+struct link_reconf_req_list {
+ u8 sta_mld_addr[ETH_ALEN];
+ u8 dialog_token;
+ u16 links_add_ok;
+ u16 links_del_ok;
+ u16 new_valid_links;
+ struct dl_list del_req; /* list of struct link_reconf_req_info */
+ struct dl_list add_req; /* list of struct link_reconf_req_info */
+};
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -63,7 +95,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode);
@@ -94,6 +126,12 @@ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
u8 *eid, bool include_mld_id);
u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
u8 *eid);
+u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ u8 *eid, struct mld_info *mld_info,
+ bool include_mld_id, bool include_bpcc);
+size_t hostapd_eid_eht_basic_ml_len(struct hostapd_data *hapd,
+ struct sta_info *info,
+ bool include_mld_id, bool include_pbcc);
size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
struct mld_info *info,
bool include_mld_id);
@@ -130,6 +168,8 @@ u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd);
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, const u8 *end);
u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
@@ -250,9 +290,12 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
u8 **elem_offset,
const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
-bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+bool hostapd_is_multiple_link_mld(struct hostapd_data *hapd);
+int sae_password_bind(struct hostapd_data *hapd, const u8 *addr,
+ const char *password);
const char * sae_get_password(struct hostapd_data *hapd,
- struct sta_info *sta, const char *rx_id,
+ struct sta_info *sta, const u8 *rx_id,
+ size_t rx_id_len,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt, const struct sae_pk **s_pk);
struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
@@ -262,6 +305,33 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *ies, size_t ies_len,
bool reassoc, int tx_link_status,
- bool offload);
+ bool offload,
+ bool *set_beacon);
+
+void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr);
+int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ struct sta_info *origin_sta,
+ struct mld_link_info *link,
+ const u8 *ies, size_t ies_len,
+ enum link_parse_type type, bool offload,
+ bool *set_beacon);
+void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ struct mld_link_info *link);
+
+void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
+void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok);
+
+size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 *eid);
+
+void ieee80211_send_eap_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 type, u16 auth_transaction, u16 status,
+ struct rsn_pmksa_cache_entry *cached_pmk,
+ const u8 *eap_req, size_t eap_req_len);
#endif /* IEEE802_11_H */
diff --git a/contrib/wpa/src/ap/ieee802_11_eht.c b/contrib/wpa/src/ap/ieee802_11_eht.c
index b935ee889a89..02e360c23f40 100644
--- a/contrib/wpa/src/ap/ieee802_11_eht.c
+++ b/contrib/wpa/src/ap/ieee802_11_eht.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11be EHT
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -8,10 +8,14 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
#include "crypto/crypto.h"
#include "crypto/dh_groups.h"
#include "hostapd.h"
#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
#include "ieee802_11.h"
@@ -44,34 +48,14 @@ static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- int he_oper_chwidth, const u8 *he_phy_cap,
+ const u8 *he_phy_cap,
const u8 *eht_phy_cap)
{
u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
bool band24, band5, band6;
- u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
u8 cap_chwidth;
- switch (he_oper_chwidth) {
- case CONF_OPER_CHWIDTH_80P80MHZ:
- he_phy_cap_chwidth |=
- HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
- /* fall through */
- case CONF_OPER_CHWIDTH_160MHZ:
- he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
- /* fall through */
- case CONF_OPER_CHWIDTH_80MHZ:
- case CONF_OPER_CHWIDTH_USE_HT:
- he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
- break;
- }
-
cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
- if (he_oper_chwidth != -1)
- he_phy_cap_chwidth &= cap_chwidth;
- else
- he_phy_cap_chwidth = cap_chwidth;
band24 = mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G ||
@@ -81,18 +65,18 @@ static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
band6 = is_6ghz_op_class(opclass);
if (band24 &&
- (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+ (cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap_chwidth &
+ (cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
if (band5 &&
- (he_phy_cap_chwidth &
+ (cap_chwidth &
(HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
@@ -122,7 +106,6 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
return 0;
len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
@@ -177,7 +160,6 @@ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
eht_cap->phy_cap);
if (mcs_nss_len) {
@@ -241,6 +223,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
oper->basic_eht_mcs_nss_set[2] = 0x00;
oper->basic_eht_mcs_nss_set[3] = 0x00;
+ if (hapd->iconf->disable_mcs15_rx)
+ oper->oper_params |= EHT_OPER_MCS15_DISABLE;
+
if (!eht_oper_info_present)
return pos + elen;
@@ -332,7 +317,6 @@ static bool check_valid_eht_mcs(struct hostapd_data *hapd,
sta_mcs = capab->optional;
if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
mode->he_capab[opmode].phy_cap,
mode->eht_capab[opmode].phy_cap) ==
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
@@ -374,7 +358,7 @@ static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap,
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap,
cap->phy_cap);
if (len < cap_len)
return true;
@@ -395,13 +379,14 @@ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
- if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
+ if (!hostapd_is_eht_enabled(hapd) ||
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
he_capab, eht_capab,
eht_capab_len) ||
- !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
+ !check_valid_eht_mcs(hapd, eht_capab, opmode) ||
+ !(sta->flags & WLAN_STA_HE)) {
sta->flags &= ~WLAN_STA_EHT;
os_free(sta->eht_capab);
sta->eht_capab = NULL;
@@ -439,9 +424,20 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd,
}
-static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
- u8 *eid, struct mld_info *mld_info,
- bool include_mld_id)
+/* Beacon or a non ML Probe Response frame should include
+ * Common Info Length(1) + MLD MAC Address(6) +
+ * Link ID Info(1) + BSS Parameters Change count(1) +
+ * EML Capabilities (2) + MLD Capabilities (2)
+ */
+#define EHT_ML_COMMON_INFO_LEN 13
+/*
+ * control (2) + station info length (1) + MAC address (6) +
+ * beacon interval (2) + TSF offset (8) + DTIM info (2)
+ */
+#define EHT_ML_STA_INFO_LEN 21
+u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ u8 *eid, struct mld_info *mld_info,
+ bool include_mld_id, bool include_bpcc)
{
struct wpabuf *buf;
u16 control;
@@ -452,6 +448,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
u8 common_info_len;
u16 mld_cap;
u8 max_simul_links, active_links;
+ u16 mld_eml_capa;
/*
* As the Multi-Link element can exceed the size of 255 bytes need to
@@ -475,7 +472,6 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
* BSS Parameters Change Count (1) + EML Capabilities (2) +
* MLD Capabilities and Operations (2)
*/
-#define EHT_ML_COMMON_INFO_LEN 13
common_info_len = EHT_ML_COMMON_INFO_LEN;
if (include_mld_id) {
@@ -494,16 +490,22 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
/* Own Link ID */
wpabuf_put_u8(buf, hapd->mld_link_id);
- /* Currently hard code the BSS Parameters Change Count to 0x1 */
- wpabuf_put_u8(buf, 0x1);
+ wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+
+ /* Set EMLSR/EMLMR Transition and Padding delay to zero for AP MLD as
+ * per IEEE Std 802.11be-2024, 9.4.2.322.2.3 (Common Info field of the
+ * Basic Multi-Link element).
+ */
+ mld_eml_capa = hapd->iface->mld_eml_capa &
+ ~(EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK |
+ EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK);
- wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
- hapd->iface->mld_eml_capa);
- wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
+ wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x", mld_eml_capa);
+ wpabuf_put_le16(buf, mld_eml_capa);
mld_cap = hapd->iface->mld_mld_capa;
max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
- active_links = hapd->mld->num_links - 1;
+ active_links = hostapd_get_active_links(hapd);
if (active_links > max_simul_links) {
wpa_printf(MSG_ERROR,
@@ -518,6 +520,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
/* TODO: Advertise T2LM based on driver support as well */
mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
+ mld_cap |= EHT_ML_MLD_CAPA_LINK_RECONF_OP_SUPPORT;
+
wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
mld_cap);
wpabuf_put_le16(buf, mld_cap);
@@ -534,16 +538,9 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
/* Add link info for the other links */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct mld_link_info *link = &mld_info->links[link_id];
+ size_t sta_info_len = EHT_ML_STA_INFO_LEN;
struct hostapd_data *link_bss;
-
- /*
- * control (2) + station info length (1) + MAC address (6) +
- * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
- * parameters change counter (1) + station profile length.
- */
-#define EHT_ML_STA_INFO_LEN 22
- size_t total_len = EHT_ML_STA_INFO_LEN +
- link->resp_sta_profile_len;
+ size_t total_len;
/* Skip the local one */
if (link_id == hapd->mld_link_id || !link->valid)
@@ -556,8 +553,15 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
continue;
}
+ /* BSS Parameters Change Count (1) for (Re)Association Response
+ * frames */
+ if (include_bpcc)
+ sta_info_len++;
+
+ total_len = sta_info_len + link->resp_sta_profile_len;
+
/* Per-STA Profile subelement */
- wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
+ wpabuf_put_u8(buf, MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE);
if (total_len <= 255)
wpabuf_put_u8(buf, total_len);
@@ -566,18 +570,21 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
/* STA Control */
control = (link_id & 0xf) |
- EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK |
- EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
- EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
- EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
- EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
- EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+ BASIC_MLE_STA_CTRL_PRES_STA_MAC |
+ BASIC_MLE_STA_CTRL_COMPLETE_PROFILE |
+ BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET |
+ BASIC_MLE_STA_CTRL_PRES_BEACON_INT |
+ BASIC_MLE_STA_CTRL_PRES_DTIM_INFO;
+
+ if (include_bpcc)
+ control |= BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT;
+
wpabuf_put_le16(buf, control);
/* STA Info */
/* STA Info Length */
- wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
+ wpabuf_put_u8(buf, sta_info_len - 2);
wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
@@ -593,7 +600,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
wpabuf_put_u8(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
- wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ if (include_bpcc)
+ wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
if (!link->resp_sta_profile)
continue;
@@ -606,7 +614,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
ptr = link->resp_sta_profile;
len = link->resp_sta_profile_len;
- slice_len = 255 - EHT_ML_STA_INFO_LEN;
+ slice_len = 255 - sta_info_len;
wpabuf_put_data(buf, ptr, slice_len);
len -= slice_len;
@@ -618,7 +626,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
else
slice_len = 255;
- wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT);
+ wpabuf_put_u8(buf,
+ MULTI_LINK_SUB_ELEM_ID_FRAGMENT);
wpabuf_put_u8(buf, slice_len);
wpabuf_put_data(buf, ptr, slice_len);
@@ -667,6 +676,75 @@ out:
}
+size_t hostapd_eid_eht_basic_ml_len(struct hostapd_data *hapd,
+ struct sta_info *info,
+ bool include_mld_id, bool include_bpcc)
+{
+ int link_id;
+ size_t len, num_frags;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Include WLAN_EID_EXT_MULTI_LINK (1) */
+ len = 1;
+ /* control field */
+ len += 2;
+ /* Common info len for Basic MLE */
+ len += EHT_ML_COMMON_INFO_LEN;
+ if (include_mld_id)
+ len++;
+
+ if (!info)
+ goto out;
+
+ /* Add link info for the other links */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->mld_info.links[link_id];
+ struct hostapd_data *link_bss;
+ size_t sta_prof_len = EHT_ML_STA_INFO_LEN +
+ link->resp_sta_profile_len;
+
+ /* Skip the local one */
+ if (link_id == hapd->mld_link_id || !link->valid)
+ continue;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!link_bss) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Couldn't find link BSS - skip it");
+ continue;
+ }
+
+ /* BSS Parameters Change Count (1) for (Re)Association Response
+ * frames */
+ if (include_bpcc)
+ sta_prof_len++;
+
+ /* Per-STA Profile Subelement(1), Length (1) */
+ len += 2;
+ len += sta_prof_len;
+ /* Consider Fragment EID(1) and Length (1) for each subelement
+ * fragment. */
+ if (sta_prof_len > 255) {
+ num_frags = (sta_prof_len / 255 - 1) +
+ !!(sta_prof_len % 255);
+ len += num_frags * 2;
+ }
+
+ }
+
+out:
+ if (len > 255) {
+ num_frags = (len / 255 - 1) + !!(len % 255);
+ len += num_frags * 2;
+ }
+
+ /* WLAN_EID_EXTENSION (1) + length (1) */
+ return len + 2;
+}
+
+
static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
{
#ifdef CONFIG_TESTING_OPTIONS
@@ -741,7 +819,7 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
- bool include_mld_id)
+ bool include_mld_id, bool include_bpcc)
{
size_t len = 0;
size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
@@ -761,19 +839,24 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
sta_len += link->resp_sta_profile_len;
+ /* BSS Parameters Change Count (1) for (Re)Association Response
+ * frames */
+ if (include_bpcc)
+ sta_len++;
+
/* Element data and (fragmentation) headers */
eht_ml_len += sta_len;
- eht_ml_len += 2 + sta_len / 255 * 2;
+ eht_ml_len += 2 + (sta_len - 1) / 255 * 2;
}
- /* Element data */
- len += eht_ml_len;
+ /* EID_EXT_MULTI_LINK (1) + Element data */
+ len += 1 + eht_ml_len;
- /* First header (254 bytes of data) */
- len += 3;
+ /* Fragmentation headers */
+ len += (len - 1) / 255 * 2;
- /* Fragmentation headers; +1 for shorter first chunk */
- len += (eht_ml_len + 1) / 255 * 2;
+ /* Outer header EID_EXT (1) + length (1) */
+ len += 2;
return len;
}
@@ -785,7 +868,8 @@ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
struct mld_info *info,
u8 *eid, bool include_mld_id)
{
- eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id);
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id,
+ false);
return hostapd_eid_eht_reconf_ml(hapd, eid);
}
@@ -798,7 +882,7 @@ u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
return eid;
eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
- false);
+ false, true);
ap_sta_free_sta_profile(&info->mld_info);
return hostapd_eid_eht_reconf_ml(hapd, eid);
}
@@ -808,7 +892,7 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
struct mld_info *info,
bool include_mld_id)
{
- return hostapd_eid_eht_ml_len(info, include_mld_id);
+ return hostapd_eid_eht_ml_len(info, include_mld_id, false);
}
@@ -871,6 +955,8 @@ sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
+ if (len - 2 < prime_len * (ec ? 3 : 2))
+ goto truncated;
/* scalar */
pos += prime_len;
@@ -882,6 +968,7 @@ sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
}
if (pos - mgmt->u.auth.variable > (int) len) {
+ truncated:
wpa_printf(MSG_DEBUG,
"EHT: Too short SAE commit Authentication frame");
return NULL;
@@ -905,16 +992,38 @@ sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
return pos;
/* send confirm integer */
+ if (len < 2)
+ goto truncated;
pos += 2;
/*
* At this stage we should already have an MLD station and actually SA
- * will be replaced with the MLD MAC address by the driver.
+ * will be replaced with the MLD MAC address by the driver. However,
+ * there is at least a theoretical race condition in a case where the
+ * peer sends the SAE confirm message quickly enough for the driver
+ * translation mechanism to not be available to update the SAE confirm
+ * message addresses. Work around that by searching for the STA entry
+ * using the link address of the non-AP MLD if no match is found based
+ * on the MLD MAC address.
*/
sta = ap_get_sta(hapd, mgmt->sa);
if (!sta) {
wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
- return NULL;
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ int link_id = hapd->mld_link_id;
+
+ if (!sta->mld_info.mld_sta ||
+ sta->mld_info.links[link_id].valid ||
+ !ether_addr_equal(
+ mgmt->sa,
+ sta->mld_info.links[link_id].peer_addr))
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "SAE: Found MLD STA for SAE confirm based on link address");
+ break;
+ }
+ if (!sta)
+ return NULL;
}
if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
@@ -929,9 +1038,12 @@ sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
sta->sae->tmp->kck_len);
+ if (len - 2 < sta->sae->tmp->kck_len)
+ goto truncated;
pos += sta->sae->tmp->kck_len;
if (pos - mgmt->u.auth.variable > (int) len) {
+ truncated:
wpa_printf(MSG_DEBUG,
"EHT: Too short SAE confirm Authentication frame");
return NULL;
@@ -948,20 +1060,43 @@ static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
size_t len)
{
u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
-#ifdef CONFIG_SAE
+#if defined(CONFIG_SAE) || defined(CONFIG_IEEE8021X_AUTH)
u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
u16 status_code = le_to_host16(mgmt->u.auth.status_code);
-#endif /* CONFIG_SAE */
+#endif /* CONFIG_SAE or CONFIG_IEEE8021X_AUTH */
const u8 *pos = mgmt->u.auth.variable;
- /* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69
+ /* Skip fixed fields as based on IEEE Std 802.11-2024, Table 9-71
* (Presence of fields and elements in Authentications frames) */
switch (auth_alg) {
case WLAN_AUTH_OPEN:
+ case WLAN_AUTH_EPPKE:
return pos;
+#ifdef CONFIG_IEEE8021X_AUTH
+ case WLAN_AUTH_802_1X: {
+ u16 encap_len;
+
+ /* Extract 2-octet Encapsulation Length from variable field */
+ if (len < 2)
+ return NULL;
+
+ encap_len = WPA_GET_LE16(pos);
+ pos += 2;
+ len -= 2;
+
+ /* Skip Encapsulation field if present */
+ if (encap_len > 0) {
+ if (len < encap_len)
+ return NULL;
+ pos += encap_len;
+ }
+
+ return pos;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
#ifdef CONFIG_SAE
case WLAN_AUTH_SAE:
- if (auth_transaction == 1) {
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) {
if (status_code == WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"EHT: SAE H2E is mandatory for MLD");
@@ -970,7 +1105,7 @@ static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
return sae_commit_skip_fixed_fields(mgmt, len, pos,
status_code);
- } else if (auth_transaction == 2) {
+ } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) {
return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
pos, status_code);
}
@@ -1099,29 +1234,7 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
/* Common Info Length and MLD MAC Address must always be present */
common_info_len = 1 + ETH_ALEN;
-
- if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
- wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
- goto out;
- }
-
- if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
- wpa_printf(MSG_DEBUG,
- "MLD: BSS Parameters Change Count not expected");
- goto out;
- }
-
- if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
- wpa_printf(MSG_DEBUG,
- "MLD: Medium Synchronization Delay Information not expected");
- goto out;
- }
-
- if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
- common_info_len += 2;
-
- if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
- common_info_len += 2;
+ /* Ignore optional fields */
if (sizeof(*ml) + common_info_len > ml_len) {
wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
@@ -1131,7 +1244,7 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
common_info = (struct eht_ml_basic_common_info *) ml->variable;
/* Common information length includes the length octet */
- if (common_info->len != common_info_len) {
+ if (common_info->len < common_info_len) {
wpa_printf(MSG_DEBUG,
"MLD: Invalid common info len=%u", common_info->len);
goto out;
@@ -1157,9 +1270,11 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
size_t ml_len, common_info_len;
struct mld_link_info *link_info;
struct mld_info *info = &sta->mld_info;
- const u8 *pos;
+ const u8 *pos, *end;
int ret = -1;
u16 ml_control;
+ const u8 *ml_end;
+ u8 saved_peer_addr[ETH_ALEN];
mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
if (!mlbuf)
@@ -1167,6 +1282,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
ml = wpabuf_head(mlbuf);
ml_len = wpabuf_len(mlbuf);
+ ml_end = ((const u8 *) ml) + ml_len;
ml_control = le_to_host16(ml->ml_control);
if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
@@ -1208,7 +1324,13 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
goto out;
}
- wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+ common_info_len += 2;
+ } else {
+ wpa_printf(MSG_DEBUG, "MLD: EXT ML capabilities not present");
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%zu",
common_info_len);
if (sizeof(*ml) + common_info_len > ml_len) {
@@ -1219,7 +1341,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
common_info = (const struct eht_ml_basic_common_info *) ml->variable;
/* Common information length includes the length octet */
- if (common_info->len != common_info_len) {
+ if (common_info->len < common_info_len) {
wpa_printf(MSG_DEBUG,
"MLD: Invalid common info len=%u (expected %zu)",
common_info->len, common_info_len);
@@ -1227,6 +1349,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
}
pos = common_info->variable;
+ end = ((const u8 *) common_info) + common_info->len;
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
info->common_info.eml_capa = WPA_GET_LE16(pos);
@@ -1238,6 +1361,10 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
info->common_info.mld_capa = WPA_GET_LE16(pos);
pos += 2;
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+ pos += 2;
+ }
+
wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
MAC2STR(info->common_info.mld_addr),
info->common_info.eml_capa, info->common_info.mld_capa);
@@ -1253,23 +1380,58 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
goto out;
}
+ /* Reset all per-link state before parsing the new MLE. A STA may send
+ * back-to-back Reassociation Request frames with different MLEs (e.g.,
+ * fewer links or without NSTR). The valid-flag-only reset would leave
+ * stale fields (nstr_bitmap_len, peer_addr, nstr_bitmap, capability) in
+ * non-association link slots and leak resp_sta_profile heap
+ * allocations. Free those allocations and zero all link slots. The
+ * association link's peer_addr holds the link-specific MAC (set from
+ * mgmt->sa at auth time, before driver address translation) and must be
+ * saved and restored.
+ */
+ os_memcpy(saved_peer_addr, info->links[hapd->mld_link_id].peer_addr,
+ ETH_ALEN);
+ ap_sta_free_sta_profile(info);
+ os_memset(info->links, 0, sizeof(info->links));
info->links[hapd->mld_link_id].valid = 1;
+ os_memcpy(info->links[hapd->mld_link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
+ os_memcpy(info->links[hapd->mld_link_id].peer_addr,
+ saved_peer_addr, ETH_ALEN);
- /* Parse the link info field */
- ml_len -= sizeof(*ml) + common_info_len;
-
- while (ml_len > 2) {
- size_t sub_elem_len = *(pos + 1);
- size_t sta_info_len;
+ /* Parse the Link Info field that starts after the end of the variable
+ * length Common Info field. */
+ pos = end;
+ while (ml_end - pos > 2) {
+ size_t sub_elem_len, sta_info_len;
u16 control;
+ const u8 *sub_elem_end;
+ int num_frag_subelems;
+ u8 link_id;
- wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
- sub_elem_len);
+ num_frag_subelems =
+ ieee802_11_defrag_mle_subelem(mlbuf, pos,
+ &sub_elem_len);
+ if (num_frag_subelems < 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to parse MLE subelem");
+ goto out;
+ }
- if (2 + sub_elem_len > ml_len) {
+ if ((size_t) num_frag_subelems * 2 > ml_len)
+ goto out;
+ ml_len -= num_frag_subelems * 2;
+ ml_end = ((const u8 *) ml) + ml_len;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: sub element len=%zu, Fragment subelems=%u",
+ sub_elem_len, num_frag_subelems);
+
+ if (2 + sub_elem_len > (size_t) (ml_end - pos)) {
wpa_printf(MSG_DEBUG,
"MLD: Invalid link info len: %zu %zu",
- 2 + sub_elem_len, ml_len);
+ 2 + sub_elem_len, ml_end - pos);
goto out;
}
@@ -1278,7 +1440,6 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
"MLD: Skip vendor specific subelement");
pos += 2 + sub_elem_len;
- ml_len -= 2 + sub_elem_len;
continue;
}
@@ -1287,41 +1448,43 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
"MLD: Skip unknown Multi-Link element subelement ID=%u",
*pos);
pos += 2 + sub_elem_len;
- ml_len -= 2 + sub_elem_len;
continue;
}
/* Skip the subelement ID and the length */
pos += 2;
- ml_len -= 2;
+ sub_elem_end = pos + sub_elem_len;
/* Get the station control field */
- if (sub_elem_len < 2) {
+ if (sub_elem_end - pos < 2) {
wpa_printf(MSG_DEBUG,
"MLD: Too short Per-STA Profile subelement");
goto out;
}
control = WPA_GET_LE16(pos);
- link_info = &info->links[control &
- EHT_PER_STA_CTRL_LINK_ID_MSK];
+ link_id = control & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid Link ID in Per-STA Profile subelement");
+ goto out;
+ }
+ link_info = &info->links[link_id];
pos += 2;
- ml_len -= 2;
- sub_elem_len -= 2;
- if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+ if (!(control & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE)) {
wpa_printf(MSG_DEBUG,
"MLD: Per-STA complete profile expected");
goto out;
}
- if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+ if (!(control & BASIC_MLE_STA_CTRL_PRES_STA_MAC)) {
wpa_printf(MSG_DEBUG,
"MLD: Per-STA MAC address not present");
goto out;
}
- if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
- EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
+ if ((control & (BASIC_MLE_STA_CTRL_PRES_BEACON_INT |
+ BASIC_MLE_STA_CTRL_PRES_DTIM_INFO))) {
wpa_printf(MSG_DEBUG,
"MLD: Beacon/DTIM interval not expected");
goto out;
@@ -1330,8 +1493,8 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
/* The length octet and the MAC address must be present */
sta_info_len = 1 + ETH_ALEN;
- if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
- if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
+ if (control & BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR) {
+ if (control & BASIC_MLE_STA_CTRL_NSTR_BITMAP)
link_info->nstr_bitmap_len = 2;
else
link_info->nstr_bitmap_len = 1;
@@ -1339,45 +1502,42 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
sta_info_len += link_info->nstr_bitmap_len;
- if (sta_info_len > ml_len || sta_info_len != *pos ||
- sta_info_len > sub_elem_len) {
+ if (sta_info_len > (size_t) (sub_elem_end - pos) ||
+ sta_info_len > *pos ||
+ *pos > sub_elem_end - pos ||
+ sta_info_len > (size_t) (sub_elem_end - pos)) {
wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
goto out;
}
+ sta_info_len = *pos;
+ end = pos + sta_info_len;
+
/* skip the length */
pos++;
- ml_len--;
/* get the link address */
os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
wpa_printf(MSG_DEBUG,
"MLD: assoc: link id=%u, addr=" MACSTR,
- control & EHT_PER_STA_CTRL_LINK_ID_MSK,
+ control & BASIC_MLE_STA_CTRL_LINK_ID_MASK,
MAC2STR(link_info->peer_addr));
pos += ETH_ALEN;
- ml_len -= ETH_ALEN;
/* Get the NSTR bitmap */
if (link_info->nstr_bitmap_len) {
os_memcpy(link_info->nstr_bitmap, pos,
link_info->nstr_bitmap_len);
pos += link_info->nstr_bitmap_len;
- ml_len -= link_info->nstr_bitmap_len;
}
- sub_elem_len -= sta_info_len;
+ pos = end;
- wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
- if (sub_elem_len > ml_len)
- goto out;
-
- if (sub_elem_len > 2)
+ if (sub_elem_end - pos >= 2)
link_info->capability = WPA_GET_LE16(pos);
- pos += sub_elem_len;
- ml_len -= sub_elem_len;
+ pos = sub_elem_end;
wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
", nstr bitmap len=%u",
@@ -1387,19 +1547,1338 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
link_info->valid = true;
}
- if (ml_len) {
- wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
- ml_len);
- goto out;
- }
-
ret = hostapd_mld_validate_assoc_info(hapd, sta);
out:
wpabuf_free(mlbuf);
if (ret) {
+ ap_sta_free_sta_profile(info);
os_memset(info, 0, sizeof(*info));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
return WLAN_STATUS_SUCCESS;
}
+
+
+void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr)
+{
+ struct link_reconf_req_list *req_list;
+ struct link_reconf_req_info *info, *tmp;
+
+ if (!(*req_list_ptr))
+ return;
+
+ wpa_printf(MSG_DEBUG, "MLD: Deinit Link Reconf Request context");
+
+ req_list = *req_list_ptr;
+
+ dl_list_for_each_safe(info, tmp, &req_list->add_req,
+ struct link_reconf_req_info, list) {
+ dl_list_del(&info->list);
+ os_free(info);
+ }
+
+ dl_list_for_each_safe(info, tmp, &req_list->del_req,
+ struct link_reconf_req_info, list) {
+ dl_list_del(&info->list);
+ os_free(info);
+ }
+
+ os_free(req_list);
+ *req_list_ptr = NULL;
+}
+
+
+void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ u8 dialog_token = mgmt->u.action.u.link_reconf_resp.dialog_token;
+ struct hostapd_data *assoc_hapd, *lhapd, *other_hapd;
+ struct sta_info *assoc_sta, *lsta, *other_sta;
+ struct link_reconf_req_list *req_list;
+ struct link_reconf_req_info *info;
+ uint8_t link_id;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Link Reconf Response TX status - dialog token=%u ok=%d",
+ dialog_token, ok);
+
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta) {
+ wpa_printf(MSG_INFO, "MLD: Assoc STA not found for " MACSTR,
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (!assoc_sta->reconf_req) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Link Reconf Request TX status");
+ return;
+ }
+
+ req_list = assoc_sta->reconf_req;
+
+ if (!ether_addr_equal(mgmt->da, req_list->sta_mld_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Link Reconfiguration Response TX status from wrong STA");
+ return;
+ }
+
+ if (dialog_token != req_list->dialog_token) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Link Reconfiguration session expired for %u",
+ dialog_token);
+ return;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_INFO,
+ "MLD: Link Reconf Response ack failed for " MACSTR
+ "; revert link additions",
+ MAC2STR(mgmt->da));
+
+ dl_list_for_each(info, &req_list->add_req,
+ struct link_reconf_req_info, list) {
+ if (info->status != WLAN_STATUS_SUCCESS)
+ continue;
+
+ lhapd = NULL;
+ lsta = NULL;
+ lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
+ if (lhapd)
+ lsta = ap_get_sta(lhapd,
+ req_list->sta_mld_addr);
+
+ if (lsta)
+ ap_free_sta(lhapd, lsta);
+ }
+ goto exit;
+ }
+
+ if (dl_list_empty(&req_list->del_req))
+ goto exit;
+
+ dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
+ list) {
+ if (info->status != WLAN_STATUS_SUCCESS)
+ continue;
+
+ link_id = info->link_id;
+ lhapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!lhapd) {
+ wpa_printf(MSG_INFO,
+ "MLD: Link (%u) hapd cannot be NULL",
+ link_id);
+ continue;
+ }
+
+ lsta = ap_get_sta(lhapd, mgmt->da);
+ if (!lsta) {
+ wpa_printf(MSG_INFO,
+ "MLD: Link (%u) STA cannot be NULL",
+ link_id);
+ continue;
+ }
+
+ /* Reassign assoc_sta to the link with lowest link ID */
+ if (!hostapd_sta_is_link_sta(lhapd, lsta) &&
+ lsta == assoc_sta) {
+ struct mld_info *mld_info = &assoc_sta->mld_info;
+ int i;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (i == assoc_sta->mld_assoc_link_id ||
+ !mld_info->links[i].valid ||
+ req_list->links_del_ok & BIT(i)) {
+ continue;
+ }
+ break;
+ }
+
+ if (i == MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_INFO,
+ "MLD: No new assoc STA could be found; disconnect STA");
+ hostapd_notif_disassoc_mld(assoc_hapd,
+ assoc_sta,
+ assoc_sta->addr);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "MLD: New assoc link=%d", i);
+
+ /* Reset wpa_auth and assoc link ID */
+ for_each_mld_link(other_hapd, lhapd) {
+ other_sta = ap_get_sta(other_hapd, mgmt->da);
+ if (other_sta)
+ other_sta->mld_assoc_link_id = i;
+ }
+
+ /* Reset reconfig request queue which will be freed
+ * at the end */
+ assoc_sta->reconf_req = NULL;
+
+ /* assoc_sta switched */
+ assoc_sta = hostapd_ml_get_assoc_sta(lhapd, lsta,
+ &assoc_hapd);
+
+ /* assoc_sta cannot be NULL since both AP and STA are
+ * MLD and new valid assoc_sta is already found */
+ if (!assoc_sta)
+ goto exit;
+
+ if (assoc_hapd == lhapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: assoc_hapd is not updated; please check");
+ goto exit;
+ }
+
+ assoc_sta->reconf_req = req_list;
+ wpa_reset_assoc_sm_info(assoc_sta->wpa_sm,
+ assoc_hapd->wpa_auth, i);
+ }
+
+ /* Free as a link STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_LINK_STA_REMOVED "sta=" MACSTR " link_id=%u",
+ MAC2STR(lsta->addr), link_id);
+ ap_free_sta(lhapd, lsta);
+
+ for_each_mld_link(other_hapd, lhapd) {
+ struct mld_link_info *link;
+
+ other_sta = ap_get_sta(other_hapd, mgmt->da);
+ if (!other_sta)
+ continue;
+
+ link = &other_sta->mld_info.links[link_id];
+ os_free(link->resp_sta_profile);
+ link->resp_sta_profile = NULL;
+ link->resp_sta_profile_len = 0;
+ link->valid = false;
+ }
+ wpa_auth_set_ml_info(assoc_sta->wpa_sm,
+ assoc_sta->mld_assoc_link_id,
+ &assoc_sta->mld_info);
+ }
+
+exit:
+ ml_deinit_link_reconf_req(&req_list);
+ if (assoc_sta && assoc_sta->reconf_req)
+ assoc_sta->reconf_req = NULL;
+}
+
+
+static bool recover_from_zero_links(u16 *links_del_ok, u8 *recovery_link)
+{
+ u8 pos = 0;
+ u16 del_links;
+
+ del_links = *links_del_ok;
+
+ while (del_links) {
+ if (del_links & 1)
+ break;
+ del_links >>= 1;
+ pos++;
+ }
+
+ /* No link found */
+ if (!del_links) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Total valid links is 0 and no del-link found to reject for recovery");
+ return false;
+ }
+
+ *recovery_link = pos;
+ *links_del_ok &= ~BIT(*recovery_link);
+ wpa_printf(MSG_INFO,
+ "MLD: Del-link request for link (%u) rejected to recover from no remaining links",
+ *recovery_link);
+ return true;
+}
+
+
+static u16
+hostapd_ml_process_reconf_link(struct hostapd_data *hapd,
+ struct sta_info *assoc_sta, const u8 *ies,
+ size_t ies_len, u8 link_id, const u8 *link_addr)
+{
+ struct hostapd_data *lhapd, *other_hapd;
+ struct mld_link_info link;
+ struct sta_info *lsta, *other_sta;
+
+ lhapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!lhapd) /* This cannot be NULL */
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ os_memset(&link, 0, sizeof(link));
+
+ link.valid = 1;
+ os_memcpy(link.local_addr, lhapd->own_addr, ETH_ALEN);
+ os_memcpy(link.peer_addr, link_addr, ETH_ALEN);
+
+ /* Parse STA profile, check the IEs, and send ADD_LINK_STA */
+ ieee80211_ml_process_link(lhapd, assoc_sta, &link, ies, ies_len,
+ LINK_PARSE_RECONF, false, NULL);
+ if (link.status != WLAN_STATUS_SUCCESS)
+ return link.status;
+
+ lsta = ap_get_sta(lhapd, assoc_sta->addr);
+ if (!lsta)
+ return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+ for_each_mld_link(other_hapd, lhapd) {
+ struct mld_link_info *_link;
+
+ other_sta = ap_get_sta(other_hapd, lsta->addr);
+ if (!other_sta)
+ continue;
+
+ _link = &other_sta->mld_info.links[link_id];
+ _link->valid = true;
+ _link->status = WLAN_STATUS_SUCCESS;
+ os_memcpy(_link->local_addr, other_hapd->own_addr, ETH_ALEN);
+ os_memcpy(_link->peer_addr, link_addr, ETH_ALEN);
+ }
+ wpa_auth_set_ml_info(lsta->wpa_sm, lsta->mld_assoc_link_id,
+ &lsta->mld_info);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static int
+hostapd_reject_all_reconf_req(struct hostapd_data *hapd, u8 *pos,
+ struct link_reconf_req_list *req_list)
+{
+ struct link_reconf_req_info *info;
+ struct hostapd_data *lhapd;
+ struct sta_info *lsta;
+ u16 status;
+ u8 *buf = pos;
+
+ dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
+ list) {
+ lhapd = NULL;
+ lsta = NULL;
+ *pos++ = info->link_id;
+ status = info->status != WLAN_STATUS_SUCCESS ? info->status :
+ WLAN_STATUS_UNSPECIFIED_FAILURE;
+ WPA_PUT_LE16(pos, status);
+
+ if (info->status == WLAN_STATUS_SUCCESS) {
+ lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
+ if (lhapd)
+ lsta = ap_get_sta(lhapd,
+ req_list->sta_mld_addr);
+
+ if (lsta)
+ ap_free_sta(lhapd, lsta);
+
+ info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "MLD: Reject add-link=%u with status=%u",
+ info->link_id, status);
+ pos += 2;
+ }
+
+ dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
+ list) {
+ *pos++ = info->link_id;
+ status = info->status != WLAN_STATUS_SUCCESS ? info->status :
+ WLAN_STATUS_UNSPECIFIED_FAILURE;
+ WPA_PUT_LE16(pos, status);
+
+ if (info->status == WLAN_STATUS_SUCCESS)
+ info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reject del-link=%u with status=%u",
+ info->link_id, status);
+ pos += 2;
+ }
+
+ return pos - buf;
+}
+
+
+static int
+hostapd_send_link_reconf_resp(struct hostapd_data *hapd,
+ struct sta_info *assoc_sta,
+ struct link_reconf_req_list *req_list)
+{
+ u8 *buf, *orig_pos, *pos;
+ struct ieee80211_mgmt *mgmt;
+ struct link_reconf_req_info *info;
+ struct mld_info mld;
+ int ret;
+ unsigned int count;
+ u8 dialog_token;
+ bool reject_all = false;
+ size_t len, pos_len, kde_len, mle_len;
+
+ count = dl_list_len(&req_list->add_req) +
+ dl_list_len(&req_list->del_req);
+ if (!count)
+ return 0;
+
+ os_memset(&mld, 0, sizeof(mld));
+
+ dialog_token = req_list->dialog_token;
+
+ /*
+ * Link Reconfiguration Response:
+ *
+ * IEEE80211 Header (24B) +
+ * Category (1B) + Action code (1B) + Dialog Token (1B) +
+ * Count (1B) + Status list (count * 3B) +
+ * Optional: Group Key Data field (variable) +
+ * Optional: OCI element (6B) +
+ * Optional: Basic Multi-Link element (variable)
+ */
+ len = IEEE80211_HDRLEN + 3 + 1 + count * 3;
+ kde_len = mle_len = 0;
+
+ if (req_list->links_add_ok) {
+ kde_len = wpa_auth_ml_group_kdes_len(
+ assoc_sta->wpa_sm, req_list->links_add_ok) + 1;
+ len += kde_len;
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(assoc_sta->wpa_sm))
+ len += OCV_OCI_EXTENDED_LEN;
+#endif /* CONFIG_OCV */
+
+ mld.mld_sta = true;
+ dl_list_for_each(info, &req_list->add_req,
+ struct link_reconf_req_info, list) {
+ struct mld_link_info *link = &mld.links[info->link_id];
+ struct hostapd_data *lhapd = NULL;
+
+ if (info->status != WLAN_STATUS_SUCCESS)
+ continue;
+
+ link->status = info->status;
+
+ lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
+ if (!lhapd)
+ continue;
+
+ link->valid = true;
+ ieee80211_ml_build_assoc_resp(lhapd, link);
+ }
+ /* TODO: Basic MLE is not supposed to include BPCC in Link
+ * Reconfiguration Response, but mac80211 implementation for
+ * processing this frame requires that to be present. For now,
+ * include that subfield as a workaround. This should be removed
+ * once mac80211 is fixed to match the standard (or this comment
+ * be removed if the standard is modified to match
+ * implementation). */
+ mle_len = hostapd_eid_eht_ml_len(&mld, false, true);
+ len += mle_len;
+ }
+
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_INFO,
+ "MLD: Failed to allocate Link Reconf Response buffer (%zu bytes)",
+ len);
+ return -1;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, assoc_sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->mld->mld_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->mld->mld_addr, ETH_ALEN);
+
+ mgmt->u.action.category = WLAN_ACTION_PROTECTED_EHT;
+ mgmt->u.action.u.link_reconf_resp.action =
+ WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE;
+ mgmt->u.action.u.link_reconf_resp.dialog_token = dialog_token;
+ mgmt->u.action.u.link_reconf_resp.count = count;
+
+ orig_pos = pos = mgmt->u.action.u.link_reconf_resp.variable;
+ pos_len = 28; /* IEEE80211 Header, category, code, token, count */
+
+ dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
+ list) {
+ *pos++ = info->link_id;
+ WPA_PUT_LE16(pos, info->status);
+ pos += 2;
+ pos_len += 3;
+ }
+
+ dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
+ list) {
+ /* Mark the status as INVALID for rejected link to recover */
+ if (!(req_list->links_del_ok & BIT(info->link_id)) &&
+ info->status == WLAN_STATUS_SUCCESS)
+ info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ *pos++ = info->link_id;
+ WPA_PUT_LE16(pos, info->status);
+ pos += 2;
+ pos_len += 3;
+ }
+
+ if (!req_list->links_add_ok)
+ goto send_resp;
+
+ /* Key Data for add links */
+ if (kde_len) {
+ u8 *kde_pos = pos;
+
+ kde_pos = wpa_auth_ml_group_kdes(assoc_sta->wpa_sm, ++kde_pos,
+ req_list->links_add_ok);
+ *pos = kde_pos - pos - 1;
+ if (kde_len - 1 != *pos) {
+ reject_all = true;
+ goto reject_all_req;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "MLD: Group KDE", pos + 1, *pos);
+
+ pos += kde_len;
+ pos_len += kde_len;
+ }
+
+#ifdef CONFIG_OCV
+ /* OCI element for add links */
+ if (wpa_auth_uses_ocv(assoc_sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to fetch OCI; reject all requests");
+ reject_all = true;
+ goto reject_all_req;
+ }
+
+ if (ocv_insert_extended_oci(&ci, pos)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to add OCI element; reject all requests");
+ reject_all = true;
+ goto reject_all_req;
+ }
+
+ pos += OCV_OCI_EXTENDED_LEN;
+ pos_len += OCV_OCI_EXTENDED_LEN;
+ }
+#endif /* CONFIG_OCV */
+
+ /* Basic Multi-Link element for add links */
+ if (mle_len) {
+ u8 *mle_pos = pos;
+
+ /* TODO: Basic MLE is not supposed to include BPCC in Link
+ * Reconfiguration Response, but mac80211 implementation for
+ * processing this frame requires that to be present. For now,
+ * include that subfield as a workaround. This should be removed
+ * once mac80211 is fixed to match the standard (or this comment
+ * be removed if the standard is modified to match
+ * implementation). */
+ mle_pos = hostapd_eid_eht_basic_ml_common(hapd, mle_pos, &mld,
+ false, true);
+ if ((size_t) (mle_pos - pos) != mle_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected MLE length: %td != %zu",
+ mle_pos - pos, mle_len);
+ reject_all = true;
+ goto reject_all_req;
+ }
+
+ pos += mle_len;
+ pos_len += mle_len;
+ }
+
+reject_all_req:
+ if (reject_all) {
+ pos = orig_pos;
+ pos_len = 28; /* reset pos_len */
+ pos += hostapd_reject_all_reconf_req(hapd, orig_pos, req_list);
+ pos_len += pos - orig_pos;
+
+ req_list->links_add_ok = req_list->links_del_ok = 0;
+ req_list->new_valid_links = 0;
+ }
+
+send_resp:
+ ret = hostapd_drv_send_mlme(hapd, mgmt, pos_len, 0, NULL, 0, 0);
+ os_free(buf);
+
+ if (mld.mld_sta)
+ ap_sta_free_sta_profile(&mld);
+
+ return ret;
+}
+
+
+static int
+hostapd_ml_check_sta_entry_by_link_addr_iter(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ const u8 *link_addr = ctx;
+ struct mld_link_info li;
+
+ if (!link_addr)
+ return 0;
+
+ if (sta->mld_info.mld_sta) {
+ li = sta->mld_info.links[hapd->mld_link_id];
+ if (!li.valid || !ether_addr_equal(li.peer_addr, link_addr))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR
+ " exists for AP (link_id=%u) as a non-AP STA affiliated with non-AP MLD "
+ MACSTR, MAC2STR(link_addr),
+ hapd->mld_link_id, MAC2STR(sta->addr));
+ return 1;
+ }
+
+ if (ether_addr_equal(sta->addr, link_addr)) {
+ wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR
+ " exists for AP (link_id=%u) as a legacy STA",
+ MAC2STR(link_addr), hapd->mld_link_id);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Returns:
+ * 0 = successful parsing
+ * 1 = per-STA profile (subelement) skipped or rejected
+ * -1 = fail due to fatal errors
+ */
+static int
+hostapd_parse_link_reconf_req_sta_profile(struct hostapd_data *hapd,
+ struct link_reconf_req_list *req,
+ const u8 *buf, size_t len)
+{
+ struct link_reconf_req_info *info = NULL;
+ const struct ieee80211_eht_per_sta_profile *per_sta_prof;
+ const struct element *elem;
+ struct hostapd_data *lhapd = NULL;
+ struct sta_info *lsta;
+ size_t sta_info_len, sta_prof_len = 0;
+ u16 sta_control, reconf_type_mask;
+ u8 link_id, reconf_type;
+ const u8 *sta_info = NULL, *end;
+ u8 sta_addr[ETH_ALEN];
+ size_t nstr_bitmap_size = 0;
+ int ret = -1;
+ u16 status;
+
+ wpa_hexdump(MSG_MSGDUMP, "MLD: Link Reconfig Request subelement",
+ buf, len);
+
+ if (len < sizeof(*elem) + 2UL)
+ goto out;
+
+ elem = (const struct element *) buf;
+ end = buf + len;
+
+ os_memset(sta_addr, 0, ETH_ALEN);
+
+ if (elem->id != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ wpa_printf(MSG_DEBUG, "MLD: Unexpected subelement (%u) found",
+ elem->id);
+ ret = 1; /* skip this subelement */
+ goto out;
+ }
+
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ per_sta_prof = (const struct ieee80211_eht_per_sta_profile *)
+ elem->data;
+ sta_control = le_to_host16(per_sta_prof->sta_control);
+ sta_info_len = 1;
+
+ link_id = sta_control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
+ wpa_printf(MSG_DEBUG, "MLD: Per-STA profile for link=%u", link_id);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto out;
+
+ reconf_type_mask =
+ sta_control & EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK;
+ reconf_type =
+ EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_VAL(reconf_type_mask);
+
+ switch (reconf_type) {
+ case EHT_RECONF_TYPE_ADD_LINK:
+ case EHT_RECONF_TYPE_DELETE_LINK:
+ break;
+ default:
+ wpa_printf(MSG_ERROR,
+ "MLD: Unsupported Reconfiguration type %u",
+ reconf_type);
+ ret = 1; /* skip this per-STA profile */
+ goto out;
+ }
+
+ if (!(sta_control & EHT_PER_STA_RECONF_CTRL_MAC_ADDR)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: STA MAC address not set in STA control");
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+ sta_info_len += ETH_ALEN;
+
+ if (sta_control & EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: AP removal timer set in STA control");
+ sta_info_len += 2;
+ }
+
+ if (sta_control & EHT_PER_STA_RECONF_CTRL_OP_PARAMS) {
+ wpa_printf(MSG_DEBUG, "MLD: Op params set in STA control");
+ sta_info_len += 3;
+ }
+
+ if (!(sta_control & EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE)) {
+ if (reconf_type == EHT_RECONF_TYPE_ADD_LINK) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Complete profile not set in STA control");
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+ } else {
+ if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK)
+ wpa_printf(MSG_DEBUG,
+ "MLD: Complete profile set in STA control");
+ }
+
+ if (sta_control & EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION) {
+ nstr_bitmap_size = 1;
+ if (sta_control &
+ EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE)
+ nstr_bitmap_size = 2;
+
+ if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK)
+ wpa_printf(MSG_DEBUG,
+ "MLD: NSTR Indication set in STA control");
+ }
+ sta_info_len += nstr_bitmap_size;
+
+ sta_info = per_sta_prof->variable;
+ if (*sta_info > end - sta_info) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough room for STA Info");
+ goto out;
+ }
+
+ if (*sta_info < sta_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid Reconf STA Info len (%u); min expected=%zu",
+ *sta_info, sta_info_len);
+ goto out;
+ }
+
+ sta_info_len = *sta_info;
+ wpa_hexdump(MSG_MSGDUMP, "MLD: STA Info", sta_info, sta_info_len);
+
+ os_memcpy(sta_addr, sta_info + 1, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "MLD: Link STA addr=" MACSTR, MAC2STR(sta_addr));
+
+ sta_info += sta_info_len;
+
+ lhapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!lhapd) {
+ wpa_printf(MSG_DEBUG, "MLD: No AP link found for link id=%u",
+ link_id);
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+
+ lsta = ap_get_sta(lhapd, req->sta_mld_addr);
+
+ if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) {
+ /* DELETE_LINK request shall not have STA profile */
+ if (len != sizeof(sta_control) + sta_info_len)
+ wpa_printf(MSG_DEBUG,
+ "MLD: Delete link request has STA profile");
+
+ if (!lsta || !ap_sta_is_mld(lhapd, lsta) ||
+ !lsta->mld_info.links[link_id].valid) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: STA invalid for link id=%u peer addr="
+ MACSTR, link_id, MAC2STR(sta_addr));
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+
+ if (!ether_addr_equal(lsta->mld_info.links[link_id].peer_addr,
+ sta_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: STA invalid for addr=" MACSTR,
+ MAC2STR(sta_addr));
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+
+ status = WLAN_STATUS_SUCCESS;
+ ret = 0;
+ goto add_to_list;
+ }
+
+ /* EHT_RECONF_TYPE_ADD_LINK */
+ if (len < sizeof(sta_control) + sta_info_len + 2)
+ goto out;
+ sta_prof_len = len - sizeof(sta_control) - sta_info_len - 2;
+ if (sta_prof_len > (size_t) (end - sta_info)) {
+ wpa_printf(MSG_DEBUG, "MLD: STA Profile with excess length");
+ goto out;
+ }
+
+ if (lsta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: STA exists for link id=%u MLD addr=" MACSTR,
+ link_id, MAC2STR(req->sta_mld_addr));
+ ret = 1; /* reject this per-STA profile */
+ goto add_to_list;
+ }
+
+ /* Check if link address is already used by any connected legacy
+ * non-AP STA or non-AP STA affiliated with a non-AP MLD.
+ */
+ if (ap_for_each_sta(lhapd, hostapd_ml_check_sta_entry_by_link_addr_iter,
+ sta_addr)) {
+ ret = 1; /* Reject this per-STA profile */
+ goto add_to_list;
+ }
+
+ status = WLAN_STATUS_SUCCESS; /* IE validations done later */
+ ret = 0;
+
+add_to_list:
+ if (sta_info && sta_prof_len)
+ wpa_hexdump(MSG_MSGDUMP, "MLD: STA Profile",
+ sta_info, sta_prof_len);
+
+ info = os_zalloc(sizeof(struct link_reconf_req_info) + sta_prof_len);
+ if (!info) {
+ wpa_printf(MSG_DEBUG, "MLD: Failed to allocate request info");
+ ret = 1; /* skip this per-STA profile */
+ goto out;
+ }
+
+ info->link_id = link_id;
+ info->status = status;
+ os_memcpy(info->peer_addr, sta_addr, ETH_ALEN);
+ if (lhapd)
+ os_memcpy(info->local_addr, lhapd->own_addr, ETH_ALEN);
+
+ if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) {
+ dl_list_add_tail(&req->del_req, &info->list);
+ } else if (sta_info) {
+ os_memcpy(info->sta_prof, sta_info, sta_prof_len);
+ info->sta_prof_len = sta_prof_len;
+
+ dl_list_add_tail(&req->add_req, &info->list);
+ } else {
+ os_free(info);
+ }
+ wpa_printf(MSG_INFO, "MLD: Link (%d) parsed to %s request; status=%u",
+ link_id,
+ reconf_type == EHT_RECONF_TYPE_DELETE_LINK ? "del" : "add",
+ status);
+
+out:
+ if (ret < 0)
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to parse reconf req STA profile");
+ return ret;
+}
+
+
+static int
+hostapd_parse_link_reconf_req_reconf_mle(
+ struct hostapd_data *hapd, const u8 *mle, size_t mle_len,
+ struct link_reconf_req_list **req_list_ptr)
+{
+ struct link_reconf_req_list *req_list;
+ struct wpabuf *mlbuf = NULL;
+ struct sta_info *sta;
+ const struct ieee80211_eht_ml *ml;
+ const struct eht_ml_reconf_common_info *ml_common_info;
+ size_t len, ml_common_len;
+ u16 ml_control;
+ const u8 *pos, *end;
+ int ret = -1;
+
+ mlbuf = ieee802_11_defrag(mle, mle_len, true);
+ if (!mlbuf) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to defrag Reconfiguration MLE");
+ goto fail;
+ }
+
+ ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ len = wpabuf_len(mlbuf);
+ end = ((const u8 *) ml) + len;
+
+ wpa_hexdump(MSG_DEBUG, "MLD: Defragged Reconfiguration MLE",
+ (const void *) ml, len);
+
+ if (len < sizeof(*ml) + ETH_ALEN + 1UL)
+ goto fail;
+
+ ml_control = WPA_GET_LE16((const u8 *) ml) >> 4;
+ ml_common_len = 1;
+ if (!(ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR))
+ goto fail;
+ ml_common_len += ETH_ALEN;
+
+ if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA)
+ ml_common_len += 2;
+
+ if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA)
+ ml_common_len += 2;
+
+ if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP)
+ ml_common_len += 2;
+
+ ml_common_info =
+ (const struct eht_ml_reconf_common_info *) ml->variable;
+ if (len < sizeof(*ml) + ml_common_info->len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Reconfiguration ML element length (%zu < %zu)",
+ len, sizeof(*ml) + ml_common_info->len);
+ goto fail;
+ }
+
+ if (ml_common_info->len < ml_common_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid Reconf common info len (%u); min expected=%zu",
+ ml_common_info->len, ml_common_len);
+ goto fail;
+ }
+
+ pos = (const u8 *) ml_common_info->variable;
+
+ sta = ap_get_sta(hapd, pos);
+ if (!sta || !ap_sta_is_mld(hapd, sta)) {
+ wpa_printf(MSG_DEBUG, "MLD: STA invalid%s for " MACSTR,
+ sta ? "" : " (NULL)", MAC2STR(pos));
+ goto fail;
+ }
+
+ *req_list_ptr = os_zalloc(sizeof(struct link_reconf_req_list));
+ if (!(*req_list_ptr)) {
+ wpa_printf(MSG_ERROR, "MLD: Failed to allocate request list");
+ goto fail;
+ }
+ req_list = *req_list_ptr;
+
+ os_memcpy(req_list->sta_mld_addr, pos, ETH_ALEN);
+ dl_list_init(&req_list->del_req);
+ dl_list_init(&req_list->add_req);
+
+ pos = ml->variable + ml_common_info->len;
+
+ while (end - pos > 2) {
+ size_t sub_elem_len;
+ int num_frag_subelems;
+
+ num_frag_subelems =
+ ieee802_11_defrag_mle_subelem(mlbuf, pos,
+ &sub_elem_len);
+ if (num_frag_subelems < 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to parse Reconfiguration MLE subelem");
+ goto fail;
+ }
+
+ if ((size_t) num_frag_subelems * 2 > len)
+ goto fail;
+ len -= num_frag_subelems * 2;
+ end = ((const u8 *) ml) + len;
+
+ if (sub_elem_len + 2 > (size_t) (end - pos))
+ goto fail;
+
+ if (hostapd_parse_link_reconf_req_sta_profile(
+ hapd, req_list, pos, sub_elem_len + 2) < 0)
+ goto fail;
+
+ pos += sub_elem_len + 2;
+ }
+
+ ret = 0;
+
+fail:
+ if (ret)
+ ml_deinit_link_reconf_req(req_list_ptr);
+
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
+static bool
+hostapd_validate_link_reconf_req(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct link_reconf_req_list *req_list)
+{
+ struct hostapd_data *assoc_hapd, *lhapd;
+ struct link_reconf_req_info *info;
+ struct sta_info *assoc_sta, *lsta;
+ struct mld_info *mld_info;
+ u8 recovery_link;
+ u16 valid_links = 0, links_add_ok = 0, links_del_ok = 0, status;
+ size_t link_kde_len, total_kde_len = 0;
+ int i;
+
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
+
+ if (dl_list_empty(&req_list->add_req) &&
+ dl_list_empty(&req_list->del_req)) {
+ wpa_printf(MSG_DEBUG, "MLD: No add or delete request found");
+ return false;
+ }
+
+ mld_info = &assoc_sta->mld_info;
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (mld_info->links[i].valid &&
+ mld_info->links[i].status == WLAN_STATUS_SUCCESS)
+ valid_links |= BIT(i);
+ }
+
+ /* Check IEs for add-link STA profiles */
+ dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info,
+ list) {
+ lhapd = NULL;
+ lsta = NULL;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Add Link Reconf STA for link id=%u status=%u",
+ info->link_id, info->status);
+ if (info->status != WLAN_STATUS_SUCCESS ||
+ info->sta_prof_len < 2)
+ continue;
+
+ /* Offset 2 bytes for Capabilities in STA Profile */
+ status = hostapd_ml_process_reconf_link(hapd, assoc_sta,
+ info->sta_prof + 2,
+ info->sta_prof_len - 2,
+ info->link_id,
+ info->peer_addr);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Add link IE validation failed for link=%u",
+ info->link_id);
+ info->status = status;
+ continue;
+ }
+
+ link_kde_len = wpa_auth_ml_group_kdes_len(assoc_sta->wpa_sm,
+ BIT(info->link_id));
+
+ /* Since Group KDE element Length subfield is one byte,
+ * accept as many add-link requests as can be fit.
+ */
+ if (total_kde_len + link_kde_len >
+ LINK_RECONF_GROUP_KDE_MAX_LEN) {
+ wpa_printf(MSG_INFO,
+ "MLD: Group KDEs cannot fit (%zu > %u) for link=%u",
+ total_kde_len + link_kde_len,
+ LINK_RECONF_GROUP_KDE_MAX_LEN,
+ info->link_id);
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
+ if (lhapd)
+ lsta = ap_get_sta(lhapd,
+ req_list->sta_mld_addr);
+
+ if (lsta)
+ ap_free_sta(lhapd, lsta);
+ } else {
+ total_kde_len += link_kde_len;
+ links_add_ok |= BIT(info->link_id);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_LINK_STA_ADDED "sta=" MACSTR
+ " link_id=%u", MAC2STR(req_list->sta_mld_addr),
+ info->link_id);
+ }
+
+ info->status = status;
+ }
+
+ dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
+ list) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Del Link Reconf STA for link id=%u status=%u",
+ info->link_id, info->status);
+ if (info->status == WLAN_STATUS_SUCCESS)
+ links_del_ok |= BIT(info->link_id);
+ }
+
+ wpa_printf(MSG_INFO, "MLD: valid_links=0x%x add_ok=0x%x del_ok=0x%x",
+ valid_links, links_add_ok, links_del_ok);
+
+ if ((links_add_ok && (valid_links & links_add_ok)) ||
+ (links_del_ok && !(valid_links & links_del_ok))) {
+ wpa_printf(MSG_INFO,
+ "MLD: Links requested failed to satisfy valid links");
+ return false;
+ }
+
+ if (links_add_ok & links_del_ok) {
+ wpa_printf(MSG_INFO,
+ "MLD: Links (0x%x) present in both valid add and delete requests",
+ links_add_ok & links_del_ok);
+ return false;
+ }
+
+ valid_links |= links_add_ok;
+ valid_links &= ~links_del_ok;
+ if (!valid_links) {
+ if (!recover_from_zero_links(&links_del_ok, &recovery_link)) {
+ wpa_printf(MSG_INFO,
+ "MLD: Total-links validation failed");
+ return false;
+ }
+ /* Add the recovery link back to valid_links */
+ valid_links |= BIT(recovery_link);
+ }
+
+ req_list->new_valid_links = valid_links;
+ req_list->links_add_ok = links_add_ok;
+ req_list->links_del_ok = links_del_ok;
+
+ /* TODO: Add support to handle multiple requests from the non-AP MLD */
+ assoc_sta->reconf_req = req_list;
+
+ return true;
+}
+
+
+static int
+hostapd_handle_link_reconf_req(struct hostapd_data *hapd, const u8 *buf,
+ size_t len)
+{
+ struct ieee802_11_elems elems;
+ struct hostapd_data *assoc_hapd;
+ struct sta_info *sta, *assoc_sta = NULL;
+ u8 dialog_token;
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ struct link_reconf_req_list *req_list = NULL;
+ const u8 *pos = NULL;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Link Reconfiguration Request frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+
+ /* Min length: IEEE80211 Header (24B) + Category (1B) + Action (1B) +
+ * Dialog token (1B) +
+ * Reconfiguration MLE header and extension ID (3B)
+ */
+ if (len < IEEE80211_HDRLEN + 3 + 3) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid minimum length (%zu) for Link Reconfiguration Request",
+ len);
+ goto out;
+ }
+
+ dialog_token = mgmt->u.action.u.link_reconf_req.dialog_token;
+ pos = mgmt->u.action.u.link_reconf_req.variable;
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "MLD: No STA found for " MACSTR
+ "; drop Link Reconfiguration Request",
+ MAC2STR(mgmt->sa));
+ goto out;
+ }
+
+ if (!ap_sta_is_mld(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Not an MLD connection; drop Link Reconfiguration Request");
+ goto out;
+ }
+
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Not able to get assoc link STA; drop Link Reconfiguration Request");
+ goto out;
+ }
+
+ if (assoc_sta->reconf_req) {
+ wpa_printf(MSG_INFO,
+ "MLD: Link Reconfiguration Request from this STA with token=%u is already in progress",
+ assoc_sta->reconf_req->dialog_token);
+ goto out;
+ }
+
+ /* Parse Reconfiguration Multi-Link element and OCI elements */
+ if (ieee802_11_parse_elems(pos, len - (pos - buf), &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Could not parse Link Reconfiguration Request");
+ goto out;
+ }
+
+ if (!elems.reconf_mle || !elems.reconf_mle_len) {
+ wpa_printf(MSG_DEBUG, "MLD: No Reconfiguration ML element");
+ goto out;
+ }
+
+ /* Process Reconfiguration MLE */
+ if (hostapd_parse_link_reconf_req_reconf_mle(hapd, elems.reconf_mle,
+ elems.reconf_mle_len,
+ &req_list)) {
+ wpa_printf(MSG_INFO,
+ "MLD: Reconfiguration MLE parsing failed; drop Link Reconfiguration Request");
+ goto out;
+ }
+
+ /* Do OCI element validation */
+ if (dl_list_empty(&req_list->add_req))
+ goto skip_oci_validation;
+
+#ifdef CONFIG_OCV
+ if (!elems.oci || !elems.oci_len) {
+ if (wpa_auth_uses_ocv(assoc_sta->wpa_sm) == 1) {
+ wpa_printf(MSG_INFO,
+ "MLD: No OCI element present; drop Link Reconfiguration Request");
+ goto out;
+ }
+ } else {
+ struct wpa_channel_info ci;
+
+ if (!wpa_auth_uses_ocv(assoc_sta->wpa_sm)) {
+ wpa_printf(MSG_INFO,
+ "MLD: Unexpected OCI element found; drop Link Reconfiguration Request");
+ goto out;
+ }
+
+ if (hostapd_drv_channel_info(hapd, &ci)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to get channel info to verify OCI element");
+ goto out;
+ }
+
+ if (!ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx)) {
+ wpa_printf(MSG_INFO,
+ "MLD: OCI verification failed; drop Link Reconfiguration Request");
+ goto out;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+skip_oci_validation:
+ /* Do STA profile validation */
+ if (!hostapd_validate_link_reconf_req(hapd, assoc_sta, req_list))
+ goto out;
+
+ req_list->dialog_token = dialog_token;
+ ret = hostapd_send_link_reconf_resp(hapd, assoc_sta, req_list);
+ if (ret)
+ wpa_printf(MSG_INFO,
+ "MLD: Failed to send Link Reconfiguration Response (%d)",
+ ret);
+
+out:
+ if (ret) {
+ ml_deinit_link_reconf_req(&req_list);
+ if (assoc_sta && assoc_sta->reconf_req)
+ assoc_sta->reconf_req = NULL;
+ }
+ return ret;
+}
+
+
+void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ const u8 *payload;
+ u8 action;
+
+ if (!hapd->conf->mld_ap)
+ return;
+
+ payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+ action = *payload++;
+
+ switch (action) {
+ case WLAN_PROT_EHT_LINK_RECONFIG_REQUEST:
+ if (hostapd_handle_link_reconf_req(hapd, (const u8 *) mgmt,
+ len))
+ wpa_printf(MSG_INFO,
+ "MLD: Link Reconf Request processing failed");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unsupported Protected EHT Action %u from " MACSTR
+ " discarded", action, MAC2STR(mgmt->sa));
+}
+
+
+size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data *hapd)
+{
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * Allocate enough space for mld_indicate_disabled. i.e.:
+ * Element ID, Length, and Element ID Extension (3) +
+ * Control including presence bitmap (2) + 8 * 2 byte link mappings
+ */
+ return 3 + 2 + 8 * 2;
+#else /* CONFIG_TESTING_OPTIONS */
+ return 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+u8 * hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct hostapd_data *other_hapd;
+ bool need_ttlm = false;
+ u16 ttlm = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ u8 *pos = eid;
+
+ if (!hapd->conf->mld_ap)
+ return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ for_each_mld_link(other_hapd, hapd) {
+ if (other_hapd->conf->mld_indicate_disabled)
+ need_ttlm = true;
+ else
+ ttlm |= BIT(other_hapd->mld_link_id);
+ }
+
+ if (need_ttlm) {
+ int i;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ /* ext EID + 2 bytes control + 8 * link mappings */
+ *pos++ = 1 + 2 + 8 * 2;
+ *pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING;
+ *pos++ = EHT_TID_TO_LINK_MAP_DIRECTION_BOTH;
+ *pos++ = 0xff; /* link mapping presence bitmap */
+
+ for (i = 0; i < 8; i++) {
+ WPA_PUT_LE16(pos, ttlm);
+ pos += 2;
+ }
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return pos;
+}
diff --git a/contrib/wpa/src/ap/ieee802_11_he.c b/contrib/wpa/src/ap/ieee802_11_he.c
index a2deda6c4718..bc0b50c6e6ab 100644
--- a/contrib/wpa/src/ap/ieee802_11_he.c
+++ b/contrib/wpa/src/ap/ieee802_11_he.c
@@ -2,6 +2,7 @@
* hostapd / IEEE 802.11ax HE
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
* Copyright (c) 2019 John Crispin <john@phrozen.org>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -70,7 +71,8 @@ static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
u8 ppe_thres_hdr;
cap = (struct ieee80211_he_capabilities *) buf;
- cap_len = sizeof(*cap) - sizeof(cap->optional);
+ cap_len = sizeof(cap->he_mac_capab_info) +
+ sizeof(cap->he_phy_capab_info);
if (len < cap_len)
return 1;
@@ -91,37 +93,21 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
{
struct ieee80211_he_capabilities *cap;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+ const struct he_capabilities *he_capab;
u8 *pos = eid;
- u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
+ u8 ie_size = 0, mcs_nss_size, ppet_size;
+ u8 *epos;
if (!mode)
return eid;
- ie_size = sizeof(*cap) - sizeof(cap->optional);
- ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
- mode->he_capab[opmode].phy_cap);
+ he_capab = &mode->he_capab[opmode];
- switch (hapd->iface->conf->he_oper_chwidth) {
- case CONF_OPER_CHWIDTH_80P80MHZ:
- he_oper_chwidth |=
- HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
- mcs_nss_size += 4;
- /* fall through */
- case CONF_OPER_CHWIDTH_160MHZ:
- he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
- mcs_nss_size += 4;
- /* fall through */
- case CONF_OPER_CHWIDTH_80MHZ:
- case CONF_OPER_CHWIDTH_USE_HT:
- he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
- break;
- default:
- break;
- }
+ mcs_nss_size = ieee80211_he_mcs_set_size(he_capab->phy_cap);
+ ppet_size = ieee80211_he_ppet_size(he_capab->ppet[0],
+ he_capab->phy_cap);
- ie_size += mcs_nss_size + ppet_size;
+ ie_size = IEEE80211_HE_CAPAB_MIN_LEN + mcs_nss_size + ppet_size;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + ie_size;
@@ -130,14 +116,16 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
cap = (struct ieee80211_he_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
- os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
+ os_memcpy(cap->he_mac_capab_info, he_capab->mac_cap,
HE_MAX_MAC_CAPAB_SIZE);
- os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
+ os_memcpy(cap->he_phy_capab_info, he_capab->phy_cap,
HE_MAX_PHY_CAPAB_SIZE);
- os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
+ epos = (u8 *) &cap->he_basic_supported_mcs_set;
+ os_memcpy(epos, he_capab->mcs, mcs_nss_size);
+ epos += mcs_nss_size;
+
if (ppet_size)
- os_memcpy(&cap->optional[mcs_nss_size],
- mode->he_capab[opmode].ppet, ppet_size);
+ os_memcpy(epos, he_capab->ppet, ppet_size);
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
@@ -160,9 +148,6 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
- cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
- he_oper_chwidth;
-
pos += ie_size;
return pos;
@@ -221,7 +206,7 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
if (is_6ghz_op_class(hapd->iconf->op_class)) {
enum oper_chan_width oper_chwidth =
- hostapd_get_oper_chwidth(hapd->iconf);
+ hapd->iconf->he_oper_chwidth;
u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
u8 control;
@@ -229,6 +214,9 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
if (punct_bitmap) {
+ oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ seg0 = hostapd_get_oper_centr_freq_seg0_idx(
+ hapd->iconf);
punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
@@ -427,14 +415,15 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
{
u16 sta_rx_mcs_set, ap_tx_mcs_set;
u8 mcs_count = 0;
- const u16 *ap_mcs_set, *sta_mcs_set;
+ const u16 *ap_mcs_set;
+ const u8 *sta_mcs_set;
int i;
if (!hapd->iface->current_mode)
return 1;
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
- sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
- sta_he_capab)->optional;
+ sta_mcs_set = (const u8 *) &((const struct ieee80211_he_capabilities *)
+ sta_he_capab)->he_basic_supported_mcs_set;
/*
* Disable HE capabilities for STAs for which there is not even a single
@@ -458,7 +447,7 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
int j;
/* AP Tx MCS map vs. STA Rx MCS map */
- sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+ sta_rx_mcs_set = WPA_GET_LE16(&sta_mcs_set[i * 4]);
ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
&ap_mcs_set[(i * 2) + 1]);
@@ -485,7 +474,7 @@ u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
size_t he_capab_len)
{
if (!he_capab || !(sta->flags & WLAN_STA_WMM) ||
- !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
+ !hostapd_is_he_enabled(hapd) ||
!check_valid_he_mcs(hapd, he_capab, opmode) ||
ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
@@ -514,9 +503,9 @@ u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *he_6ghz_capab)
{
- if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
- hapd->conf->disable_11ax ||
- !is_6ghz_op_class(hapd->iconf->op_class)) {
+ if (!he_6ghz_capab || !hostapd_is_he_enabled(hapd) ||
+ !is_6ghz_op_class(hapd->iconf->op_class) ||
+ !(sta->flags & WLAN_STA_HE)) {
sta->flags &= ~WLAN_STA_6GHZ;
os_free(sta->he_6ghz_capab);
sta->he_6ghz_capab = NULL;
@@ -545,7 +534,7 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
if (!hapd->iface->current_mode ||
!hapd->iface->current_mode->he_capab[mode].he_supported ||
- !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ !hostapd_is_he_enabled(hapd))
return 0;
mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c
index f90f1254e040..04bb60f45db3 100644
--- a/contrib/wpa/src/ap/ieee802_11_ht.c
+++ b/contrib/wpa/src/ap/ieee802_11_ht.c
@@ -26,8 +26,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
struct ieee80211_ht_capabilities *cap;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
- hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class))
+ if (!hostapd_is_ht_enabled(hapd) || !hapd->iface->current_mode ||
+ is_6ghz_op_class(hapd->iconf->op_class))
return eid;
*pos++ = WLAN_EID_HT_CAP;
@@ -79,12 +79,57 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
}
+static void set_ht_param(struct hostapd_data *hapd,
+ struct ieee80211_ht_operation *oper)
+{
+ int secondary_channel = hapd->iconf->secondary_channel;
+#ifdef CONFIG_IEEE80211BE
+ enum oper_chan_width chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ u16 bw = 0, punct_bitmap = hostapd_get_punct_bitmap(hapd);
+ u8 offset, chan_bit_pos;
+
+ switch (chwidth) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ bw = 80;
+ offset = 6;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ bw = 160;
+ offset = 14;
+ break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ bw = 320;
+ offset = 30;
+ break;
+ default:
+ goto no_update;
+ }
+
+ chan_bit_pos = (hapd->iconf->channel -
+ hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf) +
+ offset) / 4;
+ /* Check if secondary channel is punctured */
+ if (bw >= 80 && punct_bitmap && secondary_channel &&
+ (punct_bitmap & BIT(chan_bit_pos + secondary_channel)))
+ return; /* Do not indicate punctured secondary channel for HT */
+no_update:
+#endif /* CONFIG_IEEE80211BE */
+
+ if (secondary_channel == 1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ if (secondary_channel == -1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+}
+
+
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_ht_operation *oper;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ if (!hostapd_is_ht_enabled(hapd) ||
is_6ghz_op_class(hapd->iconf->op_class))
return eid;
@@ -96,12 +141,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
oper->primary_chan = hapd->iconf->channel;
oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
- if (hapd->iconf->secondary_channel == 1)
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
- HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
- if (hapd->iconf->secondary_channel == -1)
- oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ set_ht_param(hapd, oper);
pos += sizeof(*oper);
@@ -363,7 +403,7 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
* frame.
*/
if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
- !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
+ !hostapd_is_ht_enabled(hapd)) {
sta->flags &= ~WLAN_STA_HT;
os_free(sta->ht_capabilities);
sta->ht_capabilities = NULL;
@@ -417,6 +457,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 *
diff --git a/contrib/wpa/src/ap/ieee802_11_shared.c b/contrib/wpa/src/ap/ieee802_11_shared.c
index 3dd3a6a7719e..30f1c8c153ba 100644
--- a/contrib/wpa/src/ap/ieee802_11_shared.c
+++ b/contrib/wpa/src/ap/ieee802_11_shared.c
@@ -381,6 +381,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
/* Bit 13 - Collocated Interference Reporting */
*pos |= 0x20;
}
+ if (hapd->iface->conf->civic)
+ *pos |= 0x40; /* Bit 14 - Civic Location */
+ if (hapd->iface->conf->lci)
+ *pos |= 0x80; /* Bit 15 - Geospatial Location */
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
@@ -476,6 +480,19 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
#endif /* CONFIG_SAE_PK */
break;
+ case 12: /* Bits 96-103 */
+ if (hapd->iconf->i2r_lmr_policy)
+ *pos |= 0x02; /* Bit 97 - I2R LMR Feedback Policy */
+ if (hapd->iconf->peer_to_peer_twt)
+ *pos |= 0x10; /* Bit 100 - Peer to Peer TWT */
+ if (hapd->conf->known_sta_identification)
+ *pos |= 0x40; /* Bit 102 - Known STA Identification
+ * Enabled */
+ break;
+ case 13: /* Bits 104-111 */
+ if (hapd->iconf->channel_usage)
+ *pos |= 0x01; /* Bit 104 - Channel Usage support */
+ break;
}
}
@@ -1034,7 +1051,8 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
int requested_bw;
if (sta->ht_capabilities)
- ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+ ht_40mhz = !!(le_to_host16(sta->ht_capabilities->
+ ht_capabilities_info) &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
if (sta->vht_operation) {
@@ -1070,9 +1088,9 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
* normal clients), use it to determine the supported channel
* bandwidth.
*/
- vht_chanwidth = capab->vht_capabilities_info &
+ vht_chanwidth = le_to_host32(capab->vht_capabilities_info) &
VHT_CAP_SUPP_CHAN_WIDTH_MASK;
- vht_80p80 = capab->vht_capabilities_info &
+ vht_80p80 = le_to_host32(capab->vht_capabilities_info) &
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
/* TODO: Also take into account Extended NSS BW Support field */
@@ -1097,7 +1115,7 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
bool sae_pk = false;
- u32 capab = 0, tmp;
+ u64 capab = 0, tmp;
size_t flen;
if (!(hapd->conf->wpa & WPA_PROTO_RSN))
@@ -1124,10 +1142,37 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
- if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
- capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP) {
+ if (hapd->conf->urnm_mfpr != 0)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (hapd->conf->urnm_mfpr_x20 == 1)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20);
+ }
if (hapd->conf->ssid_protection)
capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+ if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) &&
+ hapd->conf->spp_amsdu)
+ capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
+#ifdef CONFIG_ENC_ASSOC
+ /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy)
+ * a STA that sets the PMKSA Caching Privacy Support
+ * field in the RSNXE to 1 shall set the (Re)Association
+ * Frame Encryption Support field in the RSNXE to 1.
+ */
+ if ((hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) &&
+ (hapd->conf->assoc_frame_encryption ||
+ hapd->conf->pmksa_caching_privacy))
+ capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION);
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_EPPKE)
+ capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN);
+ if (hapd->conf->pmksa_caching_privacy)
+ capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY);
+ if (hapd->conf->eap_using_authentication_frames)
+ capab |= BIT(WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES);
+ if (hapd->conf->eppke_unauth)
+ capab |= BIT_ULL(WLAN_RSNX_CAPAB_UNAUTH_EPPKE);
+#endif /* CONFIG_ENC_ASSOC */
if (!capab)
return eid; /* no supported extended RSN capabilities */
@@ -1221,8 +1266,52 @@ struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd)
{
return hapd->iconf->ht_vht_twt_responder &&
- ((hapd->iconf->ieee80211n && !hapd->conf->disable_11n) ||
- (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)) &&
+ (hostapd_is_ht_enabled(hapd) ||
+ hostapd_is_vht_enabled(hapd)) &&
(hapd->iface->drv_flags2 &
WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER);
}
+
+
+static void hostapd_wfa_gen_capab(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *capab, size_t len)
+{
+ char *hex;
+ size_t buflen;
+
+ wpa_printf(MSG_DEBUG,
+ "WFA: Received indication of generational capabilities from "
+ MACSTR, MAC2STR(sta->addr));
+ wpa_hexdump(MSG_DEBUG, "WFA: Generational Capabilities", capab, len);
+
+ buflen = 2 * len + 1;
+ hex = os_zalloc(buflen);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, buflen, capab, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WFA_GEN_CAPAB_RX MACSTR " %s",
+ MAC2STR(sta->addr), hex);
+ os_free(hex);
+}
+
+
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, const u8 *end)
+{
+ u8 capab_len;
+ const u8 *gen_capa;
+
+ if (end - pos < 1)
+ return;
+ capab_len = *pos++;
+ if (capab_len > end - pos)
+ return;
+ pos += capab_len; /* skip the Capabilities field */
+
+ /* Wi-Fi Alliance Capabilities attributes use a header that is similar
+ * to the one used in Information Elements. */
+ gen_capa = get_ie(pos, end - pos, WFA_CAPA_ATTR_GENERATIONAL_CAPAB);
+ if (gen_capa)
+ hostapd_wfa_gen_capab(hapd, sta, gen_capa + 2, gen_capa[1]);
+}
diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c
index 4dc325ce89bb..65143626a771 100644
--- a/contrib/wpa/src/ap/ieee802_11_vht.c
+++ b/contrib/wpa/src/ap/ieee802_11_vht.c
@@ -21,28 +21,35 @@
#include "dfs.h"
-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+static struct hostapd_hw_modes *
+mode_for_vht_capab(struct hostapd_data *hapd, struct hostapd_hw_modes *mode)
{
- struct ieee80211_vht_capabilities *cap;
- struct hostapd_hw_modes *mode = hapd->iface->current_mode;
- u8 *pos = eid;
-
- if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
- return eid;
-
if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
mode->vht_capab == 0 && hapd->iface->hw_features) {
int i;
for (i = 0; i < hapd->iface->num_hw_features; i++) {
if (hapd->iface->hw_features[i].mode ==
- HOSTAPD_MODE_IEEE80211A) {
- mode = &hapd->iface->hw_features[i];
- break;
- }
+ HOSTAPD_MODE_IEEE80211A)
+ return &hapd->iface->hw_features[i];
}
}
+ return mode;
+}
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+{
+ struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ u8 *pos = eid;
+
+ if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ mode = mode_for_vht_capab(hapd, mode);
+
*pos++ = WLAN_EID_VHT_CAP;
*pos++ = sizeof(*cap);
@@ -95,6 +102,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
#ifdef CONFIG_IEEE80211BE
if (punct_bitmap) {
+ oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
+ seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
@@ -140,9 +150,10 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
}
-static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+static int check_valid_vht_mcs(struct hostapd_data *hapd,
const u8 *sta_vht_capab)
{
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
const struct ieee80211_vht_capabilities *vht_cap;
struct ieee80211_vht_capabilities ap_vht_cap;
u16 sta_rx_mcs_set, ap_tx_mcs_set;
@@ -150,6 +161,7 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
if (!mode)
return 1;
+ mode = mode_for_vht_capab(hapd, mode);
/*
* Disable VHT caps for STAs for which there is not even a single
@@ -166,10 +178,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
- if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+ if (((ap_tx_mcs_set >> (i * 2)) & 0x3) == 3)
continue;
- if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+ if (((sta_rx_mcs_set >> (i * 2)) & 0x3) == 3)
continue;
return 1;
@@ -186,8 +198,9 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
{
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
if (!vht_capab || !(sta->flags & WLAN_STA_WMM) ||
- !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
- !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
+ !hostapd_is_vht_enabled(hapd) ||
+ !check_valid_vht_mcs(hapd, vht_capab) ||
+ !(sta->flags & WLAN_STA_HT)) {
sta->flags &= ~WLAN_STA_VHT;
os_free(sta->vht_capabilities);
sta->vht_capabilities = NULL;
@@ -212,7 +225,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_oper)
{
- if (!vht_oper) {
+ if (!vht_oper || !(sta->flags & WLAN_STA_VHT)) {
os_free(sta->vht_operation);
sta->vht_operation = NULL;
return WLAN_STATUS_SUCCESS;
diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c
index f4103ac9a16d..7f47318b2710 100644
--- a/contrib/wpa/src/ap/ieee802_1x.c
+++ b/contrib/wpa/src/ap/ieee802_1x.c
@@ -45,7 +45,7 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
#endif /* CONFIG_HS20 */
static bool ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
- int remediation, bool logoff);
+ bool logoff);
static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -149,9 +149,9 @@ static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
bool authorized)
{
#ifdef CONFIG_IEEE80211BE
- unsigned int i, link_id;
+ unsigned int i;
- if (!hostapd_is_mld_ap(hapd))
+ if (!hostapd_is_multiple_link_mld(hapd))
return;
/*
@@ -161,32 +161,35 @@ static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
return;
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- struct mld_link_info *link = &sta->mld_info.links[link_id];
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct mld_link_info *link;
+ struct hostapd_data *tmp_hapd =
+ hapd->iface->interfaces->iface[i]->bss[0];
- if (!link->valid)
+ if (!hostapd_is_ml_partner(hapd, tmp_hapd))
continue;
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct sta_info *tmp_sta;
- struct hostapd_data *tmp_hapd =
- hapd->iface->interfaces->iface[i]->bss[0];
+ link = &sta->mld_info.links[tmp_hapd->mld_link_id];
+ if (!link->valid)
+ continue;
- if (!hostapd_is_ml_partner(hapd, tmp_hapd))
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
continue;
- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
- tmp_sta = tmp_sta->next) {
- if (tmp_sta == sta ||
- tmp_sta->mld_assoc_link_id !=
- sta->mld_assoc_link_id ||
- tmp_sta->aid != sta->aid)
- continue;
+ if (!ether_addr_equal(
+ tmp_sta->mld_info.common_info.mld_addr,
+ sta->mld_info.common_info.mld_addr))
+ continue;
- ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
- authorized, true);
- break;
- }
+ ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+ authorized, true);
+ break;
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -453,8 +456,7 @@ static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
return -1;
}
- suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
- hapd->conf->osen) ?
+ suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2)) ?
WPA_PROTO_RSN : WPA_PROTO_WPA,
hapd->conf->wpa_group);
if (!hostapd_config_get_radius_attr(req_attr,
@@ -583,7 +585,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211R_AP */
- if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+ if (hapd->conf->wpa && sta->wpa_sm &&
add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
return -1;
@@ -1125,7 +1127,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
struct rsn_pmksa_cache_entry *pmksa;
int key_mgmt;
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
!hapd->conf->wps_state)
return;
@@ -1134,6 +1136,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
(unsigned long) len, MAC2STR(sa), encrypted);
sta = ap_get_sta(hapd, sa);
if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+#ifdef CONFIG_IEEE8021X_AUTH
+ !sta->epp_sta &&
+#endif /* CONFIG_IEEE8021X_AUTH */
!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
wpa_printf(MSG_DEBUG,
"IEEE 802.1X data frame from not associated/Pre-authenticating STA");
@@ -1185,7 +1190,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
}
- if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
+ if (!hapd->conf->ieee802_1x &&
!(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
wpa_printf(MSG_DEBUG,
"IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
@@ -1252,6 +1257,29 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG,
"received EAPOL-Start from STA");
+#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm &&
+ ((wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) &&
+ (sta->flags & WLAN_STA_AUTHORIZED)) ||
+ sta->auth_alg == WLAN_AUTH_FT)) {
+ /* When FT is used, reauthentication to generate a new
+ * PMK-R0 would be complicated since the current AP
+ * might not be the one with which the currently used
+ * PMK-R0 was generated. IEEE Std 802.11-2020, 13.4.2
+ * (FT initial mobility domain association in an RSN)
+ * mandates STA to perform a new FT initial mobility
+ * domain association whenever its Supplicant would
+ * trigger sending of an EAPOL-Start frame. As such,
+ * this EAPOL-Start frame should not have been sent.
+ * Discard it to avoid unexpected behavior. */
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "discard unexpected EAPOL-Start from STA that uses FT");
+ break;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (pmksa) {
@@ -1337,7 +1365,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
}
#endif /* CONFIG_WPS */
- if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+ if (!force_1x && !hapd->conf->ieee802_1x) {
wpa_printf(MSG_DEBUG,
"IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
/*
@@ -1348,6 +1376,19 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
return;
}
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (sta->auth_alg == WLAN_AUTH_802_1X) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Ignore STA - EAP in Authentication frames");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPA-EAP to PSK.
+ */
+ ieee802_1x_free_station(hapd, sta);
+ return;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
if (key_mgmt != -1 &&
(wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
@@ -1471,10 +1512,6 @@ void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
{
struct eapol_state_machine *sm = sta->eapol_sm;
-#ifdef CONFIG_HS20
- eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
-#endif /* CONFIG_HS20 */
-
if (sta->pending_eapol_rx) {
wpabuf_free(sta->pending_eapol_rx->buf);
os_free(sta->pending_eapol_rx);
@@ -1750,32 +1787,6 @@ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
-static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
-{
- sta->remediation = 1;
- os_free(sta->remediation_url);
- if (len > 2) {
- sta->remediation_url = os_malloc(len);
- if (!sta->remediation_url)
- return;
- sta->remediation_method = pos[0];
- os_memcpy(sta->remediation_url, pos + 1, len - 1);
- sta->remediation_url[len - 1] = '\0';
- wpa_printf(MSG_DEBUG,
- "HS 2.0: Subscription remediation needed for "
- MACSTR " - server method %u URL %s",
- MAC2STR(sta->addr), sta->remediation_method,
- sta->remediation_url);
- } else {
- sta->remediation_url = NULL;
- wpa_printf(MSG_DEBUG,
- "HS 2.0: Subscription remediation needed for "
- MACSTR, MAC2STR(sta->addr));
- }
- /* TODO: assign the STA into remediation VLAN or add filtering */
-}
-
-
static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
struct sta_info *sta, const u8 *pos,
size_t len)
@@ -1891,7 +1902,6 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
size_t len;
buf = NULL;
- sta->remediation = 0;
sta->hs20_deauth_requested = 0;
sta->hs20_deauth_on_ack = 0;
@@ -1916,9 +1926,6 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
continue; /* invalid WFA VSA */
switch (type) {
- case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
- ieee802_1x_hs20_sub_rem(sta, pos, sublen);
- break;
case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
break;
@@ -2306,9 +2313,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
const u8 *data, size_t datalen)
{
-#ifdef CONFIG_WPS
+ struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
+#ifdef CONFIG_WPS
+
if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
WLAN_STA_MAYBE_WPS) {
const u8 *identity;
@@ -2330,7 +2339,16 @@ static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
}
#endif /* CONFIG_WPS */
- ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (sta->epp_sta && hapd->send_eap_req) {
+ hapd->send_eap_req(hapd, sta, type,
+ sta->eap_auth_data.auth_transaction + 1,
+ WLAN_STATUS_SUCCESS, NULL, data, datalen);
+ return;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+ ieee802_1x_send(hapd, sta, type, data, datalen);
}
@@ -2347,7 +2365,7 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
- int preauth, int remediation, bool logoff)
+ int preauth, bool logoff)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
@@ -2357,7 +2375,7 @@ static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
return false;
}
- return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
+ return ieee802_1x_finished(hapd, sta, success, logoff);
}
@@ -2399,7 +2417,6 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
- user->remediation = eap_user->remediation;
rv = 0;
out:
@@ -2557,8 +2574,6 @@ int ieee802_1x_init(struct hostapd_data *hapd)
}
#endif /* CONFIG_IEEE80211BE */
- dl_list_init(&hapd->erp_keys);
-
os_memset(&conf, 0, sizeof(conf));
conf.eap_cfg = hapd->eap_cfg;
conf.ctx = hapd;
@@ -2987,7 +3002,11 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
id = eap_get_identity(sm->eap, &id_len);
if (id)
identity_buf = dup_binstr(id, id_len);
+ } else if (sm->eap && sm->identity) {
+ identity_buf = dup_binstr(sm->identity,
+ sm->identity_len);
}
+
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -3003,8 +3022,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
(unsigned int) diff.sec,
- sm->identity ? (char *) sm->identity :
- (identity_buf ? identity_buf : "N/A"));
+ identity_buf ? identity_buf : "N/A");
os_free(identity_buf);
if (os_snprintf_error(buflen - len, ret))
return len;
@@ -3041,17 +3059,6 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
- if (sta->remediation) {
- wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
- MACSTR " to indicate Subscription Remediation",
- MAC2STR(sta->addr));
- hs20_send_wnm_notification(hapd, sta->addr,
- sta->remediation_method,
- sta->remediation_url);
- os_free(sta->remediation_url);
- sta->remediation_url = NULL;
- }
-
if (sta->hs20_deauth_req) {
wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
MACSTR " to indicate imminent deauthentication",
@@ -3074,7 +3081,7 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
static bool ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
- int remediation, bool logoff)
+ bool logoff)
{
const u8 *key;
size_t len;
@@ -3084,16 +3091,7 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd,
struct os_reltime now, remaining;
#ifdef CONFIG_HS20
- if (remediation && !sta->remediation) {
- sta->remediation = 1;
- os_free(sta->remediation_url);
- sta->remediation_url =
- os_strdup(hapd->conf->subscr_remediation_url);
- sta->remediation_method = 1; /* SOAP-XML SPP */
- }
-
- if (success && (sta->remediation || sta->hs20_deauth_req ||
- sta->hs20_t_c_filtering)) {
+ if (success && (sta->hs20_deauth_req || sta->hs20_t_c_filtering)) {
wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
MACSTR " in 100 ms", MAC2STR(sta->addr));
eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -3114,7 +3112,7 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd,
} else {
session_timeout = dot11RSNAConfigPMKLifetime;
}
- if (success && key && len >= PMK_LEN && !sta->remediation &&
+ if (success && key && len >= PMK_LEN &&
!sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
sta->eapol_sm) == 0) {
@@ -3142,3 +3140,13 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd,
return false;
}
+
+
+#ifdef CONFIG_IEEE8021X_AUTH
+void ieee802_1x_eapol_sm_set_port_enabled(struct eapol_state_machine *sm,
+ bool value)
+{
+ if (sm && sm->eap_if)
+ sm->eap_if->portEnabled = value;
+}
+#endif /* CONFIG_IEEE8021X_AUTH */
diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h
index 1469351c1e55..35d16d2c7236 100644
--- a/contrib/wpa/src/ap/ieee802_1x.h
+++ b/contrib/wpa/src/ap/ieee802_1x.h
@@ -66,4 +66,6 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_eapol_sm_set_port_enabled(struct eapol_state_machine *sm,
+ bool value);
#endif /* IEEE802_1X_H */
diff --git a/contrib/wpa/src/ap/interference.c b/contrib/wpa/src/ap/interference.c
new file mode 100644
index 000000000000..c93cdc75f13f
--- /dev/null
+++ b/contrib/wpa/src/ap/interference.c
@@ -0,0 +1,592 @@
+/*
+ * Management of interference due to incumbent signals in 6 GHz band
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/hw_features_common.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "interference.h"
+
+
+#define BASE_6GHZ_FREQ 5950
+/* Channel 2 (op class 136) has a center frequency of 5935 MHz. */
+#define BASE_6GHZ_CH2_FREQ 5935
+
+enum chan_seg {
+ SEG_PRI20 = 0x1,
+ SEG_SEC20 = 0x2,
+ SEG_SEC40_LOW = 0x4,
+ SEG_SEC40_UP = 0x8,
+ SEG_SEC40 = 0xC,
+ SEG_SEC80_LOW = 0x10,
+ SEG_SEC80_LOW_UP = 0x20,
+ SEG_SEC80_UP_LOW = 0x40,
+ SEG_SEC80_UP = 0x80,
+ SEG_SEC80 = 0xF0,
+ SEG_SEC160_LOW = 0x0100,
+ SEG_SEC160_LOW_UP = 0x0200,
+ SEG_SEC160_LOW_UP_UP = 0x0400,
+ SEG_SEC160_LOW_UP_UP_UP = 0x0800,
+ SEG_SEC160_UP_LOW_LOW_LOW = 0x1000,
+ SEG_SEC160_UP_LOW_LOW = 0x2000,
+ SEG_SEC160_UP_LOW = 0x4000,
+ SEG_SEC160_UP = 0x8000,
+ SEG_SEC160 = 0xFF00,
+};
+
+
+static bool is_chan_disabled(struct hostapd_hw_modes *mode, int chan_num)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == chan_num)
+ return !!(chan->flag & HOSTAPD_CHAN_DISABLED);
+ }
+
+ return true;
+}
+
+
+/*
+ * chan_range_available - Check whether the channel can operate in the given
+ * bandwidth in 6 GHz
+ * @first_chan_idx - Channel index of the first 20 MHz channel in a segment
+ * @num_chans - Number of 20 MHz channels needed for the operating bandwidth
+ * @allowed_arr - Range of channels allowed in the given bandwidth
+ * @allowed_arr_size - Number of channels allowed in the given bandwidth
+ * Returns: Whether the channel can operation in the given bandwidth
+ */
+static bool chan_range_available(struct hostapd_hw_modes *mode,
+ unsigned int first_chan_idx,
+ unsigned int num_chans,
+ const int *allowed_arr,
+ unsigned int allowed_arr_size)
+{
+ struct hostapd_channel_data *first_chan;
+ unsigned int i;
+
+ first_chan = &mode->channels[first_chan_idx];
+
+ if (!chan_pri_allowed(first_chan)) {
+ wpa_printf(MSG_DEBUG, "Intf: Primary channel %d not allowed",
+ first_chan->chan);
+ return false;
+ }
+
+ /* 20 MHz channel, so no need to check the range */
+ if (num_chans == 1)
+ return true;
+
+ if (allowed_arr) {
+ for (i = 0; i < allowed_arr_size; i++) {
+ if (first_chan->chan == allowed_arr[i])
+ break;
+ }
+ if (i == allowed_arr_size)
+ return false;
+ }
+
+ /*
+ * Check whether all the 20 MHz channels in the given operating range
+ * are enabled.
+ */
+ for (i = 1; i < num_chans; i++) {
+ if (is_chan_disabled(mode, first_chan->chan + i * 4))
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_ch_list.num)
+ return true;
+
+ return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+static int get_center_freq_6g(int chan_idx, enum chan_width chan_width,
+ int *center_freq)
+{
+ if (!center_freq)
+ return -1;
+
+ *center_freq = 0;
+
+ switch (chan_width) {
+ case CHAN_WIDTH_20:
+ if (chan_idx == 2) {
+ *center_freq = BASE_6GHZ_CH2_FREQ;
+ break;
+ }
+ if (chan_idx >= 1 && chan_idx <= 233)
+ *center_freq = ((chan_idx / 4) * 4 + 1) * 5 +
+ BASE_6GHZ_FREQ;
+ break;
+ case CHAN_WIDTH_40:
+ if (chan_idx >= 1 && chan_idx <= 229)
+ *center_freq = ((chan_idx / 8) * 8 + 3) * 5 +
+ BASE_6GHZ_FREQ;
+ break;
+ case CHAN_WIDTH_80:
+ if (chan_idx >= 1 && chan_idx <= 221)
+ *center_freq = ((chan_idx / 16) * 16 + 7) * 5 +
+ BASE_6GHZ_FREQ;
+ break;
+ case CHAN_WIDTH_160:
+ if (chan_idx >= 1 && chan_idx <= 221)
+ *center_freq = ((chan_idx / 32) * 32 + 15) * 5 +
+ BASE_6GHZ_FREQ;
+ break;
+ case CHAN_WIDTH_320:
+ if (chan_idx >= 1 && chan_idx <= 221)
+ *center_freq = ((chan_idx / 32) * 32 + 31) * 5 +
+ BASE_6GHZ_FREQ;
+ break;
+ default:
+ break;
+ }
+
+ if (*center_freq == 0)
+ return -1;
+
+ return 0;
+}
+
+
+static bool is_interference_in_chanlist(int freq_start, int freq_end,
+ const int *interference_freqs)
+{
+ int i;
+
+ for (i = freq_start; i <= freq_end; i += 20) {
+ if (int_array_includes(interference_freqs, i))
+ return true;
+ }
+ return false;
+}
+
+
+
+static const int * get_allowed_channel_array(int num_chans, unsigned int *size)
+{
+ static const int allowed_40_6g[] = { 1, 9, 17, 25, 33, 41, 49, 57, 65,
+ 73, 81, 89, 97, 105, 113, 121, 129,
+ 137, 145, 153, 161, 169, 177, 185,
+ 193, 201, 209, 217, 225, 233 };
+ static const int allowed_80_6g[] = { 1, 17, 33, 49, 65, 81, 97, 113,
+ 129, 145, 161, 177,
+ 193, 209 };
+ static const int allowed_160_6g[] = { 1, 33, 65, 97, 129, 161, 193 };
+ static const int allowed_320_6g[] = { 1, 65, 129, 33, 97, 161 };
+
+ switch (num_chans) {
+ case 2:
+ *size = ARRAY_SIZE(allowed_40_6g);
+ return allowed_40_6g;
+ case 4:
+ *size = ARRAY_SIZE(allowed_80_6g);
+ return allowed_80_6g;
+ case 8:
+ *size = ARRAY_SIZE(allowed_160_6g);
+ return allowed_160_6g;
+ case 16:
+ *size = ARRAY_SIZE(allowed_320_6g);
+ return allowed_320_6g;
+ default:
+ *size = 0;
+ return NULL;
+ }
+}
+
+
+/**
+ * intf_find_channel_list - Find the list of channels that can operate with
+ * channel width chan_width and not present within the range of current
+ * operating range.
+ * @chan_width - Channel width to be checked
+ * @chandef_list - Pointer array to hold the list of valid available chandef
+ * Returns: The total number of available chandefs that support the provided
+ * bandwidth
+ */
+static int intf_find_channel_list(struct hostapd_iface *iface, int chan_width,
+ struct hostapd_channel_data **chandef_list,
+ const int *interference_freqs)
+{
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ int new_center_freq, new_start_freq, new_end_freq;
+ const int *allowed_arr = NULL;
+ unsigned int allowed_arr_size = 0;
+ struct hostapd_channel_data *chan;
+ int i;
+ unsigned int channel_idx = 0, n_chans;
+ int ret;
+
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ n_chans = 1;
+ break;
+ case CHAN_WIDTH_40:
+ n_chans = 2;
+ break;
+ case CHAN_WIDTH_80:
+ n_chans = 4;
+ break;
+ case CHAN_WIDTH_80P80:
+ case CHAN_WIDTH_160:
+ n_chans = 8;
+ break;
+ case CHAN_WIDTH_320:
+ n_chans = 16;
+ break;
+ default:
+ n_chans = 1;
+ break;
+ }
+
+ allowed_arr = get_allowed_channel_array(n_chans, &allowed_arr_size);
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (!chan_in_current_hw_info(iface->current_hw_info, chan)) {
+ wpa_printf(MSG_DEBUG,
+ "Intf: Channel %d (%d) is not under current hardware index",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ /* Skip incompatible chandefs */
+ if (!chan_range_available(mode, i, n_chans,
+ allowed_arr, allowed_arr_size)) {
+ wpa_printf(MSG_DEBUG,
+ "Intf: Range not available for chan %d (%d)",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ if (!is_in_chanlist(iface, chan)) {
+ wpa_printf(MSG_DEBUG,
+ "Intf: Channel %d (%d) not in chanlist",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ ret = get_center_freq_6g(chan->chan, chan_width,
+ &new_center_freq);
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "Intf: Couldn't find center freq for chan %d chan_width %d",
+ chan->chan, chan_width);
+ return 0;
+ }
+
+ new_start_freq = (new_center_freq -
+ channel_width_to_int(chan_width) / 2) + 10;
+ new_end_freq = (new_center_freq +
+ channel_width_to_int(chan_width) / 2) - 10;
+
+ if (is_interference_in_chanlist(new_start_freq, new_end_freq,
+ interference_freqs)) {
+ wpa_printf(MSG_DEBUG,
+ "Intf: Found frequency which has interference in channel (%d)",
+ chan->chan);
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "Intf: Adding channel %d (%d) to valid chandef list",
+ chan->freq, chan->chan);
+ chandef_list[channel_idx] = chan;
+ channel_idx++;
+ }
+
+ return channel_idx;
+}
+
+
+static enum chan_width downgrade_bandwidth(enum chan_width chan_width)
+{
+ switch (chan_width) {
+ case CHAN_WIDTH_320:
+ return CHAN_WIDTH_160;
+ case CHAN_WIDTH_160:
+ return CHAN_WIDTH_80;
+ case CHAN_WIDTH_80:
+ return CHAN_WIDTH_40;
+ case CHAN_WIDTH_40:
+ return CHAN_WIDTH_20;
+ default:
+ return CHAN_WIDTH_20_NOHT;
+ }
+}
+
+
+/*
+ * hostapd_incumbt_sig_intf_detected - Incumbent signal interference is detected
+ * in the operating channel. The interference channel information is available
+ * as a bitmap(chan_bw_interference_bitmap). If interference has occurred in
+ * the primary channel, do a complete channel switch to a different channel;
+ * otherwise, reduce the operating bandwidth and continue AP operation using
+ * the same primary channel.
+ */
+int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
+ int chan_width, int cf1, int cf2,
+ u32 chan_bw_interference_bitmap)
+{
+ struct csa_settings settings;
+ struct hostapd_channel_data *chan_data = NULL;
+ struct hostapd_channel_data *chan_temp;
+ struct hostapd_channel_data **available_chandef_list = NULL;
+ int ret = 0;
+ unsigned int i;
+ u32 _rand;
+ u32 chan_idx;
+ int num_available_chandefs = 0;
+ enum chan_width new_chan_width;
+ int new_center_freq;
+ int current_start_freq;
+ int temp_width;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ int *interference_freqs = NULL;
+ int primary_chan_bit = -1;
+ int segment_freq;
+ int start_freq = (cf1 - channel_width_to_int(chan_width) / 2) + 10;
+ u8 channel_no;
+ unsigned int num_segments;
+ int new_bw;
+
+ wpa_printf(MSG_DEBUG,
+ "Intf: input freq=%d, chan_width=%d, cf1=%d, cf2=%d, chan_bw_interference_bitmap=0x%x",
+ freq, chan_width, cf1, cf2, chan_bw_interference_bitmap);
+
+ num_segments = channel_width_to_int(chan_width) / 20;
+ if (!num_segments) {
+ wpa_printf(MSG_INFO, "Intf: Invalid channel width %d",
+ chan_width);
+ return -1;
+ }
+
+ /* Determine which 20 MHz segment contains the primary channel */
+ for (i = 0; i < num_segments; i++) {
+ segment_freq = start_freq + i * 20;
+ if (segment_freq == freq) {
+ primary_chan_bit = i;
+ break;
+ }
+ }
+
+ /* Check whether interference has occurred in primary 20 MHz channel */
+ if (primary_chan_bit >= 0 &&
+ (chan_bw_interference_bitmap & BIT(primary_chan_bit))) {
+ available_chandef_list = os_calloc(
+ mode->num_channels,
+ sizeof(struct hostapd_channel_data *));
+ if (!available_chandef_list) {
+ wpa_printf(MSG_ERROR,
+ "Intf: Failed to allocate available_chandef_list");
+ return -1;
+ }
+
+ /* Store frequencies with interference in interference_freqs */
+ current_start_freq =
+ (cf1 - channel_width_to_int(chan_width) / 2) + 10;
+ for (i = 0; i < num_segments; i++) {
+ if (chan_bw_interference_bitmap & BIT(i)) {
+ wpa_printf(MSG_DEBUG,
+ "Intf: Found incumbent signal interference in frequency %d",
+ current_start_freq + 20 * i);
+ int_array_add_unique(&interference_freqs,
+ current_start_freq +
+ 20 * i);
+ if (!interference_freqs) {
+ wpa_printf(MSG_ERROR,
+ "Intf: Failed to store interference frequencies");
+ ret = -1;
+ goto exit;
+ }
+ }
+ }
+
+ /* Find a random channel to be switched */
+ temp_width = chan_width;
+
+ while (temp_width > CHAN_WIDTH_20_NOHT) {
+ num_available_chandefs =
+ intf_find_channel_list(iface, temp_width,
+ available_chandef_list,
+ interference_freqs);
+ if (num_available_chandefs > 0)
+ break;
+ wpa_printf(MSG_DEBUG, "Intf: Downgrading bandwidth");
+ temp_width = downgrade_bandwidth(temp_width);
+ }
+
+ if (num_available_chandefs == 0) {
+ wpa_printf(MSG_INFO, "Intf: No available_chandefs");
+ goto exit;
+ }
+
+ if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ _rand = os_random();
+
+ chan_idx = _rand % num_available_chandefs;
+ chan_data = available_chandef_list[chan_idx];
+ new_chan_width = temp_width;
+
+ wpa_printf(MSG_DEBUG, "Intf: Got random channel %d (%d)",
+ chan_data->freq, chan_data->chan);
+ } else {
+ /*
+ * Interference is not present in the primary 20 MHz, so
+ * reduce bandwidth.
+ */
+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+ u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+ enum oper_chan_width oper_chwidth;
+ int j;
+
+ for (j = 0; j < mode->num_channels; j++) {
+ chan_temp = &mode->channels[j];
+ if (chan_temp->freq == freq)
+ chan_data = chan_temp;
+ }
+ if (!chan_data) {
+ wpa_printf(MSG_INFO, "Intf: No channel found");
+ goto exit;
+ }
+
+ oper_chwidth = chan_width_to_oper_chwidth(chan_width);
+
+ if (chan_width > CHAN_WIDTH_40) {
+ punct_update_legacy_bw(chan_bw_interference_bitmap,
+ iface->conf->channel,
+ &oper_chwidth, &seg0, &seg1);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
+ new_chan_width = CHAN_WIDTH_160;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+ new_chan_width = CHAN_WIDTH_80;
+ else if (seg0 == 0)
+ new_chan_width = CHAN_WIDTH_20;
+ else
+ new_chan_width = CHAN_WIDTH_40;
+ } else {
+ new_chan_width = CHAN_WIDTH_20;
+ }
+ }
+
+ if (new_chan_width > CHAN_WIDTH_20) {
+ ret = get_center_freq_6g(chan_data->chan, new_chan_width,
+ &new_center_freq);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "Intf: Couldn't find center freq for chan : %d chan_width : %d",
+ chan_data->chan, new_chan_width);
+ goto exit;
+ }
+ } else {
+ new_center_freq = chan_data->freq;
+ }
+
+ ieee80211_freq_to_chan(chan_data->freq, &channel_no);
+
+ os_memset(&settings, 0, sizeof(settings));
+ settings.cs_count = 5;
+ settings.freq_params.freq = chan_data->freq;
+
+ switch (new_chan_width) {
+ case CHAN_WIDTH_40:
+ new_bw = 40;
+ break;
+ case CHAN_WIDTH_80P80:
+ case CHAN_WIDTH_80:
+ new_bw = 80;
+ break;
+ case CHAN_WIDTH_160:
+ new_bw = 160;
+ break;
+ case CHAN_WIDTH_320:
+ new_bw = 320;
+ break;
+ default:
+ new_bw = 20;
+ break;
+ }
+
+ settings.freq_params.bandwidth = new_bw;
+ settings.freq_params.channel = channel_no;
+ settings.freq_params.center_freq1 = new_center_freq;
+ settings.freq_params.ht_enabled = iface->conf->ieee80211n;
+ settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
+ settings.freq_params.he_enabled = iface->conf->ieee80211ax;
+ settings.freq_params.eht_enabled = iface->conf->ieee80211be;
+ wpa_printf(MSG_DEBUG,
+ "Intf: channel=%u, freq=%d, bw=%d, center_freq1=%d",
+ settings.freq_params.channel,
+ settings.freq_params.freq,
+ settings.freq_params.bandwidth,
+ settings.freq_params.center_freq1);
+
+ if (chan_data->freq == iface->freq) {
+ if (hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &settings.freq_params, NULL)) {
+ wpa_printf(MSG_INFO,
+ "Intf: Failed to update bandwidth");
+ ret = -1;
+ goto exit;
+ }
+ if (hostapd_set_freq(
+ iface->bss[0], iface->conf->hw_mode, iface->freq,
+ iface->conf->channel, iface->conf->enable_edmg,
+ iface->conf->edmg_channel, iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ iface->conf->secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
+ wpa_printf(MSG_ERROR,
+ "Intf: Failed to apply bandwidth update");
+ ret = -1;
+ goto exit;
+ }
+ ieee802_11_set_beacons(iface);
+ goto exit;
+ }
+
+ /* Channel and bandwidth have been decided, triggering channel switch */
+ for (i = 0; i < iface->num_bss; i++) {
+ /* Save CHAN_SWITCH VHT and HE config */
+ hostapd_chan_switch_config(iface->bss[i],
+ &settings.freq_params);
+
+ ret = hostapd_switch_channel(iface->bss[i], &settings);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "Intf: Channel switch failed");
+ break;
+ }
+ }
+
+exit:
+ os_free(available_chandef_list);
+ os_free(interference_freqs);
+ return ret;
+}
diff --git a/contrib/wpa/src/ap/interference.h b/contrib/wpa/src/ap/interference.h
new file mode 100644
index 000000000000..fb13abab1573
--- /dev/null
+++ b/contrib/wpa/src/ap/interference.h
@@ -0,0 +1,18 @@
+/*
+ * Management of interference due to incumbent signals in 6 GHz band
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef INTERFERENCE_H
+#define INTERFERENCE_H
+
+struct hostapd_iface;
+
+int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
+ int chan_width,
+ int cf1, int cf2,
+ u32 chan_bw_interference_bitmap);
+
+#endif /* INTERFERENCE_H */
diff --git a/contrib/wpa/src/ap/nan_usd_ap.c b/contrib/wpa/src/ap/nan_usd_ap.c
index 52a967a4ec41..076415f31979 100644
--- a/contrib/wpa/src/ap/nan_usd_ap.c
+++ b/contrib/wpa/src/ap/nan_usd_ap.c
@@ -1,6 +1,6 @@
/*
* NAN unsynchronized service discovery (USD)
- * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,38 +29,38 @@ static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
wpabuf_len(buf));
/* TODO: Force use of OFDM */
- return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
- wpabuf_head(buf), wpabuf_len(buf));
+ return hostapd_drv_send_action_forced_addr3(hapd, hapd->iface->freq, 0,
+ dst, bssid,
+ wpabuf_head(buf),
+ wpabuf_len(buf));
}
static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
- unsigned int duration)
+ unsigned int duration, const u8 *forced_addr)
{
return 0;
}
static void
-hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
- enum nan_service_protocol_type srv_proto_type,
- const u8 *ssi, size_t ssi_len,
- int peer_publish_id, const u8 *peer_addr,
- bool fsd, bool fsd_gas)
+hostapd_nan_de_discovery_result(void *ctx, struct nan_discovery_result *res)
{
struct hostapd_data *hapd = ctx;
char *ssi_hex;
- ssi_hex = os_zalloc(2 * ssi_len + 1);
+ ssi_hex = os_zalloc(2 * res->ssi_len + 1);
if (!ssi_hex)
return;
- if (ssi)
- wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ if (res->ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * res->ssi_len + 1,
+ res->ssi, res->ssi_len);
wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
"subscribe_id=%d publish_id=%d address=" MACSTR
" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
- subscribe_id, peer_publish_id, MAC2STR(peer_addr),
- fsd, fsd_gas, srv_proto_type, ssi_hex);
+ res->subscribe_id, res->peer_publish_id,
+ MAC2STR(res->peer_addr), res->fsd, res->fsd_gas,
+ res->srv_proto_type, ssi_hex);
os_free(ssi_hex);
}
@@ -127,7 +127,8 @@ static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
const u8 *ssi, size_t ssi_len,
- const u8 *peer_addr)
+ const u8 *peer_addr,
+ const u8 *buf, size_t len)
{
struct hostapd_data *hapd = ctx;
char *ssi_hex;
@@ -148,6 +149,10 @@ int hostapd_nan_usd_init(struct hostapd_data *hapd)
{
struct nan_callbacks cb;
+ /* If nan_de is already initialized, do not create a new instance. */
+ if (hapd->nan_de)
+ return 0;
+
os_memset(&cb, 0, sizeof(cb));
cb.ctx = hapd;
cb.tx = hostapd_nan_de_tx;
@@ -158,26 +163,33 @@ int hostapd_nan_usd_init(struct hostapd_data *hapd)
cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
cb.receive = hostapd_nan_de_receive;
- hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb);
+ hapd->nan_de = nan_de_init(hapd->own_addr, false, true, 0, &cb);
if (!hapd->nan_de)
return -1;
+
+ hapd->nan_de_is_owned = true;
+
return 0;
}
void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
{
- nan_de_deinit(hapd->nan_de);
+ if (hapd->nan_de_is_owned) {
+ nan_de_deinit(hapd->nan_de);
+ hapd->nan_de_is_owned = false;
+ }
hapd->nan_de = NULL;
}
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
- unsigned int freq, const u8 *buf, size_t len)
+ const u8 *a3, unsigned int freq,
+ const u8 *buf, size_t len)
{
if (!hapd->nan_de)
return;
- nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+ nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len, 0);
}
@@ -192,7 +204,7 @@ void hostapd_nan_usd_flush(struct hostapd_data *hapd)
int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
- struct nan_publish_params *params)
+ struct nan_publish_params *params, bool p2p)
{
int publish_id;
struct wpabuf *elems = NULL;
@@ -201,7 +213,7 @@ int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
return -1;
publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
- ssi, elems, params);
+ ssi, elems, params, p2p, NULL);
wpabuf_free(elems);
return publish_id;
}
@@ -231,7 +243,7 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
- struct nan_subscribe_params *params)
+ struct nan_subscribe_params *params, bool p2p)
{
int subscribe_id;
struct wpabuf *elems = NULL;
@@ -240,7 +252,8 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
return -1;
subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
- srv_proto_type, ssi, elems, params);
+ srv_proto_type, ssi, elems, params, p2p,
+ NULL);
wpabuf_free(elems);
return subscribe_id;
}
@@ -258,10 +271,11 @@ void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
const struct wpabuf *ssi,
const struct wpabuf *elems,
- const u8 *peer_addr, u8 req_instance_id)
+ const u8 *peer_addr,
+ u8 req_instance_id)
{
if (!hapd->nan_de)
return -1;
return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
- req_instance_id);
+ req_instance_id, NULL, NULL);
}
diff --git a/contrib/wpa/src/ap/nan_usd_ap.h b/contrib/wpa/src/ap/nan_usd_ap.h
index 58ff5fc4808b..69400dfba05c 100644
--- a/contrib/wpa/src/ap/nan_usd_ap.h
+++ b/contrib/wpa/src/ap/nan_usd_ap.h
@@ -1,6 +1,6 @@
/*
* NAN unsynchronized service discovery (USD)
- * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -16,12 +16,13 @@ enum nan_service_protocol_type;
int hostapd_nan_usd_init(struct hostapd_data *hapd);
void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+ const u8 *a3,
unsigned int freq, const u8 *buf, size_t len);
void hostapd_nan_usd_flush(struct hostapd_data *hapd);
int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
- struct nan_publish_params *params);
+ struct nan_publish_params *params, bool p2p);
void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id);
int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
const struct wpabuf *ssi);
@@ -29,7 +30,7 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi,
- struct nan_subscribe_params *params);
+ struct nan_subscribe_params *params, bool p2p);
void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
int subscribe_id);
int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
diff --git a/contrib/wpa/src/ap/neighbor_db.c b/contrib/wpa/src/ap/neighbor_db.c
index f7a7d83d427d..fd4f881df029 100644
--- a/contrib/wpa/src/ap/neighbor_db.c
+++ b/contrib/wpa/src/ap/neighbor_db.c
@@ -203,16 +203,15 @@ void hostapd_free_neighbor_db(struct hostapd_data *hapd)
#ifdef NEED_AP_MLME
-static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
- int ht, int vht, int he)
+static enum nr_chan_width
+hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+ int ht, int vht, int he,
+ enum oper_chan_width oper_chwidth,
+ int secondary_channel)
{
- enum oper_chan_width oper_chwidth;
-
- oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
-
if (!ht && !vht && !he)
return NR_CHAN_WIDTH_20;
- if (!hapd->iconf->secondary_channel)
+ if (!secondary_channel)
return NR_CHAN_WIDTH_20;
if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
return NR_CHAN_WIDTH_40;
@@ -231,14 +230,16 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
{
#ifdef NEED_AP_MLME
u16 capab = hostapd_own_capab_info(hapd);
- int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
- int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
- int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
- bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
+ int ht = hostapd_is_ht_enabled(hapd);
+ int vht = hostapd_is_vht_enabled(hapd);
+ int he = hostapd_is_he_enabled(hapd);
+ bool eht = he && hostapd_is_eht_enabled(hapd);
struct wpa_ssid_value ssid;
u8 channel, op_class;
u8 center_freq1_idx = 0, center_freq2_idx = 0;
+ enum oper_chan_width oper_chwidth;
enum nr_chan_width width;
+ int secondary_channel;
u32 bssid_info;
struct wpabuf *nr;
@@ -264,10 +265,7 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
}
if (ht) {
- bssid_info |= NEI_REP_BSSID_INFO_HT |
- NEI_REP_BSSID_INFO_DELAYED_BA;
-
- /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+ bssid_info |= NEI_REP_BSSID_INFO_HT;
if (vht)
bssid_info |= NEI_REP_BSSID_INFO_VHT;
}
@@ -278,23 +276,30 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
bssid_info |= NEI_REP_BSSID_INFO_EHT;
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+ hostapd_get_oper_chan_info_of_bss(hapd, &oper_chwidth,
+ &center_freq1_idx, &center_freq2_idx);
+ secondary_channel = hapd->iconf->secondary_channel;
+
+ if (center_freq1_idx == hapd->iconf->channel &&
+ oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ secondary_channel = 0;
+
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
- hapd->iconf->secondary_channel,
- hostapd_get_oper_chwidth(hapd->iconf),
+ secondary_channel,
+ oper_chwidth,
&op_class, &channel) ==
NUM_HOSTAPD_MODES)
return;
- width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
- if (vht) {
- center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
- hapd->iconf);
- if (width == NR_CHAN_WIDTH_80P80)
- center_freq2_idx =
- hostapd_get_oper_centr_freq_seg1_idx(
- hapd->iconf);
- } else if (ht) {
+ width = hostapd_get_nr_chan_width(hapd, ht, vht, he, oper_chwidth,
+ secondary_channel);
+
+ if (width != NR_CHAN_WIDTH_80P80)
+ center_freq2_idx = 0;
+
+ if (!vht && ht) {
+ center_freq1_idx = 0;
ieee80211_freq_to_chan(hapd->iface->freq +
- 10 * hapd->iconf->secondary_channel,
+ 10 * secondary_channel,
&center_freq1_idx);
}
@@ -313,12 +318,13 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
wpabuf_put_le32(nr, bssid_info);
wpabuf_put_u8(nr, op_class);
wpabuf_put_u8(nr, channel);
- wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht,
+ he));
/*
* Wide Bandwidth Channel subelement may be needed to allow the
- * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
- * Figure 9-301.
+ * receiving STA to send packets to the AP. See IEEE Std 802.11-2024,
+ * Figure 9-423 (Wide Bandwidth Channel subelement format).
*/
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
wpabuf_put_u8(nr, 3);
diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.c b/contrib/wpa/src/ap/pmksa_cache_auth.c
index 2fce8383d975..8769032aaa93 100644
--- a/contrib/wpa/src/ap/pmksa_cache_auth.c
+++ b/contrib/wpa/src/ap/pmksa_cache_auth.c
@@ -141,8 +141,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
}
-static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
- struct eapol_state_machine *eapol)
+void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
{
struct vlan_description *vlan_desc;
@@ -471,6 +471,17 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
/**
+ * pmksa_cache_auth_set_ctx - Set the context for PMKSA cache
+ * @cache: Pointer to the PMKSA cache structure
+ * @ctx: Context pointer to be stored in the cache
+ */
+void pmksa_cache_auth_set_ctx(struct rsn_pmksa_cache *cache, void *ctx)
+{
+ cache->ctx = ctx;
+}
+
+
+/**
* pmksa_cache_auth_get - Fetch a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @spa: Supplicant address or %NULL to match any
@@ -644,29 +655,25 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @buf: Buffer for the list
* @len: Length of the buffer
+ * @index: Externally stored index counter
* Returns: Number of bytes written to buffer
*
* This function is used to generate a text format representation of the
* current PMKSA cache contents for the ctrl_iface PMKSA command.
*/
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+ int *index)
{
- int i, ret;
+ int ret;
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
os_get_reltime(&now);
- ret = os_snprintf(pos, buf + len - pos,
- "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
- if (os_snprintf_error(buf + len - pos, ret))
- return pos - buf;
- pos += ret;
- i = 0;
entry = pmksa->pmksa;
while (entry) {
ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
- i, MAC2STR(entry->spa));
+ *index, MAC2STR(entry->spa));
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
@@ -679,6 +686,7 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
return pos - buf;
pos += ret;
entry = entry->next;
+ (*index)++;
}
return pos - buf;
}
diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.h b/contrib/wpa/src/ap/pmksa_cache_auth.h
index e38e7eca66e7..f7b7450faa3d 100644
--- a/contrib/wpa/src/ap/pmksa_cache_auth.h
+++ b/contrib/wpa/src/ap/pmksa_cache_auth.h
@@ -33,9 +33,17 @@ struct rsn_pmksa_cache_entry {
struct radius_class_data radius_class;
u8 eap_type_authsrv;
struct vlan_description *vlan_desc;
+ int sae_vlan_id;
int opportunistic;
u64 acct_multi_session_id;
+#ifdef CONFIG_IEEE80211BE
+ /**
+ * This field is used to identify whether the entry is from ml_pmksa or
+ * pmksa.
+ */
+ bool is_ml;
+#endif /* CONFIG_IEEE80211BE */
};
struct rsn_pmksa_cache;
@@ -45,6 +53,7 @@ struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx), void *ctx);
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+void pmksa_cache_auth_set_ctx(struct rsn_pmksa_cache *cache, void *ctx);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid);
@@ -75,9 +84,12 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
struct radius_das_attrs *attr);
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+ int *index);
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
char *buf, size_t len);
+void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol);
#endif /* PMKSA_CACHE_H */
diff --git a/contrib/wpa/src/ap/rrm.c b/contrib/wpa/src/ap/rrm.c
index fbcddf3f98c9..73771e4c5e11 100644
--- a/contrib/wpa/src/ap/rrm.c
+++ b/contrib/wpa/src/ap/rrm.c
@@ -536,13 +536,14 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
if (!hapd->range_req_token) /* For wraparounds */
hapd->range_req_token++;
- /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+ /* IEEE Std 802.11-2024, 9.6.6.2 (Radio Measurement Request frame
+ * format) */
wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
wpabuf_put_le16(buf, 0); /* Number of Repetitions */
- /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+ /* IEEE Std 802.11-2024, 9.4.2.19 (Measurement Request element) */
wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
len = wpabuf_put(buf, 1); /* Length will be set later */
@@ -554,7 +555,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
- /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+ /* IEEE Std 802.11-2024, 9.4.2.19.19 (FTM Range request) */
wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c
index 13613dbab0d6..a88f275afb0f 100644
--- a/contrib/wpa/src/ap/sta_info.c
+++ b/contrib/wpa/src/ap/sta_info.c
@@ -77,6 +77,24 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
}
+#ifdef CONFIG_IEEE80211BE
+struct sta_info * ap_get_link_sta(struct hostapd_data *hapd,
+ const u8 *link_addr)
+{
+ struct sta_info *link_sta;
+
+ for (link_sta = hapd->sta_list; link_sta; link_sta = link_sta->next) {
+ if (link_sta->mld_info.mld_sta &&
+ ether_addr_equal(link_sta->mld_info.links[hapd->mld_link_id].peer_addr,
+ link_addr))
+ return link_sta;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
#ifdef CONFIG_P2P
struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
{
@@ -191,9 +209,10 @@ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
#ifdef CONFIG_IEEE80211BE
- if (hostapd_sta_is_link_sta(hapd, sta) &&
- !hostapd_drv_link_sta_remove(hapd, sta->addr))
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ hostapd_drv_link_sta_remove(hapd, sta->addr);
return;
+ }
#endif /* CONFIG_IEEE80211BE */
hostapd_drv_sta_remove(hapd, sta->addr);
@@ -201,8 +220,9 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
#ifdef CONFIG_IEEE80211BE
-static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
- struct sta_info *psta)
+
+void set_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta, void *wpa_sm)
{
struct sta_info *lsta;
struct hostapd_data *lhapd;
@@ -215,15 +235,54 @@ static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
continue;
lsta = ap_get_sta(lhapd, psta->addr);
- if (lsta)
- lsta->wpa_sm = NULL;
+ if (lsta) {
+ if (lsta->wpa_sm && lsta->wpa_sm != wpa_sm)
+ wpa_printf(MSG_DEBUG,
+ "AP MLD: Replacing lsta->wpa_sm %p with %p for a partner link",
+ lsta->wpa_sm, wpa_sm);
+ lsta->wpa_sm = wpa_sm;
+ }
+ }
+}
+
+
+void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta)
+{
+ set_wpa_sm_for_each_partner_link(hapd, psta, NULL);
+}
+
+
+void clear_wpa_sm_for_all_sta(struct hostapd_data *hapd,
+ struct wpa_state_machine *wpa_sm)
+{
+ struct hostapd_data *lhapd;
+
+ if (!hapd->mld)
+ return;
+
+ for_each_mld_link(lhapd, hapd) {
+ struct sta_info *sta;
+
+ for (sta = lhapd->sta_list; sta; sta = sta->next) {
+ if (sta->wpa_sm == wpa_sm) {
+ wpa_printf(MSG_DEBUG,
+ "AP MLD: Clearing to be freed wpa_sm=%p pointer from sta=%p",
+ sta->wpa_sm, sta);
+ sta->wpa_sm = NULL;
+ }
+ }
}
}
+
#endif /* CONFIG_IEEE80211BE */
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
+#ifndef CONFIG_NO_VLAN
+ struct hostapd_data *vlan_bss = hapd;
+#endif /* CONFIG_NO_VLAN */
int set_beacon = 0;
accounting_sta_stop(hapd, sta);
@@ -334,22 +393,27 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
+ ap_sta_clear_assoc_timeout(hapd, sta);
sae_clear_retransmit_timer(hapd, sta);
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
+ /* Release group references in case non-association link STA is removed
+ * before association link STA */
+ if (hostapd_sta_is_link_sta(hapd, sta))
+ wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id,
+ false);
if (!ap_sta_is_mld(hapd, sta) ||
hapd->mld_link_id == sta->mld_assoc_link_id) {
- wpa_auth_sta_deinit(sta->wpa_sm);
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
/* Remove references from partner links. */
clear_wpa_sm_for_each_partner_link(hapd, sta);
- }
- /* Release group references in case non-association link STA is removed
- * before association link STA */
- if (hostapd_sta_is_link_sta(hapd, sta))
- wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
+ clear_wpa_sm_for_all_sta(hapd, sm);
+ wpa_auth_sta_deinit(sm);
+ }
#else /* CONFIG_IEEE80211BE */
wpa_auth_sta_deinit(sta->wpa_sm);
#endif /* CONFIG_IEEE80211BE */
@@ -361,13 +425,20 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
#endif /* CONFIG_NO_RADIUS */
#ifndef CONFIG_NO_VLAN
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (!vlan_bss)
+ vlan_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
/*
* sta->wpa_sm->group needs to be released before so that
* vlan_remove_dynamic() can check that no stations are left on the
* AP_VLAN netdev.
*/
if (sta->vlan_id)
- vlan_remove_dynamic(hapd, sta->vlan_id);
+ vlan_remove_dynamic(vlan_bss, sta->vlan_id);
if (sta->vlan_id_bound) {
/*
* Need to remove the STA entry before potentially removing the
@@ -378,7 +449,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
- vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+ vlan_remove_dynamic(vlan_bss, sta->vlan_id_bound);
}
#endif /* CONFIG_NO_VLAN */
@@ -391,7 +462,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
#endif /* CONFIG_P2P */
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
if (sta->gas_dialog) {
int i;
@@ -399,7 +470,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
gas_serv_dialog_clear(&sta->gas_dialog[i]);
os_free(sta->gas_dialog);
}
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
wpabuf_free(sta->wps_ie);
wpabuf_free(sta->p2p_ie);
@@ -418,7 +489,6 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
- os_free(sta->remediation_url);
os_free(sta->t_c_url);
wpabuf_free(sta->hs20_deauth_req);
os_free(sta->hs20_session_info_url);
@@ -467,6 +537,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
#ifdef CONFIG_IEEE80211BE
ap_sta_free_sta_profile(&sta->mld_info);
+ ml_deinit_link_reconf_req(&sta->reconf_req);
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
@@ -474,6 +545,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_free(sta->sae_pw_id);
+
+#ifdef CONFIG_IEEE8021X_AUTH
+ crypto_ecdh_deinit(sta->eap_auth_data.ecdh);
+ wpabuf_clear_free(sta->eap_auth_data.dhss);
+ os_free(sta->eap_auth_data.rsnxe);
+#endif /* CONFIG_IEEE8021X_AUTH */
+
os_free(sta);
}
@@ -580,6 +659,17 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
sta->timeout_next = STA_DISASSOC;
goto skip_poll;
} else if (inactive_sec < max_inactivity) {
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->skip_inactivity_poll == -1) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Force inactivity timeout for station "
+ MACSTR
+ " even though it has been active %is ago",
+ MAC2STR(sta->addr), inactive_sec);
+ sta->timeout_next = STA_DISASSOC;
+ goto skip_poll;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
/* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago",
@@ -791,6 +881,25 @@ void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
}
+static void ap_sta_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->flags & WLAN_STA_ASSOC)
+ return;
+
+ wpa_printf(MSG_DEBUG, "STA " MACSTR
+ " did not complete association in time - remove it",
+ MAC2STR(sta->addr));
+ if (sta->flags & WLAN_STA_AUTH)
+ ap_sta_deauthenticate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ else
+ ap_free_sta(hapd, sta);
+}
+
+
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -821,11 +930,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
}
for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
- if (!hapd->iface->basic_rates)
+ if (!hapd->basic_rates)
break;
- if (hapd->iface->basic_rates[i] < 0)
+ if (hapd->basic_rates[i] <= 0)
break;
- sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5;
+ sta->supported_rates[i] = hapd->basic_rates[i] / 5;
}
sta->supported_rates_len = i;
@@ -856,6 +965,9 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
&sta->probe_ie_taxonomy);
#endif /* CONFIG_TAXONOMY */
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ eloop_register_timeout(60, 0, ap_sta_assoc_timeout, hapd, sta);
+
return sta;
}
@@ -924,7 +1036,8 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
static void ap_sta_disconnect_common(struct hostapd_data *hapd,
- struct sta_info *sta, unsigned int timeout)
+ struct sta_info *sta, unsigned int timeout,
+ bool free_1x)
{
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
@@ -933,17 +1046,21 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG,
"reschedule ap_handle_timer timeout (%u sec) for " MACSTR,
- MAC2STR(sta->addr), timeout);
+ timeout, MAC2STR(sta->addr));
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
- ieee802_1x_free_station(hapd, sta);
+ if (free_1x)
+ ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
- if (!hapd->conf->mld_ap ||
+ if (!ap_sta_is_mld(hapd, sta) ||
hapd->mld_link_id == sta->mld_assoc_link_id) {
- wpa_auth_sta_deinit(sta->wpa_sm);
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
clear_wpa_sm_for_each_partner_link(hapd, sta);
+ clear_wpa_sm_for_all_sta(hapd, sm);
+ wpa_auth_sta_deinit(sm);
}
#else /* CONFIG_IEEE80211BE */
wpa_auth_sta_deinit(sta->wpa_sm);
@@ -953,6 +1070,18 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd,
}
+static void ap_sta_disassociate_common(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
+{
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
struct sta_info *sta, u16 reason)
{
@@ -970,14 +1099,9 @@ static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
sta->timeout_next = STA_DEAUTH;
}
- ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
-
- sta->disassoc_reason = reason;
- sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
- eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
- eloop_register_timeout(hapd->iface->drv_flags &
- WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
- ap_sta_disassoc_cb_timeout, hapd, sta);
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC,
+ true);
+ ap_sta_disassociate_common(hapd, sta, reason);
}
@@ -993,37 +1117,59 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
}
-static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+static void ap_sta_deauthenticate_common(struct hostapd_data *hapd,
struct sta_info *sta, u16 reason)
{
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
- /* Deauthentication is not used in DMG/IEEE 802.11ad;
- * disassociate the STA instead. */
- ap_sta_disassociate(hapd, sta, reason);
- return;
- }
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
+{
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
sta->timeout_next = STA_REMOVE;
- ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+ true);
+ ap_sta_deauthenticate_common(hapd, sta, reason);
+}
- sta->deauth_reason = reason;
- sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
- eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
- eloop_register_timeout(hapd->iface->drv_flags &
- WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
- ap_sta_deauth_cb_timeout, hapd, sta);
+
+static void ap_sta_handle_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: disconnect STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ sta->timeout_next = STA_REMOVE;
+
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+ false);
+ ap_sta_deauthenticate_common(hapd, sta, reason);
}
+enum ap_sta_disconnect_op {
+ AP_STA_DEAUTHENTICATE,
+ AP_STA_DISASSOCIATE,
+ AP_STA_DISCONNECT
+};
+
static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
struct sta_info *sta, u16 reason,
- bool disassoc)
+ enum ap_sta_disconnect_op op)
{
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *assoc_hapd, *tmp_hapd;
@@ -1031,7 +1177,7 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
unsigned int i, link_id;
struct hapd_interfaces *interfaces;
- if (!hostapd_is_mld_ap(hapd))
+ if (!hostapd_is_multiple_link_mld(hapd))
return false;
/*
@@ -1072,25 +1218,30 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
tmp_sta->aid != assoc_sta->aid)
continue;
- if (disassoc)
+ if (op == AP_STA_DISASSOCIATE)
ap_sta_handle_disassociate(tmp_hapd,
tmp_sta,
reason);
- else
+ else if (op == AP_STA_DEAUTHENTICATE)
ap_sta_handle_deauthenticate(tmp_hapd,
tmp_sta,
reason);
-
+ else
+ ap_sta_handle_disconnect(tmp_hapd,
+ tmp_sta,
+ reason);
break;
}
}
}
/* Disconnect the station on which the association was performed. */
- if (disassoc)
+ if (op == AP_STA_DISASSOCIATE)
ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
- else
+ else if (op == AP_STA_DEAUTHENTICATE)
ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+ else
+ ap_sta_handle_disconnect(assoc_hapd, assoc_sta, reason);
return true;
#else /* CONFIG_IEEE80211BE */
@@ -1102,7 +1253,7 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
- if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+ if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISASSOCIATE))
return;
ap_sta_handle_disassociate(hapd, sta, reason);
@@ -1112,7 +1263,15 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
- if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ ap_sta_disassociate(hapd, sta, reason);
+ return;
+ }
+
+ if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DEAUTHENTICATE))
return;
ap_sta_handle_deauthenticate(hapd, sta, reason);
@@ -1139,10 +1298,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd,
static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd)
{
struct hostapd_vlan *vlan;
+ struct hostapd_data *vlan_bss = hapd;
int vlan_id = MAX_VLAN_ID + 2;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (!vlan_bss)
+ vlan_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
retry:
- for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) {
if (vlan->vlan_id == vlan_id) {
vlan_id++;
goto retry;
@@ -1152,12 +1320,22 @@ retry:
}
-int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
- struct vlan_description *vlan_desc)
+static int ap_sta_set_vlan_helper(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct vlan_description *vlan_desc)
{
struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
+ struct hostapd_data *vlan_bss = hapd;
int old_vlan_id, vlan_id = 0, ret = 0;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (!vlan_bss)
+ vlan_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
/* Check if there is something to do */
if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
/* This sta is lacking its own vif */
@@ -1174,7 +1352,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
/* find a free vlan_id sufficiently big */
vlan_id = ap_sta_get_free_vlan_id(hapd);
/* Get wildcard VLAN */
- for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) {
if (vlan->vlan_id == VLAN_ID_WILDCARD)
break;
}
@@ -1188,7 +1366,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
goto done;
}
} else if (vlan_desc && vlan_desc->notempty) {
- for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) {
if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
break;
if (vlan->vlan_id == VLAN_ID_WILDCARD)
@@ -1201,10 +1379,10 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
vlan_id = vlan_desc->untagged;
if (vlan_desc->tagged[0]) {
/* Tagged VLAN configuration */
- vlan_id = ap_sta_get_free_vlan_id(hapd);
+ vlan_id = ap_sta_get_free_vlan_id(vlan_bss);
}
} else {
- hostapd_logger(hapd, sta->addr,
+ hostapd_logger(vlan_bss, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"missing vlan and wildcard for vlan=%d%s",
@@ -1217,9 +1395,9 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
}
if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
- vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
+ vlan = vlan_add_dynamic(vlan_bss, vlan, vlan_id, vlan_desc);
if (vlan == NULL) {
- hostapd_logger(hapd, sta->addr,
+ hostapd_logger(vlan_bss, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"could not add dynamic VLAN interface for vlan=%d%s",
@@ -1231,13 +1409,13 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
goto done;
}
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"added new dynamic VLAN interface '%s'",
vlan->ifname);
} else if (vlan && vlan->dynamic_vlan > 0) {
vlan->dynamic_vlan++;
- hostapd_logger(hapd, sta->addr,
+ hostapd_logger(vlan_bss, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"updated existing dynamic VLAN interface '%s'",
@@ -1249,24 +1427,63 @@ done:
sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL;
if (vlan_id != old_vlan_id && old_vlan_id)
- vlan_remove_dynamic(hapd, old_vlan_id);
+ vlan_remove_dynamic(vlan_bss, old_vlan_id);
return ret;
}
-int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ struct vlan_description *vlan_desc)
+{
+ int ret;
+#ifdef CONFIG_IEEE80211BE
+ size_t i;
+ struct hapd_interfaces *interfaces = hapd->iface->interfaces;
+#endif /* CONFIG_IEEE80211BE */
+
+ ret = ap_sta_set_vlan_helper(hapd, sta, vlan_desc);
+ if (ret)
+ return ret;
+#ifdef CONFIG_IEEE80211BE
+ for (i = 0; interfaces && i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ hapd == tmp_hapd ||
+ !hostapd_is_ml_partner(hapd, tmp_hapd))
+ continue;
+
+ tmp_sta = ap_get_sta(tmp_hapd, sta->addr);
+ if (tmp_sta)
+ ap_sta_set_vlan_helper(tmp_hapd, tmp_sta, vlan_desc);
+ }
+
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
+static int ap_sta_bind_vlan_helper(struct hostapd_data *hapd,
+ struct sta_info *sta)
{
#ifndef CONFIG_NO_VLAN
const char *iface;
struct hostapd_vlan *vlan = NULL;
+ struct hostapd_data *vlan_bss = hapd;
int ret;
int old_vlanid = sta->vlan_id_bound;
int mld_link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
mld_link_id = hapd->mld_link_id;
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (!vlan_bss)
+ vlan_bss = hapd;
+ }
#endif /* CONFIG_IEEE80211BE */
if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) {
@@ -1281,7 +1498,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
iface = hapd->conf->ssid.vlan;
if (sta->vlan_id > 0) {
- for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) {
if (vlan->vlan_id == sta->vlan_id)
break;
}
@@ -1299,7 +1516,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->vlan_id > 0 && !vlan &&
!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
"binding station to (vlan_id=%d)",
sta->vlan_id);
@@ -1307,7 +1524,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
goto done;
} else if (vlan && vlan->dynamic_vlan > 0) {
vlan->dynamic_vlan++;
- hostapd_logger(hapd, sta->addr,
+ hostapd_logger(vlan_bss, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"updated existing dynamic VLAN interface '%s'",
@@ -1318,24 +1535,25 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
sta->vlan_id_bound = sta->vlan_id;
skip_counting:
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "binding station to interface "
"'%s'", iface);
- if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+ if (wpa_auth_sta_set_vlan(sta->wpa_sm, hapd->wpa_auth,
+ sta->vlan_id) < 0)
wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id,
mld_link_id);
if (ret < 0) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
"entry to vlan_id=%d", sta->vlan_id);
}
/* During 1x reauth, if the vlan id changes, then remove the old id. */
if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
- vlan_remove_dynamic(hapd, old_vlanid);
+ vlan_remove_dynamic(vlan_bss, old_vlanid);
done:
return ret;
@@ -1345,6 +1563,60 @@ done:
}
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int ret;
+#ifdef CONFIG_IEEE80211BE
+ size_t i;
+ struct hapd_interfaces *interfaces = hapd->iface->interfaces;
+#endif /* CONFIG_IEEE80211BE */
+
+ ret = ap_sta_bind_vlan_helper(hapd, sta);
+ if (ret)
+ return ret;
+#ifdef CONFIG_IEEE80211BE
+ for (i = 0; interfaces && i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ hapd == tmp_hapd ||
+ !hostapd_is_ml_partner(hapd, tmp_hapd))
+ continue;
+
+ tmp_sta = ap_get_sta(tmp_hapd, sta->addr);
+ if (tmp_sta)
+ ap_sta_bind_vlan_helper(tmp_hapd, tmp_sta);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
+void ap_sta_set_sa_query_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int value)
+{
+ sta->sa_query_timed_out = value;
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ struct hostapd_data *lhapd;
+
+ for_each_mld_link(lhapd, hapd) {
+ struct sta_info *lsta;
+
+ if (lhapd == hapd)
+ continue;
+
+ lsta = ap_get_sta(lhapd, sta->addr);
+ if (lsta)
+ lsta->sa_query_timed_out = value;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
{
u32 tu;
@@ -1357,7 +1629,7 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"association SA Query timed out");
- sta->sa_query_timed_out = 1;
+ ap_sta_set_sa_query_timeout(hapd, sta, 1);
os_free(sta->sa_query_trans_id);
sta->sa_query_trans_id = NULL;
sta->sa_query_count = 0;
@@ -1529,10 +1801,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
char dpp_pkhash_buf[100];
char keyid_buf[100];
char ip_addr[100];
+ char vlanid_buf[20];
dpp_pkhash_buf[0] = '\0';
keyid_buf[0] = '\0';
ip_addr[0] = '\0';
+ vlanid_buf[0] = '\0';
+
#ifdef CONFIG_P2P
if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
os_snprintf(ip_addr, sizeof(ip_addr),
@@ -1561,15 +1836,21 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
dpp_pkhash, SHA256_MAC_LEN);
}
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
- buf, ip_addr, keyid_buf, dpp_pkhash_buf);
+#ifndef CONFIG_NO_VLAN
+ if (sta->vlan_id)
+ os_snprintf(vlanid_buf, sizeof(vlanid_buf),
+ " vlanid=%u", sta->vlan_id);
+#endif /* CONFIG_NO_VLAN */
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
+ buf, ip_addr, keyid_buf, dpp_pkhash_buf, vlanid_buf);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s%s%s%s",
+ AP_STA_CONNECTED "%s%s%s%s%s",
buf, ip_addr, keyid_buf,
- dpp_pkhash_buf);
+ dpp_pkhash_buf, vlanid_buf);
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
@@ -1596,12 +1877,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd,
}
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+bool ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
- return;
+ return false;
ap_sta_set_authorized_event(hapd, sta, authorized);
+ return true;
}
@@ -1625,41 +1907,19 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
if (sta == NULL)
return;
- ap_sta_set_authorized(hapd, sta, 0);
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- hostapd_set_sta_flags(hapd, sta);
- wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
- ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
- wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
- hapd->conf->iface, __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DEAUTH);
- eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
- ap_handle_timer, hapd, sta);
- sta->timeout_next = STA_REMOVE;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
/* Deauthentication is not used in DMG/IEEE 802.11ad;
* disassociate the STA instead. */
- sta->disassoc_reason = reason;
- sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
- eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
- eloop_register_timeout(hapd->iface->drv_flags &
- WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
- 2 : 0, 0, ap_sta_disassoc_cb_timeout,
- hapd, sta);
+ ap_sta_disassociate_common(hapd, sta, reason);
return;
}
- sta->deauth_reason = reason;
- sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
- eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
- eloop_register_timeout(hapd->iface->drv_flags &
- WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
- ap_sta_deauth_cb_timeout, hapd, sta);
+ if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISCONNECT))
+ return;
+
+ ap_sta_handle_disconnect(hapd, sta, reason);
}
@@ -1712,13 +1972,20 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
}
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_sta_assoc_timeout, hapd, sta);
+}
+
+
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
{
int res;
buf[0] = '\0';
res = os_snprintf(buf, buflen,
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1741,6 +2008,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
(flags & WLAN_STA_EHT ? "[EHT]" : ""),
(flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+ (flags & WLAN_STA_SPP_AMSDU ? "[SPP-A-MSDU]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
"[WNM_SLEEP_MODE]" : ""));
if (os_snprintf_error(buflen, res))
@@ -1825,8 +2093,12 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
{
const u8 *mld_link_addr = NULL;
- bool mld_link_sta = false;
+ bool mld_link_sta = false, epp_sta = false;
+ u16 eml_cap = 0;
+#ifdef CONFIG_ENC_ASSOC
+ epp_sta = sta->epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
/*
* If a station that is already associated to the AP, is trying to
* authenticate again, remove the STA entry, in order to make sure the
@@ -1841,6 +2113,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+ eml_cap = sta->mld_info.common_info.eml_capa;
/*
* In case the AP is affiliated with an AP MLD, we need to
@@ -1859,7 +2132,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
sta->supported_rates_len,
0, NULL, NULL, NULL, 0, NULL, 0, NULL,
sta->flags, 0, 0, 0, 0,
- mld_link_addr, mld_link_sta)) {
+ mld_link_addr, mld_link_sta, eml_cap, epp_sta)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h
index 84629358cc22..9810eb22f9a4 100644
--- a/contrib/wpa/src/ap/sta_info.h
+++ b/contrib/wpa/src/ap/sta_info.h
@@ -22,6 +22,7 @@
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_SPP_AMSDU BIT(2)
#define WLAN_STA_AUTHORIZED BIT(5)
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
#define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -70,6 +71,25 @@ struct pending_eapol_rx {
enum frame_encryption encrypted;
};
+struct eap_over_auth_data {
+ int akm;
+ int cipher;
+ u16 group;
+ u16 auth_transaction;
+ u8 snonce[WPA_NONCE_LEN];
+ u8 anonce[WPA_NONCE_LEN];
+ u8 *rsnxe;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ struct wpa_ptk ptk;
+ size_t rsnxe_len;
+ struct crypto_ecdh *ecdh;
+ struct wpabuf *dhss;
+ bool add_mic;
+ u8 epp_pmkid_cur[PMKID_LEN];
+ u8 epp_pmkid_next[PMKID_LEN];
+};
+
#define EHT_ML_MAX_STA_PROF_LEN 1024
struct mld_info {
bool mld_sta;
@@ -81,20 +101,7 @@ struct mld_info {
u16 mld_capa;
} common_info;
- struct mld_link_info {
- u8 valid:1;
- u8 nstr_bitmap_len:2;
- u8 local_addr[ETH_ALEN];
- u8 peer_addr[ETH_ALEN];
-
- u8 nstr_bitmap[2];
-
- u16 capability;
-
- u16 status;
- u16 resp_sta_profile_len;
- u8 *resp_sta_profile;
- } links[MAX_NUM_MLD_LINKS];
+ struct mld_link_info links[MAX_NUM_MLD_LINKS];
};
struct sta_info {
@@ -111,6 +118,16 @@ struct sta_info {
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int supported_rates_len;
u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+#ifdef CONFIG_ENC_ASSOC
+ bool epp_sta; /* Indicates if the station is an EPP peer */
+#endif /* CONFIG_ENC_ASSOC */
+#ifdef CONFIG_PMKSA_PRIVACY
+ u8 snonce[NONCE_LEN]; /* SNonce to compute next PMKID if
+ * PMKID caching privacy is on */
+ u8 anonce[NONCE_LEN]; /* ANonce to compute next PMKID if
+ * PMKID caching privacy is on */
+ u8 epp_pmkid_next[PMKID_LEN];
+#endif /* CONFIG_PMKSA_PRIVACY */
#ifdef CONFIG_MESH
enum mesh_plink_state plink_state;
@@ -144,7 +161,6 @@ struct sta_info {
unsigned int ht_20mhz_set:1;
unsigned int no_p2p_set:1;
unsigned int qos_map_enabled:1;
- unsigned int remediation:1;
unsigned int hs20_deauth_requested:1;
unsigned int hs20_deauth_on_ack:1;
unsigned int session_timeout_set:1;
@@ -230,8 +246,6 @@ struct sta_info {
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
struct wpabuf *roaming_consortium;
- u8 remediation_method;
- char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
@@ -277,7 +291,7 @@ struct sta_info {
#endif /* CONFIG_TAXONOMY */
#ifdef CONFIG_FILS
- u8 fils_snonce[FILS_NONCE_LEN];
+ u8 fils_snonce[NONCE_LEN];
u8 fils_session[FILS_SESSION_LEN];
u8 fils_erp_pmkid[PMKID_LEN];
u8 *fils_pending_assoc_req;
@@ -331,10 +345,20 @@ struct sta_info {
#ifdef CONFIG_IEEE80211BE
struct mld_info mld_info;
u8 mld_assoc_link_id;
+ struct link_reconf_req_list *reconf_req;
#endif /* CONFIG_IEEE80211BE */
u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
* units of 1000 TUs */
+
+ u64 last_known_sta_id_timestamp;
+
+ struct wpabuf *sae_pw_id;
+ unsigned int sae_pw_id_counter;
+
+#ifdef CONFIG_IEEE8021X_AUTH
+ struct eap_over_auth_data eap_auth_data;
+#endif /* CONFIG_IEEE8021X_AUTH */
};
@@ -359,6 +383,8 @@ int ap_for_each_sta(struct hostapd_data *hapd,
void *ctx),
void *ctx);
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_link_sta(struct hostapd_data *hapd,
+ const u8 *link_addr);
struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
@@ -387,6 +413,8 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
struct vlan_description *vlan_desc);
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_set_sa_query_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int value);
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
struct sta_info *sta);
@@ -399,7 +427,7 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
int authorized);
void ap_sta_set_authorized_event(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
-void ap_sta_set_authorized(struct hostapd_data *hapd,
+bool ap_sta_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
static inline int ap_sta_is_authorized(struct sta_info *sta)
{
@@ -410,6 +438,8 @@ void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
struct sta_info *sta);
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta);
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
@@ -442,5 +472,32 @@ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
void ap_sta_free_sta_profile(struct mld_info *info);
void hostapd_free_link_stas(struct hostapd_data *hapd);
+void set_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta, void *wpa_sm);
+void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta);
+void clear_wpa_sm_for_all_sta(struct hostapd_data *hapd,
+ struct wpa_state_machine *wpa_sm);
+static inline bool ap_sta_is_epp(const struct sta_info *sta)
+{
+#ifdef CONFIG_ENC_ASSOC
+ return sta && sta->epp_sta;
+#else /* CONFIG_ENC_ASSOC */
+ return false;
+#endif /* CONFIG_ENC_ASSOC */
+}
+
+static inline bool ap_sta_support_enc_assoc(struct hostapd_data *hapd,
+ const u8 *rsnxe, size_t rsnxe_len)
+{
+#ifdef CONFIG_ENC_ASSOC
+ return (hapd->conf->assoc_frame_encryption &&
+ ieee802_11_rsnx_capab_len(rsnxe,
+ rsnxe_len,
+ WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION));
+#else /* CONFIG_ENC_ASSOC */
+ return false;
+#endif /* CONFIG_ENC_ASSOC */
+}
#endif /* STA_INFO_H */
diff --git a/contrib/wpa/src/ap/vlan_full.c b/contrib/wpa/src/ap/vlan_full.c
index 19aa3c649a5d..687fa9c23b63 100644
--- a/contrib/wpa/src/ap/vlan_full.c
+++ b/contrib/wpa/src/ap/vlan_full.c
@@ -462,11 +462,19 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
{
char br_name[IFNAMSIZ];
struct hostapd_vlan *vlan;
+ struct hostapd_data *vlan_bss = hapd;
int untagged, *tagged, i, notempty;
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
- for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (vlan_bss == NULL)
+ vlan_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) {
if (vlan->configured ||
os_strcmp(ifname, vlan->ifname) != 0)
continue;
@@ -563,10 +571,20 @@ static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
{
- struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+ struct hostapd_vlan *first, *prev, *vlan;
+ struct hostapd_data *vlan_bss = hapd;
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ vlan_bss = hostapd_mld_get_first_bss(hapd);
+ if (!vlan_bss)
+ vlan_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ vlan = vlan_bss->conf->vlan;
first = prev = vlan;
while (vlan) {
diff --git a/contrib/wpa/src/ap/wnm_ap.c b/contrib/wpa/src/ap/wnm_ap.c
index af8cccaefcde..90922cd85c75 100644
--- a/contrib/wpa/src/ap/wnm_ap.c
+++ b/contrib/wpa/src/ap/wnm_ap.c
@@ -148,7 +148,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26
#define MAX_BIGTK_SUBELEM_LEN 26
- mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len +
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
MAX_BIGTK_SUBELEM_LEN +
oci_ie_len);
@@ -740,7 +740,7 @@ static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
switch (report_ie->type) {
#ifdef CONFIG_IEEE80211AX
case WNM_EVENT_TYPE_BSS_COLOR_COLLISION:
- if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ if (!hostapd_is_he_enabled(hapd))
return;
if (report_ie->len <
fixed_field_len + tsf_len + 8) {
@@ -756,7 +756,7 @@ static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
hostapd_switch_color(hapd->iface->bss[0], bitmap);
break;
case WNM_EVENT_TYPE_BSS_COLOR_IN_USE:
- if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ if (!hostapd_is_he_enabled(hapd))
return;
if (report_ie->len < fixed_field_len + tsf_len + 1) {
wpa_printf(MSG_DEBUG,
@@ -1030,7 +1030,13 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
if (disassoc_timer) {
#ifdef CONFIG_IEEE80211BE
- if (ap_sta_is_mld(hapd, sta)) {
+ /* Link removal is scheduled only when the Link Removal Imminent
+ * field is set to 1 in BTM as per IEEE Std 802.11be-2024,
+ * 9.6.13.9 (BSS Transition Management Request frame format);
+ * else schedule full disconnection.
+ */
+ if (ap_sta_is_mld(hapd, sta) &&
+ (req_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT)) {
int i;
unsigned int links = 0;
diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c
index 504ecf13428f..d736d1bc9a47 100644
--- a/contrib/wpa/src/ap/wpa_auth.c
+++ b/contrib/wpa/src/ap/wpa_auth.c
@@ -38,6 +38,7 @@
#define STATE_MACHINE_DATA struct wpa_state_machine
#define STATE_MACHINE_DEBUG_PREFIX "WPA"
#define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
+#define KDE_ALL_LINKS 0xffff
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -54,8 +55,6 @@ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_request_new_ptk(struct wpa_state_machine *sm);
-static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group);
static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
@@ -63,16 +62,18 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
struct wpa_ptk *ptk, int force_sha256,
u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
size_t *key_len, bool no_kdk);
-static void wpa_group_free(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group);
static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+#ifdef CONFIG_IEEE80211BE
+static void wpa_group_put_vlan(struct wpa_authenticator *wpa_auth,
+ int vlan_id);
+#endif /* CONFIG_IEEE80211BE */
static int ieee80211w_kde_len(struct wpa_state_machine *sm);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group);
+static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
static const u32 eapol_key_timeout_first = 100; /* ms */
@@ -112,34 +113,41 @@ static void wpa_gkeydone_sta(struct wpa_state_machine *sm)
int link_id;
#endif /* CONFIG_IEEE80211BE */
- if (!sm->wpa_auth)
- return;
-
- sm->wpa_auth->group->GKeyDoneStations--;
+ sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
#ifdef CONFIG_IEEE80211BE
- for_each_sm_auth(sm, link_id)
- sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations--;
+ for_each_sm_auth(sm, link_id) {
+ struct wpa_authenticator *partner_auth =
+ sm->mld_links[link_id].wpa_auth;
+ struct wpa_group *partner_group =
+ wpa_select_vlan_wpa_group(partner_auth->group,
+ sm->group->vlan_id);
+ partner_group->GKeyDoneStations--;
+ }
#endif /* CONFIG_IEEE80211BE */
}
#ifdef CONFIG_IEEE80211BE
-void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
- int release_link_id)
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id,
+ bool rejected)
{
- int link_id;
+ struct wpa_authenticator *wpa_auth;
+ struct mld_link *link;
- if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
+ if (!sm || link_id >= MAX_NUM_MLD_LINKS)
return;
- for_each_sm_auth(sm, link_id) {
- if (link_id == release_link_id) {
- wpa_group_put(sm->mld_links[link_id].wpa_auth,
- sm->mld_links[link_id].wpa_auth->group);
- sm->mld_links[link_id].wpa_auth = NULL;
+ link = &sm->mld_links[link_id];
+ if (link->valid) {
+ link->valid = false;
+ link->rejected = rejected;
+ wpa_auth = link->wpa_auth;
+ if (wpa_auth) {
+ link->wpa_auth = NULL;
+ wpa_group_put_vlan(wpa_auth, sm->group->vlan_id);
}
}
}
@@ -367,6 +375,25 @@ static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_MESH */
+static inline int wpa_auth_get_drv_flags(struct wpa_authenticator *wpa_auth,
+ u64 *drv_flags, u64 *drv_flags2)
+{
+ if (!wpa_auth->cb->get_drv_flags)
+ return -1;
+ return wpa_auth->cb->get_drv_flags(wpa_auth->cb_ctx, drv_flags,
+ drv_flags2);
+}
+
+
+static bool wpa_auth_4way_handshake_offload(struct wpa_authenticator *wpa_auth)
+{
+ u64 drv_flags = 0, drv_flags2 = 0;
+
+ return wpa_auth_get_drv_flags(wpa_auth, &drv_flags, &drv_flags2) == 0 &&
+ (drv_flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK);
+}
+
+
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
@@ -387,6 +414,20 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
}
+#ifdef CONFIG_IEEE80211BE
+static int wpa_auth_for_each_partner_auth(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_authenticator *a,
+ void *ctx),
+ void *cb_ctx)
+{
+ if (!wpa_auth->cb->for_each_partner_auth)
+ return 0;
+ return wpa_auth->cb->for_each_partner_auth(wpa_auth->cb_ctx, cb,
+ cb_ctx);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
const u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk)
@@ -616,7 +657,7 @@ void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
MACSTR " (%d seconds)",
MAC2STR(wpa_auth_get_spa(sm)),
sm->wpa_auth->conf.wpa_ptk_rekey);
- eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm);
eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
wpa_rekey_ptk, sm->wpa_auth, sm);
}
@@ -631,11 +672,39 @@ static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
}
+
+#ifdef CONFIG_IEEE80211BE
+static int wpa_auth_pmksa_clear_auth_cb(struct wpa_authenticator *wpa_auth,
+ void *ctx)
+{
+ return wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, ctx);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
void *ctx)
{
struct wpa_authenticator *wpa_auth = ctx;
+
+#ifdef CONFIG_IEEE80211BE
+ if (!entry->is_ml) {
+ wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+ } else {
+ /* Clear stale PMKSA references across partner MLD links. */
+ wpa_auth_for_each_partner_auth(wpa_auth,
+ wpa_auth_pmksa_clear_auth_cb,
+ entry);
+ }
+#else /* CONFIG_IEEE80211BE */
wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+#endif /* CONFIG_IEEE80211BE */
+
+ /* Remove matching PMKID from the driver, if it had been added, e.g.,
+ * by external SAE authentication */
+ if (wpa_auth->cb->remove_pmkid)
+ wpa_auth->cb->remove_pmkid(wpa_auth->cb_ctx, entry->spa,
+ entry->pmkid);
}
@@ -721,6 +790,19 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
}
+static void wpa_deinit_groups(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *prev;
+
+ group = wpa_auth->group;
+ while (group) {
+ prev = group;
+ group = group->next;
+ bin_clear_free(prev, sizeof(*prev));
+ }
+}
+
+
/**
* wpa_init - Initialize WPA authenticator
* @addr: Authenticator address
@@ -756,36 +838,48 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
- os_free(wpa_auth);
- return NULL;
+ goto fail;
}
wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
- if (!wpa_auth->group) {
- os_free(wpa_auth->wpa_ie);
- os_free(wpa_auth);
- return NULL;
- }
+ if (!wpa_auth->group)
+ goto fail;
+ /* Per-link PMKSA cache */
wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
wpa_auth);
if (!wpa_auth->pmksa) {
wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
- os_free(wpa_auth->group);
- os_free(wpa_auth->wpa_ie);
- os_free(wpa_auth);
- return NULL;
+ goto fail;
}
+#ifdef CONFIG_IEEE80211BE
+ /* MLD-level PMKSA cache */
+ if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+ wpa_auth->ml_pmksa = pmksa_cache_auth_init(
+ wpa_auth_pmksa_free_cb, wpa_auth);
+ if (!wpa_auth->ml_pmksa) {
+ wpa_printf(MSG_ERROR,
+ "MLD-level PMKSA cache initialization failed.");
+ goto fail;
+ }
+ } else if (wpa_auth->is_ml) {
+ struct wpa_authenticator *pa = wpa_get_primary_auth(wpa_auth);
+
+ if (!pa) {
+ wpa_printf(MSG_ERROR,
+ "Could not find primary authenticator.");
+ goto fail;
+ }
+ wpa_auth->ml_pmksa = pa->ml_pmksa;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (!wpa_auth->ft_pmk_cache) {
wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
- os_free(wpa_auth->group);
- os_free(wpa_auth->wpa_ie);
- pmksa_cache_auth_deinit(wpa_auth->pmksa);
- os_free(wpa_auth);
- return NULL;
+ goto fail;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -820,14 +914,23 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
if (conf->tx_bss_auth && conf->beacon_prot) {
conf->tx_bss_auth->non_tx_beacon_prot = true;
- if (!conf->tx_bss_auth->conf.beacon_prot)
- conf->tx_bss_auth->conf.beacon_prot = true;
if (!conf->tx_bss_auth->conf.group_mgmt_cipher)
conf->tx_bss_auth->conf.group_mgmt_cipher =
conf->group_mgmt_cipher;
}
return wpa_auth;
+
+fail:
+ wpa_deinit_groups(wpa_auth);
+ os_free(wpa_auth->wpa_ie);
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->primary_auth)
+ pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+ os_free(wpa_auth);
+ return NULL;
}
@@ -863,16 +966,36 @@ static void wpa_auth_free_conf(struct wpa_auth_config *conf)
*/
void wpa_deinit(struct wpa_authenticator *wpa_auth)
{
- struct wpa_group *group, *prev;
+#ifdef CONFIG_IEEE80211BE
+ struct wpa_authenticator *next_pa;
+#endif /* CONFIG_IEEE80211BE */
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
-
- /* TODO: Assign ML primary authenticator to next link authenticator and
- * start rekey timer. */
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+ next_pa = wpa_auth->cb->next_primary_auth(wpa_auth->cb_ctx);
+
+ if (!next_pa) {
+ /* Deinit PMKSA entry list if last link */
+ pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+ } else {
+ /* Assign ML primary authenticator to the next link
+ * authenticator and start rekey timer.
+ */
+ next_pa->primary_auth = true;
+ pmksa_cache_auth_set_ctx(next_pa->ml_pmksa, next_pa);
+ if (next_pa->conf.wpa_group_rekey)
+ eloop_register_timeout(
+ next_pa->conf.wpa_group_rekey,
+ 0, wpa_rekey_gtk, next_pa, NULL);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
wpa_auth->ft_pmk_cache = NULL;
@@ -883,16 +1006,8 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
bitfield_free(wpa_auth->ip_pool);
#endif /* CONFIG_P2P */
-
os_free(wpa_auth->wpa_ie);
-
- group = wpa_auth->group;
- while (group) {
- prev = group;
- group = group->next;
- bin_clear_free(prev, sizeof(*prev));
- }
-
+ wpa_deinit_groups(wpa_auth);
wpa_auth_free_conf(&wpa_auth->conf);
os_free(wpa_auth);
}
@@ -960,12 +1075,51 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
}
+#ifdef CONFIG_ENC_ASSOC
+void wpa_store_eppke_pmk_ptk_sm(struct wpa_state_machine *sm,
+ const struct wpa_ptk *ptk, const u8 *pmk,
+ size_t pmk_len)
+{
+ os_memcpy(&sm->PTK, ptk, sizeof(struct wpa_ptk));
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ sm->PTK_valid = true;
+ sm->pairwise_set = true;
+ sm->hash_alg = ptk->hash_alg;
+}
+#endif /* CONFIG_ENC_ASSOC */
+
+
int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm)
{
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return -1;
+#ifdef CONFIG_ENC_ASSOC
+ if (sm->auth_alg == WLAN_AUTH_EPPKE) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "EPPKE authentication already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = true;
+ return 0;
+ }
+#endif /* CONFIG_ENC_ASSOC */
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (wpa_auth->conf.assoc_frame_encryption &&
+ sm->auth_alg == WLAN_AUTH_802_1X &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "IEEE 802.1X (EAP) over Authentication frames already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = true;
+ return 0;
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
#ifdef CONFIG_IEEE80211R_AP
if (sm->ft_completed) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
@@ -1002,7 +1156,13 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
if (wpa_sm_step(sm) == 1)
return 1; /* should not really happen */
sm->Init = false;
- sm->AuthenticationRequest = true;
+
+ if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "Skip EAPOL for 4-way handshake offload case");
+ else
+ sm->AuthenticationRequest = true;
+
return wpa_sm_step(sm);
}
@@ -1046,17 +1206,22 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm->rsnxe);
+ os_free(sm->rsn_selection);
#ifdef CONFIG_IEEE80211BE
for_each_sm_auth(sm, link_id) {
- wpa_group_put(sm->mld_links[link_id].wpa_auth,
- sm->mld_links[link_id].wpa_auth->group);
+ struct wpa_authenticator *wpa_auth;
+
+ wpa_auth = sm->mld_links[link_id].wpa_auth;
sm->mld_links[link_id].wpa_auth = NULL;
+ sm->mld_links[link_id].valid = false;
+ wpa_group_put_vlan(wpa_auth, sm->group->vlan_id);
}
#endif /* CONFIG_IEEE80211BE */
wpa_group_put(sm->wpa_auth, sm->group);
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
#endif /* CONFIG_DPP2 */
+ wpabuf_free(sm->sae_pw_id);
bin_clear_free(sm, sizeof(*sm));
}
@@ -1086,10 +1251,10 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
primary_auth, NULL);
}
- eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm);
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
- eloop_cancel_timeout(wpa_rekey_ptk, wpa_auth, sm);
+ eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm);
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_sta_deinit(sm);
#endif /* CONFIG_IEEE80211R_AP */
@@ -1368,6 +1533,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
forced_memzero(&PTK, sizeof(PTK));
sm->PTK_valid = true;
+ sm->hash_alg = sm->PTK.hash_alg;
ret = 0;
fail:
@@ -1551,7 +1717,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
- mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
keyhdrlen = sizeof(*key) + mic_len + 2;
if (data_len < sizeof(*hdr) + keyhdrlen) {
@@ -1802,7 +1968,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
continue_fuzz:
#endif /* TEST_FUZZ */
sm->MICVerified = true;
- eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm);
sm->pending_1_of_4_timeout = 0;
}
@@ -1886,7 +2052,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
sm->EAPOLKeyReceived = true;
sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
- os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+ if (msg == PAIRWISE_2)
+ os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
wpa_sm_step(sm);
out:
@@ -1978,7 +2145,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
int i;
u8 *key_mic, *key_data;
- mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
keyhdrlen = sizeof(*key) + mic_len + 2;
len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
@@ -2064,6 +2231,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (key_rsc)
os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_key_reserved_random &&
+ random_get_bytes(key->key_id, sizeof(key->key_id)) < 0)
+ os_memset(key->key_id, 0x11, sizeof(key->key_id));
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (kde && !encr) {
os_memcpy(key_data, kde, kde_len);
WPA_PUT_BE16(key_mic + mic_len, kde_len);
@@ -2085,6 +2258,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
1, aad, aad_len, key_mic + 2) < 0) {
wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+ os_free(hdr);
return;
}
@@ -2155,7 +2329,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
- sm->wpa_key_mgmt, version,
+ sm->wpa_key_mgmt, sm->hash_alg, version,
(u8 *) hdr, len, key_mic) < 0) {
os_free(hdr);
return;
@@ -2255,7 +2429,7 @@ static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
u16 key_info;
int ret = 0;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
- size_t mic_len = wpa_mic_len(akmp, pmk_len);
+ size_t mic_len = wpa_mic_len(akmp, pmk_len, PTK->hash_alg);
if (data_len < sizeof(*hdr) + sizeof(*key))
return -1;
@@ -2266,7 +2440,7 @@ static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
key_info = WPA_GET_BE16(key->key_info);
os_memcpy(mic, mic_pos, mic_len);
os_memset(mic_pos, 0, mic_len);
- if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+ if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, PTK->hash_alg,
key_info & WPA_KEY_INFO_TYPE_MASK,
data, data_len, mic_pos) ||
os_memcmp_const(mic, mic_pos, mic_len) != 0)
@@ -2293,7 +2467,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
wpa_printf(MSG_DEBUG,
"RSN: PTK Key ID 1 removal from the driver failed");
sm->pairwise_set = false;
- eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm);
}
@@ -2333,6 +2507,13 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
break;
case WPA_REAUTH:
case WPA_REAUTH_EAPOL:
+ if (event == WPA_REAUTH && sm->auth_alg == WLAN_AUTH_EPPKE &&
+ !sm->started) {
+ wpa_printf(MSG_DEBUG,
+ "EPPKE: Do not start state machine for a STA that has used EPPKE and reassociates");
+ break;
+ }
+
if (!sm->started) {
/*
* When using WPS, we may end up here if the STA
@@ -2348,7 +2529,12 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
if (wpa_sm_step(sm) == 1)
return 1; /* should not really happen */
sm->Init = false;
- sm->AuthenticationRequest = true;
+
+ if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+ wpa_printf(MSG_DEBUG,
+ "Skip EAPOL for 4-way handshake offload case");
+ else
+ sm->AuthenticationRequest = true;
break;
}
@@ -2424,6 +2610,16 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
(event == WPA_AUTH || event == WPA_ASSOC))
remove_ptk = 0;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_ENC_ASSOC
+ if (sm->auth_alg == WLAN_AUTH_EPPKE &&
+ (event == WPA_AUTH || event == WPA_ASSOC))
+ remove_ptk = 0;
+#endif /* CONFIG_ENC_ASSOC */
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (sm->auth_alg == WLAN_AUTH_802_1X &&
+ (event == WPA_AUTH || event == WPA_ASSOC))
+ remove_ptk = 0;
+#endif /* CONFIG_IEEE8021X_AUTH */
if (remove_ptk) {
sm->PTK_valid = false;
@@ -2508,11 +2704,38 @@ SM_STATE(WPA_PTK, AUTHENTICATION)
}
+static bool wpa_group_first_sta_seen_mbssid(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ if (!wpa_auth->cb->first_sta_seen_mbssid)
+ return false;
+
+ return wpa_auth->cb->first_sta_seen_mbssid(wpa_auth->cb_ctx,
+ group->vlan_id);
+}
+
+
static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
- if (group->first_sta_seen)
+ bool first_sta_seen_mbssid =
+ wpa_group_first_sta_seen_mbssid(wpa_auth, group);
+
+ /* Skip group keys renewal for below cases:
+ * This is not the first station association on this authenticator.
+ * In MBSSID case, this is not the first station association in the
+ * MBSSID set.
+ */
+ if (group->first_sta_seen || first_sta_seen_mbssid)
return;
+
+ if (!first_sta_seen_mbssid && wpa_auth->conf.tx_bss_auth) {
+ struct wpa_group *tx_group =
+ wpa_auth->conf.tx_bss_auth->group;
+ tx_group->bigtk_set = false;
+ tx_group->bigtk_configured = false;
+ }
+
/*
* System has run bit further than at the time hostapd was started
* potentially very early during boot up. This provides better chances
@@ -2532,7 +2755,7 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
}
if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
- wpa_gtk_update(wpa_auth, group) < 0 ||
+ wpa_group_update_gtk(wpa_auth, group) < 0 ||
wpa_group_config_group_keys(wpa_auth, group) < 0) {
wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
group->first_sta_seen = false;
@@ -2543,9 +2766,18 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
SM_STATE(WPA_PTK, AUTHENTICATION2)
{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
wpa_group_ensure_init(sm->wpa_auth, sm->group);
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ wpa_group_ensure_init(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+#endif /* CONFIG_IEEE80211BE */
sm->ReAuthenticationRequest = false;
/*
@@ -2755,8 +2987,7 @@ SM_STATE(WPA_PTK, PTKSTART)
if (sm->wpa == WPA_VERSION_WPA2 &&
(wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
(sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
- wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))) {
pmkid = buf;
kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -3034,8 +3265,8 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
forced_memzero(ick, sizeof(ick));
/* Store nonces for (Re)Association Request/Response frame processing */
- os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
- os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+ os_memcpy(sm->SNonce, snonce, NONCE_LEN);
+ os_memcpy(sm->ANonce, anonce, NONCE_LEN);
return res;
}
@@ -3252,10 +3483,10 @@ int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
aad_len[1] = ETH_ALEN;
/* The STA's nonce */
aad[2] = sm->SNonce;
- aad_len[2] = FILS_NONCE_LEN;
+ aad_len[2] = NONCE_LEN;
/* The AP's nonce */
aad[3] = sm->ANonce;
- aad_len[3] = FILS_NONCE_LEN;
+ aad_len[3] = NONCE_LEN;
/*
* The (Re)Association Request frame from the Capability Information
* field to the FILS Session element (both inclusive).
@@ -3311,10 +3542,10 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
aad_len[1] = ETH_ALEN;
/* The AP's nonce */
aad[2] = sm->ANonce;
- aad_len[2] = FILS_NONCE_LEN;
+ aad_len[2] = NONCE_LEN;
/* The STA's nonce */
aad[3] = sm->SNonce;
- aad_len[3] = FILS_NONCE_LEN;
+ aad_len[3] = NONCE_LEN;
/*
* The (Re)Association Response frame from the Capability Information
* field (the same offset in both Association and Reassociation
@@ -3403,7 +3634,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
/* GTK KDE */
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -3591,6 +3822,12 @@ static int wpa_auth_validate_ml_kdes_m2(struct wpa_state_machine *sm,
return -1;
}
+ /* Skip rejected links although the non-AP MLD will send them in
+ * M2 of the initial 4-way handshake. */
+ if (sm->mld_links[i].rejected) {
+ n_links++;
+ continue;
+ }
if (!sm->mld_links[i].valid || i == sm->mld_assoc_link_id) {
wpa_printf(MSG_DEBUG,
"RSN: MLD: Invalid link ID=%u", i);
@@ -3651,7 +3888,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
sm->update_snonce = false;
os_memset(&PTK, 0, sizeof(PTK));
- mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
derive_kdk = sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF);
@@ -3807,7 +4044,6 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
key_data_buf_len = key_data_length;
if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
key_data, key_data_buf)) {
- bin_clear_free(key_data_buf, key_data_buf_len);
wpa_printf(MSG_INFO,
"RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
goto out;
@@ -3825,9 +4061,6 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
if (kde.rsn_ie) {
eapol_key_ie = kde.rsn_ie;
eapol_key_ie_len = kde.rsn_ie_len;
- } else if (kde.osen) {
- eapol_key_ie = kde.osen;
- eapol_key_ie_len = kde.osen_len;
} else {
eapol_key_ie = kde.wpa_ie;
eapol_key_ie_len = kde.wpa_ie_len;
@@ -3914,6 +4147,34 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
goto out;
}
#endif /* CONFIG_IEEE80211R_AP */
+
+ /* Verify RSN Selection element for RSN overriding */
+ if ((wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2) &&
+ ((rsn_is_snonce_cookie(sm->SNonce) && !kde.rsn_selection) ||
+ (!rsn_is_snonce_cookie(sm->SNonce) && kde.rsn_selection) ||
+ (sm->rsn_selection && !kde.rsn_selection) ||
+ (!sm->rsn_selection && kde.rsn_selection) ||
+ (sm->rsn_selection && kde.rsn_selection &&
+ (sm->rsn_selection_len != kde.rsn_selection_len ||
+ os_memcmp(sm->rsn_selection, kde.rsn_selection,
+ sm->rsn_selection_len) != 0)))) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "RSN Selection element from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
+ wpa_printf(MSG_DEBUG,
+ "SNonce cookie for RSN overriding %sused",
+ rsn_is_snonce_cookie(sm->SNonce) ? "" : "not ");
+ wpa_hexdump(MSG_DEBUG, "RSN Selection in AssocReq",
+ sm->rsn_selection, sm->rsn_selection_len);
+ wpa_hexdump(MSG_DEBUG, "RSN Selection in EAPOL-Key msg 2/4",
+ kde.rsn_selection, kde.rsn_selection_len);
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+
+ }
+
#ifdef CONFIG_P2P
if (kde.ip_addr_req && kde.ip_addr_req[0] &&
wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
@@ -3958,7 +4219,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
if (wpa_auth_validate_ml_kdes_m2(sm, &kde) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
- return;
+ goto out;
}
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
@@ -3969,7 +4230,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
sm->pending_1_of_4_timeout = 0;
- eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && sm->PMK != pmk) {
/* PSK may have changed from the previous choice, so update
@@ -3992,6 +4253,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
forced_memzero(&PTK, sizeof(PTK));
sm->PTK_valid = true;
+ sm->hash_alg = sm->PTK.hash_alg;
out:
forced_memzero(pmk_r0, sizeof(pmk_r0));
forced_memzero(pmk_r1, sizeof(pmk_r1));
@@ -4053,7 +4315,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
else
os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
@@ -4066,6 +4328,11 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
NULL, 0);
forced_memzero(&igtk, sizeof(igtk));
+ if (gsm->vlan_id) {
+ wpa_printf(MSG_DEBUG, "Fetch BIGTK from default VLAN");
+ gsm = wpa_auth->group;
+ }
+
if (wpa_auth->conf.tx_bss_auth) {
wpa_auth = wpa_auth->conf.tx_bss_auth;
conf = &wpa_auth->conf;
@@ -4079,19 +4346,11 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
bigtk.keyid[0] = gsm->GN_bigtk;
bigtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
- wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
+ wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
else
os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
- /*
- * Provide unique random BIGTK to each OSEN STA to prevent use
- * of BIGTK in the BSS.
- */
- if (random_get_bytes(bigtk.bigtk, len) < 0)
- return pos;
- }
pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
(const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
NULL, 0);
@@ -4177,20 +4436,24 @@ static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
struct wpa_auth_ml_link_key_info *info,
- bool mgmt_frame_prot, bool beacon_prot)
+ bool mgmt_frame_prot, bool beacon_prot,
+ bool rekey, int vlan_id)
{
struct wpa_group *gsm = a->group;
u8 rsc[WPA_KEY_RSC_LEN];
wpa_printf(MSG_DEBUG,
- "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u",
- info->link_id, mgmt_frame_prot, beacon_prot);
+ "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u VLAN ID:%d",
+ info->link_id, mgmt_frame_prot, beacon_prot, vlan_id);
+
+ if (vlan_id)
+ gsm = wpa_select_vlan_wpa_group(gsm, vlan_id);
info->gtkidx = gsm->GN & 0x03;
info->gtk = gsm->GTK[gsm->GN - 1];
info->gtk_len = gsm->GTK_len;
- if (wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0)
+ if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0)
os_memset(info->pn, 0, sizeof(info->pn));
else
os_memcpy(info->pn, rsc, sizeof(info->pn));
@@ -4202,7 +4465,7 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
info->igtk = gsm->IGTK[gsm->GN_igtk - 4];
info->igtk_len = wpa_cipher_key_len(a->conf.group_mgmt_cipher);
- if (wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0)
+ if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0)
os_memset(info->ipn, 0, sizeof(info->ipn));
else
os_memcpy(info->ipn, rsc, sizeof(info->ipn));
@@ -4210,6 +4473,11 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
if (!beacon_prot)
return;
+ if (gsm->vlan_id) {
+ wpa_printf(MSG_DEBUG, "Fetch BIGTK from default VLAN");
+ gsm = a->group;
+ }
+
if (a->conf.tx_bss_auth) {
a = a->conf.tx_bss_auth;
gsm = a->group;
@@ -4218,7 +4486,7 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
info->bigtkidx = gsm->GN_bigtk;
info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
- if (wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0)
+ if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0)
os_memset(info->bipn, 0, sizeof(info->bipn));
else
os_memcpy(info->bipn, rsc, sizeof(info->bipn));
@@ -4226,16 +4494,17 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
- struct wpa_auth_ml_key_info *info)
+ struct wpa_auth_ml_key_info *info,
+ bool rekey, int vlan_id)
{
if (!wpa_auth->cb->get_ml_key_info)
return;
- wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+ wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info, rekey, vlan_id);
}
-static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links)
{
struct wpa_authenticator *wpa_auth;
size_t kde_len = 0;
@@ -4248,6 +4517,9 @@ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
if (!sm->mld_links[link_id].valid)
continue;
+ if (!(req_links & BIT(link_id)))
+ continue;
+
wpa_auth = sm->mld_links[link_id].wpa_auth;
if (!wpa_auth || !wpa_auth->group)
continue;
@@ -4283,15 +4555,16 @@ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
}
-static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos,
+ u16 req_links)
{
struct wpa_auth_ml_key_info ml_key_info;
unsigned int i, link_id;
u8 *start = pos;
+ bool rekey = sm->wpa_ptk_group_state == WPA_PTK_GROUP_REKEYNEGOTIATING;
/* First fetch the key information from all the authenticators */
os_memset(&ml_key_info, 0, sizeof(ml_key_info));
- ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
/*
* Assume that management frame protection and beacon protection are the
@@ -4304,13 +4577,20 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
if (!sm->mld_links[link_id].valid)
continue;
+ if (!(req_links & BIT(link_id)))
+ continue;
+
ml_key_info.links[i++].link_id = link_id;
}
+ ml_key_info.n_mld_links = i;
- wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info);
+ wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info, rekey,
+ sm->group->vlan_id);
/* Add MLO GTK KDEs */
- for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < ml_key_info.n_mld_links; i++) {
+ link_id = ml_key_info.links[i].link_id;
+
if (!sm->mld_links[link_id].valid ||
!ml_key_info.links[i].gtk_len)
continue;
@@ -4335,18 +4615,18 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
os_memcpy(pos, ml_key_info.links[i].gtk,
ml_key_info.links[i].gtk_len);
pos += ml_key_info.links[i].gtk_len;
-
- i++;
}
if (!sm->mgmt_frame_prot) {
- wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td",
pos - start);
return pos;
}
/* Add MLO IGTK KDEs */
- for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < ml_key_info.n_mld_links; i++) {
+ link_id = ml_key_info.links[i].link_id;
+
if (!sm->mld_links[link_id].valid ||
!ml_key_info.links[i].igtk_len)
continue;
@@ -4378,18 +4658,18 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
os_memcpy(pos, ml_key_info.links[i].igtk,
ml_key_info.links[i].igtk_len);
pos += ml_key_info.links[i].igtk_len;
-
- i++;
}
if (!sm->wpa_auth->conf.beacon_prot) {
- wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td",
pos - start);
return pos;
}
/* Add MLO BIGTK KDEs */
- for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < ml_key_info.n_mld_links; i++) {
+ link_id = ml_key_info.links[i].link_id;
+
if (!sm->mld_links[link_id].valid ||
!ml_key_info.links[i].bigtk ||
!ml_key_info.links[i].igtk_len)
@@ -4422,11 +4702,9 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
os_memcpy(pos, ml_key_info.links[i].bigtk,
ml_key_info.links[i].igtk_len);
pos += ml_key_info.links[i].igtk_len;
-
- i++;
}
- wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start);
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td", pos - start);
return pos;
}
@@ -4446,7 +4724,7 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
/* For the MAC Address KDE */
kde_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN;
- /* MLO Link KDE for each link */
+ /* MLO Link KDE and RSN Override Link KDE for each link */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct wpa_authenticator *wpa_auth;
const u8 *ie;
@@ -4455,18 +4733,44 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
if (!wpa_auth)
continue;
+ /* MLO Link KDE */
kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN;
+
ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
WLAN_EID_RSN);
if (ie)
kde_len += 2 + ie[1];
+
ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
WLAN_EID_RSNX);
if (ie)
kde_len += 2 + ie[1];
+
+ if ((!wpa_auth->conf.rsn_override_key_mgmt &&
+ !wpa_auth->conf.rsn_override_key_mgmt_2) ||
+ !rsn_is_snonce_cookie(sm->SNonce))
+ continue;
+
+ /* RSN Override Link KDE */
+ kde_len += 2 + RSN_SELECTOR_LEN + 1;
+
+ ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if (ie)
+ kde_len += 2 + ie[1];
+
+ ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ if (ie)
+ kde_len += 2 + ie[1];
+
+ ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if (ie)
+ kde_len += 2 + ie[1];
}
- kde_len += wpa_auth_ml_group_kdes_len(sm);
+ kde_len += wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
#endif /* CONFIG_IEEE80211BE */
return kde_len;
@@ -4488,8 +4792,9 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct wpa_authenticator *wpa_auth;
- const u8 *rsne, *rsnxe;
- size_t rsne_len, rsnxe_len;
+ const u8 *rsne, *rsnxe, *rsnoe, *rsno2e, *rsnxoe;
+ size_t rsne_len, rsnxe_len, rsnoe_len, rsno2e_len, rsnxoe_len;
+ size_t kde_len;
wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
if (!wpa_auth)
@@ -4508,6 +4813,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
RSN_SELECTOR_LEN + 1 + ETH_ALEN +
rsne_len + rsnxe_len);
+ /* MLO Link KDE */
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
rsne_len + rsnxe_len;
@@ -4535,16 +4841,165 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
os_memcpy(pos, rsnxe, rsnxe_len);
pos += rsnxe_len;
}
+
+ if ((!wpa_auth->conf.rsn_override_key_mgmt &&
+ !wpa_auth->conf.rsn_override_key_mgmt_2) ||
+ !rsn_is_snonce_cookie(sm->SNonce))
+ continue;
+
+ rsnoe = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ rsnoe_len = rsnoe ? 2 + rsnoe[1] : 0;
+
+ rsno2e = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ rsno2e_len = rsno2e ? 2 + rsno2e[1] : 0;
+
+ rsnxoe = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ rsnxoe_len = rsnxoe ? 2 + rsnxoe[1] : 0;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: RSN Override Link KDE: link=%u, len=%zu",
+ link_id, RSN_SELECTOR_LEN + rsnoe_len + rsno2e_len +
+ rsnxoe_len);
+
+ /* RSN Override Link KDE */
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ kde_len = RSN_SELECTOR_LEN + 1 + rsnoe_len + rsno2e_len +
+ rsnxoe_len;
+ if (kde_len > 255) {
+ wpa_printf(MSG_ERROR,
+ "RSN: RSNOE/RSNO2E/RSNXOE too long (KDE length %zu) to fit in RSN Override Link KDE for link %u",
+ kde_len, link_id);
+ return NULL;
+ }
+ *pos++ = kde_len;
+
+ RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_RSN_OVERRIDE_LINK);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = link_id;
+
+ if (rsnoe_len) {
+ os_memcpy(pos, rsnoe, rsnoe_len);
+ pos += rsnoe_len;
+ }
+
+ if (rsno2e_len) {
+ os_memcpy(pos, rsno2e, rsno2e_len);
+ pos += rsno2e_len;
+ }
+
+ if (rsnxoe_len) {
+ os_memcpy(pos, rsnxoe, rsnxoe_len);
+ pos += rsnxoe_len;
+ }
}
- wpa_printf(MSG_DEBUG, "RSN: MLO Link KDE len = %ld", pos - start);
- pos = wpa_auth_ml_group_kdes(sm, pos);
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLO Link KDEs and RSN Override Link KDEs len = %td",
+ pos - start);
+ pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
#endif /* CONFIG_IEEE80211BE */
return pos;
}
+#ifdef CONFIG_SAE
+static u8 * add_sae_pw_ids(struct wpa_state_machine *sm, u8 *pos, u8 *end)
+{
+ static const size_t max_padding = 8;
+ u8 *start = pos, *len;
+ unsigned int i;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ u8 *data, *dpos;
+ unsigned int counter;
+ size_t pw_id_len = wpabuf_len(sm->sae_pw_id);
+ struct os_time t;
+ size_t kde_len;
+
+ wpa_printf(MSG_DEBUG, "RSN: Add SAE Password Identifiers KDE (num=%u)",
+ conf->sae_pw_id_num);
+ wpa_hexdump_buf(MSG_DEBUG, "RSN: Real SAE Password Identifier",
+ sm->sae_pw_id);
+ data = os_malloc(pw_id_len + max_padding + 4 + 4);
+ if (!data)
+ return NULL;
+
+ if (end - pos < 2 + RSN_SELECTOR_LEN + 1) {
+ pos = NULL;
+ goto fail;
+ }
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = pos++; /* Length to be filled */
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_SAE_PW_IDS);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 0; /* Flags */
+
+ counter = sm->sae_pw_id_counter + conf->sae_pw_id_num;
+
+ os_get_time(&t);
+
+ WPA_PUT_BE32(data, t.sec);
+ os_memcpy(data + 4, wpabuf_head(sm->sae_pw_id), pw_id_len);
+
+ for (i = 0; i < conf->sae_pw_id_num; i++) {
+ size_t pad_len, dlen, elen;
+
+ pad_len = 1 + os_random() % max_padding;
+ dpos = data + 4 + pw_id_len;
+ os_memset(dpos, 0, pad_len);
+ dpos += pad_len;
+ WPA_PUT_BE32(dpos, counter);
+ dpos += 4;
+ dlen = dpos - data;
+ elen = dlen + AES_BLOCK_SIZE;
+ counter++;
+
+ kde_len = (pos - len - 1) + (1 + elen);
+ if ((size_t) (end - pos) < 1 + elen || kde_len > 255) {
+ wpa_printf(MSG_INFO,
+ "RSN: Not enough room in the buffer for a new SAE Password Identifier - send only %u",
+ i);
+ break;
+ }
+
+ *pos++ = elen;
+ if (aes_siv_encrypt(conf->sae_pw_id_key,
+ sizeof(conf->sae_pw_id_key),
+ data, dlen, 0, NULL, NULL, pos) < 0) {
+ wpa_printf(MSG_INFO,
+ "RSN: Failed to encrypt SAE Password Identifier");
+ pos = NULL;
+ goto fail;
+ }
+ pos += elen;
+ }
+
+ kde_len = pos - len - 1;
+ if (kde_len > 255) {
+ wpa_printf(MSG_INFO,
+ "RSN: SAE Password Identifiers do not fit in a KDE");
+ wpa_hexdump_key(MSG_DEBUG, "RSN: KDE", start, pos - start);
+ pos = NULL;
+ goto fail;
+ }
+
+ *len = kde_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: SAE Password Identifiers KDE",
+ start, pos - start);
+
+fail:
+ os_free(data);
+ return pos;
+}
+#endif /* CONFIG_SAE */
+
+
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
@@ -4552,7 +5007,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int secure, gtkidx, encr = 0;
- u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+ u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL, *wpa_ie_buf3 = NULL;
u8 hdr[2];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
#ifdef CONFIG_IEEE80211BE
@@ -4560,6 +5015,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
#else /* CONFIG_IEEE80211BE */
bool is_mld = false;
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_SAE
+ bool sae_pw_ids = false;
+#endif /* CONFIG_SAE */
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = false;
@@ -4593,6 +5051,39 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
+ if ((conf->rsn_override_key_mgmt || conf->rsn_override_key_mgmt_2) &&
+ !rsn_is_snonce_cookie(sm->SNonce)) {
+ u8 *ie;
+ size_t ie_len;
+ u32 ids[] = {
+ RSNE_OVERRIDE_IE_VENDOR_TYPE,
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE,
+ 0
+ };
+ int i;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: Remove RSNE/RSNXE override elements");
+ wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs before edits",
+ wpa_ie, wpa_ie_len);
+ wpa_ie_buf3 = os_memdup(wpa_ie, wpa_ie_len);
+ if (!wpa_ie_buf3)
+ goto done;
+ wpa_ie = wpa_ie_buf3;
+
+ for (i = 0; ids[i]; i++) {
+ ie = (u8 *) get_vendor_ie(wpa_ie, wpa_ie_len, ids[i]);
+ if (ie) {
+ ie_len = 2 + ie[1];
+ os_memmove(ie, ie + ie_len,
+ wpa_ie_len - (ie + ie_len - wpa_ie));
+ wpa_ie_len -= ie_len;
+ }
+ }
+ wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs after edits",
+ wpa_ie, wpa_ie_len);
+ }
#ifdef CONFIG_TESTING_OPTIONS
if (conf->rsne_override_eapol_set) {
wpa_ie_buf2 = replace_ie(
@@ -4628,6 +5119,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
return;
}
+ if (!sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
+ wpa_auth_set_key(sm->wpa_auth, 0,
+ wpa_cipher_to_alg(sm->pairwise),
+ sm->addr, 0, sm->PTK.tk,
+ wpa_cipher_key_len(sm->pairwise),
+ KEY_FLAG_PAIRWISE_NEXT)) {
+ /* Continue anyway since the many drivers do not support
+ * configuration of the TK for RX-only purposes for
+ * cases where multiple keys might be in use in parallel
+ * and this being an optional optimization to avoid race
+ * condition during TK changes that could result in some
+ * protected frames getting discarded. */
+ }
+
#ifdef CONFIG_PASN
if (sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe,
@@ -4647,8 +5152,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (conf->disable_gtk ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -4714,6 +5218,17 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
if (sm->ssid_protection)
kde_len += 2 + conf->ssid_len;
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ conf->sae_pw_id_num &&
+ sm->sae_pw_id &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE)) {
+ kde_len += 2 + 255;
+ sae_pw_ids = true;
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements)
kde_len += wpabuf_len(conf->eapol_m3_elements);
@@ -4832,6 +5347,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
#endif /* CONFIG_DPP2 */
pos = wpa_auth_ml_kdes(sm, pos);
+ if (!pos) {
+ wpa_printf(MSG_ERROR, "RSN: Failed to add MLO KDEs");
+ goto done;
+ }
if (sm->ssid_protection) {
*pos++ = WLAN_EID_SSID;
@@ -4840,6 +5359,23 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos += conf->ssid_len;
}
+#ifdef CONFIG_SAE
+ if (sae_pw_ids) {
+ u8 *npos;
+
+ npos = add_sae_pw_ids(sm, pos, kde + kde_len);
+ if (!npos) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Failed to add SAE Password Identifiers KDE");
+ /* Ignore this since it is not a fatal error for the
+ * Authenticator and the STA can decide whether to
+ * proceed without getting new identifiers. */
+ } else {
+ pos = npos;
+ }
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements) {
os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
@@ -4853,8 +5389,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
- (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
- WPA_KEY_INFO_MIC : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len,
+ sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
@@ -4862,6 +5398,7 @@ done:
bin_clear_free(kde, kde_len);
os_free(wpa_ie_buf);
os_free(wpa_ie_buf2);
+ os_free(wpa_ie_buf3);
}
@@ -4882,7 +5419,7 @@ static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm)
* Note: last_rx_eapol_key length fields have already been validated in
* wpa_receive().
*/
- mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (const struct wpa_eapol_key *) (hdr + 1);
@@ -5192,13 +5729,14 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
sm->TimeoutEvt = false;
/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
- if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+ if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE && !is_mld)
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"sending 1/2 msg of Group Key Handshake");
gtk = gsm->GTK[gsm->GN - 1];
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -5229,14 +5767,14 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
kde_len = pos - kde;
#ifdef CONFIG_IEEE80211BE
} else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
- kde_len = wpa_auth_ml_group_kdes_len(sm);
+ kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
if (kde_len) {
kde_buf = os_malloc(kde_len);
if (!kde_buf)
return;
kde = pos = kde_buf;
- pos = wpa_auth_ml_group_kdes(sm, pos);
+ pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
kde_len = pos - kde_buf;
}
#endif /* CONFIG_IEEE80211BE */
@@ -5247,8 +5785,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_SECURE |
- (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
- WPA_KEY_INFO_MIC : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len,
+ sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
@@ -5273,7 +5811,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
sm->EAPOLKeyReceived = false;
#ifdef CONFIG_OCV
- mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
/*
* Note: last_rx_eapol_key length fields have already been validated in
@@ -5411,7 +5949,7 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "GTK",
group->GTK[group->GN - 1], group->GTK_len);
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ if (wpa_auth_pmf_enabled(conf)) {
len = wpa_cipher_key_len(conf->group_mgmt_cipher);
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -5423,10 +5961,18 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
group->IGTK[group->GN_igtk - 4], len);
}
+ /* Skip BIGTK generation for groups with a non-zero VLAN ID since only
+ * a single BIGTK is shared for all VLANs in a BSS. */
+ if (group->vlan_id)
+ return ret;
+
if (!wpa_auth->non_tx_beacon_prot &&
- conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ !wpa_auth_pmf_enabled(conf))
return ret;
- if (!conf->beacon_prot)
+
+ /* Skip BIGTK generation if neither the TX BSS nor any of the non-TX BSS
+ * enable beacon protection */
+ if (!wpa_auth->non_tx_beacon_prot && !conf->beacon_prot)
return ret;
if (wpa_auth->conf.tx_bss_auth) {
@@ -5475,11 +6021,44 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
{
- if (ctx != NULL && ctx != sm->group)
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *group = sm->group;
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct wpa_group *g;
+
+ if (!sm->mld_links[link_id].valid)
+ continue;
+ if (!sm->mld_links[link_id].wpa_auth)
+ continue;
+ g = wpa_select_vlan_wpa_group(
+ sm->mld_links[link_id].wpa_auth->group,
+ sm->group->vlan_id);
+ if (g == ctx) {
+ group = g;
+ wpa_auth = sm->mld_links[link_id].wpa_auth;
+ break;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (ctx && ctx != group)
+ return 0;
+
+#ifdef CONFIG_IEEE80211BE
+ /* For ML STA, run rekey on the association link and send G1 with keys
+ * for all links. This is based on assumption that MLD level
+ * Authenticator updates group keys on all affiliated links in one shot
+ * and not independently or concurrently for separate links. */
+ if (sm->mld_assoc_link_id >= 0 &&
+ sm->mld_assoc_link_id != wpa_auth->link_id)
return 0;
+#endif /* CONFIG_IEEE80211BE */
if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
- wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_DEBUG,
"Not in PTKINITDONE; skip Group Key update");
sm->GUpdateStationKeys = false;
@@ -5491,7 +6070,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
* Since we clear the GKeyDoneStations before the loop, the
* station needs to be counted here anyway.
*/
- wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_DEBUG,
"GUpdateStationKeys was already set when marking station for GTK rekeying");
}
@@ -5501,6 +6080,17 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
return 0;
sm->group->GKeyDoneStations++;
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id) {
+ struct wpa_authenticator *partner_auth =
+ sm->mld_links[link_id].wpa_auth;
+ struct wpa_group *partner_group =
+ wpa_select_vlan_wpa_group(partner_auth->group,
+ sm->group->vlan_id);
+ partner_group->GKeyDoneStations++;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
sm->GUpdateStationKeys = true;
wpa_sm_step(sm);
@@ -5547,7 +6137,7 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
return 0;
pos += 8;
os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -5586,7 +6176,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
pos += 6;
os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random IGTK to each STA to prevent use
* of IGTK in the BSS.
@@ -5625,14 +6215,6 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
pos += 6;
os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
- /*
- * Provide unique random BIGTK to each STA to prevent use
- * of BIGTK in the BSS.
- */
- if (random_get_bytes(pos, len) < 0)
- return 0;
- }
pos += len;
wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
@@ -5646,8 +6228,8 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
#endif /* CONFIG_WNM_AP */
-static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
int tmp;
@@ -5663,7 +6245,7 @@ static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
/* "GKeyDoneStations = GNoStations" is done in more robust way by
* counting the STAs that are marked with GUpdateStationKeys instead of
* including all STAs that could be in not-yet-completed state. */
- wpa_gtk_update(wpa_auth, group);
+ return wpa_gtk_update(wpa_auth, group);
}
@@ -5705,6 +6287,8 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
{
struct wpa_auth_config *conf = &wpa_auth->conf;
int ret = 0;
+ enum wpa_alg alg;
+ size_t len;
if (wpa_auth_set_key(wpa_auth, group->vlan_id,
wpa_cipher_to_alg(conf->wpa_group),
@@ -5713,36 +6297,44 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- enum wpa_alg alg;
- size_t len;
+ alg = wpa_cipher_to_alg(conf->group_mgmt_cipher);
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
- alg = wpa_cipher_to_alg(conf->group_mgmt_cipher);
- len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ if (wpa_auth_pmf_enabled(conf) && ret == 0 &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4], len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ ret = -1;
- if (ret == 0 &&
- wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
- broadcast_ether_addr, group->GN_igtk,
- group->IGTK[group->GN_igtk - 4], len,
- KEY_FLAG_GROUP_TX_DEFAULT) < 0)
- ret = -1;
+ /* Skip setting of BIGTK in following cases:
+ * PMF is not enabled and no beacon protection requirement for any of
+ * the non TX BSSs in the MBSSID set.
+ * Groups with a non-zero VLAN ID since only a single BIGTK is shared
+ * for all VLANs in a BSS.
+ * If beacon protection is enabled neither in the TX BSS nor in any of
+ * the non-TX BSS.
+ */
+ if (!wpa_auth->non_tx_beacon_prot && !wpa_auth_pmf_enabled(conf))
+ return ret;
- if (ret || !conf->beacon_prot)
+ if (ret || (!conf->beacon_prot && !wpa_auth->non_tx_beacon_prot) ||
+ group->vlan_id)
+ return ret;
+
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ group = wpa_auth->group;
+ if (!group->bigtk_set || group->bigtk_configured)
return ret;
- if (wpa_auth->conf.tx_bss_auth) {
- wpa_auth = wpa_auth->conf.tx_bss_auth;
- group = wpa_auth->group;
- if (!group->bigtk_set || group->bigtk_configured)
- return ret;
- }
- if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
- broadcast_ether_addr, group->GN_bigtk,
- group->BIGTK[group->GN_bigtk - 6], len,
- KEY_FLAG_GROUP_TX_DEFAULT) < 0)
- ret = -1;
- else
- group->bigtk_configured = true;
}
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_bigtk,
+ group->BIGTK[group->GN_bigtk - 6], len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ ret = -1;
+ else
+ group->bigtk_configured = true;
return ret;
}
@@ -5836,12 +6428,16 @@ static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
if (!sm || !sm->wpa_auth)
return;
- wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
+ wpa_group_sm_step(sm->wpa_auth, sm->group);
#ifdef CONFIG_IEEE80211BE
for_each_sm_auth(sm, link_id) {
- wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
- sm->mld_links[link_id].wpa_auth->group);
+ struct wpa_authenticator *partner_auth =
+ sm->mld_links[link_id].wpa_auth;
+ struct wpa_group *partner_group =
+ wpa_select_vlan_wpa_group(partner_auth->group,
+ sm->group->vlan_id);
+ wpa_group_sm_step(partner_auth, partner_group);
}
#endif /* CONFIG_IEEE80211BE */
}
@@ -6269,19 +6865,37 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int akmp)
+ int akmp, bool is_ml, int vlan_id)
{
+ struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+ struct rsn_pmksa_cache_entry *entry;
+ const u8 *aa = wpa_auth->addr;
+
if (wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
if (!akmp)
akmp = WPA_KEY_MGMT_SAE;
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
- NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
- return 0;
- return -1;
+#ifdef CONFIG_IEEE80211BE
+ if (is_ml) {
+ pmksa = wpa_auth->ml_pmksa;
+ aa = wpa_auth->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0,
+ aa, addr, 0, NULL, akmp);
+ if (!entry)
+ return -1;
+
+ entry->sae_vlan_id = vlan_id;
+#ifdef CONFIG_IEEE80211BE
+ entry->is_ml = is_ml;
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
}
@@ -6294,20 +6908,33 @@ void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp, const u8 *dpp_pkhash)
+ int session_timeout, int akmp, const u8 *dpp_pkhash,
+ bool is_ml)
{
+ struct rsn_pmksa_cache *pmksa;
+ const u8 *aa;
struct rsn_pmksa_cache_entry *entry;
if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
- entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
- NULL, 0, wpa_auth->addr, addr, session_timeout,
- NULL, akmp);
+ pmksa = wpa_auth->pmksa;
+ aa = wpa_auth->addr;
+#ifdef CONFIG_IEEE80211BE
+ if (is_ml) {
+ pmksa = wpa_auth->ml_pmksa;
+ aa = wpa_auth->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa,
+ addr, session_timeout, NULL, akmp);
if (!entry)
return -1;
+#ifdef CONFIG_IEEE80211BE
+ entry->is_ml = is_ml;
+#endif /* CONFIG_IEEE80211BE */
if (dpp_pkhash)
entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
@@ -6322,28 +6949,66 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
if (!wpa_auth || !wpa_auth->pmksa)
return;
+
pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
if (pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
MACSTR " based on request", MAC2STR(sta_addr));
pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
}
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->ml_pmksa) {
+ pmksa = pmksa_cache_auth_get(wpa_auth->ml_pmksa,
+ sta_addr, NULL);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Remove PMKSA cache entry for " MACSTR
+ " based on request (MLD)",
+ MAC2STR(sta_addr));
+ pmksa_cache_free_entry(wpa_auth->ml_pmksa, pmksa);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
}
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
size_t len)
{
+ int ret, index;
+ char *pos = buf, *end = buf + len;
+
if (!wpa_auth || !wpa_auth->pmksa)
return 0;
- return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+
+ ret = os_snprintf(pos, len,
+ "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ index = 0;
+ pos += pmksa_cache_auth_list(wpa_auth->pmksa, pos, end - pos, &index);
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->ml_pmksa)
+ pos += pmksa_cache_auth_list(wpa_auth->ml_pmksa,
+ pos, end - pos, &index);
+#endif /* CONFIG_IEEE80211BE */
+
+ return pos - buf;
}
void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
{
- if (wpa_auth && wpa_auth->pmksa)
+ if (wpa_auth && wpa_auth->pmksa) {
pmksa_cache_auth_flush(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->ml_pmksa && wpa_auth->primary_auth)
+ pmksa_cache_auth_flush(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+ }
}
@@ -6401,11 +7066,15 @@ int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
struct rsn_pmksa_cache *
-wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml)
{
if (!wpa_auth || !wpa_auth->pmksa)
return NULL;
+#ifdef CONFIG_IEEE80211BE
+ return is_ml ? wpa_auth->ml_pmksa : wpa_auth->pmksa;
+#else /* CONFIG_IEEE80211BE */
return wpa_auth->pmksa;
+#endif /* CONFIG_IEEE80211BE */
}
@@ -6419,6 +7088,26 @@ wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
}
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+ const u8 **pmkid)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = wpa_auth_pmksa_get(wpa_auth, sta_addr, NULL);
+ if (!pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR,
+ MAC2STR(sta_addr));
+ return -1;
+ }
+
+ *pmk = pmksa->pmk;
+ *pmk_len = pmksa->pmk_len;
+ *pmkid = pmksa->pmkid;
+ return 0;
+}
+
+
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
@@ -6488,6 +7177,18 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
}
+#ifdef CONFIG_IEEE80211BE
+static void wpa_group_put_vlan(struct wpa_authenticator *wpa_auth,
+ int vlan_id)
+{
+ struct wpa_group *vlan_group =
+ wpa_select_vlan_wpa_group(wpa_auth->group, vlan_id);
+
+ wpa_group_put(wpa_auth, vlan_group);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
/*
* Add a group that has its references counter set to zero. Caller needs to
* call wpa_group_get() on the return value to mark the entry in use.
@@ -6605,14 +7306,15 @@ int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
}
-int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth, int vlan_id)
{
struct wpa_group *group;
if (!sm || !sm->wpa_auth)
return 0;
- group = sm->wpa_auth->group;
+ group = wpa_auth->group;
while (group) {
if (group->vlan_id == vlan_id)
break;
@@ -6620,11 +7322,19 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
}
if (!group) {
- group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+ group = wpa_auth_add_group(wpa_auth, vlan_id);
if (!group)
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0 &&
+ (sm->mld_assoc_link_id != wpa_auth->link_id)) {
+ wpa_group_get(wpa_auth, group);
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (sm->group == group)
return 0;
@@ -6665,7 +7375,7 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
wpa_printf(MSG_DEBUG,
"WPA: Increase initial EAPOL-Key 1/4 timeout by %u ms because of acknowledged frame",
timeout_ms);
- eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm);
eloop_register_timeout(timeout_ms / 1000,
(timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
@@ -6813,6 +7523,45 @@ void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
}
+void wpa_auth_set_hash_alg_sae_ext_key(struct wpa_state_machine *sm,
+ size_t pmk_len)
+{
+ if (!sm)
+ return;
+
+ if (pmk_len == 32)
+ sm->hash_alg = RSN_HASH_SHA256;
+ else if (pmk_len == 48)
+ sm->hash_alg = RSN_HASH_SHA384;
+ else if (pmk_len == 64)
+ sm->hash_alg = RSN_HASH_SHA512;
+}
+
+
+void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie,
+ size_t len)
+{
+ if (!sm)
+ return;
+ os_free(sm->rsn_selection);
+ sm->rsn_selection = NULL;
+ sm->rsn_selection_len = 0;
+ sm->rsn_override = false;
+ sm->rsn_override_2 = false;
+ if (ie) {
+ if (len >= 1) {
+ if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE)
+ sm->rsn_override = true;
+ else if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE_2)
+ sm->rsn_override_2 = true;
+ }
+ sm->rsn_selection = os_memdup(ie, len);
+ if (sm->rsn_selection)
+ sm->rsn_selection_len = len;
+ }
+}
+
+
#ifdef CONFIG_DPP2
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
{
@@ -7033,8 +7782,8 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
- (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
- WPA_KEY_INFO_MIC : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len,
+ sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
@@ -7100,8 +7849,8 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_SECURE |
- (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
- WPA_KEY_INFO_MIC : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len,
+ sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
@@ -7216,6 +7965,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
struct wpa_get_link_auth_ctx ctx;
sm_link->valid = link->valid;
+ sm_link->rejected = false;
if (!link->valid)
continue;
@@ -7234,11 +7984,8 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
ctx.wpa_auth = NULL;
wpa_auth_for_each_auth(sm->wpa_auth,
wpa_get_link_sta_auth, &ctx);
- if (ctx.wpa_auth) {
+ if (ctx.wpa_auth)
sm_link->wpa_auth = ctx.wpa_auth;
- wpa_group_get(sm_link->wpa_auth,
- sm_link->wpa_auth->group);
- }
} else {
sm_link->wpa_auth = sm->wpa_auth;
}
@@ -7252,3 +7999,313 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
}
#endif /* CONFIG_IEEE80211BE */
}
+
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+ const u8 *timestamp,
+ const u8 *mic, size_t mic_len)
+{
+ size_t exp_mic_len;
+ u8 exp_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ int ver;
+
+ if (!sm)
+ return false;
+
+ if (!sm->PTK_valid || !mic_len || sm->PTK.kck_len == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: No KCK to verify Known STA Identification");
+ return false;
+ }
+
+ exp_mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg);
+ if (mic_len != exp_mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MIC length mismatch in Known STA Identification (received %zu, expected %zu)",
+ mic_len, exp_mic_len);
+ return false;
+ }
+
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+ ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+ else if (wpa_use_cmac(sm->wpa_key_mgmt))
+ ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+ else if (sm->pairwise != WPA_CIPHER_TKIP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt,
+ sm->hash_alg, ver, timestamp, 8, exp_mic) ||
+ os_memcmp_const(mic, exp_mic, exp_mic_len) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Invalid MIC in Known STA Identification");
+ return false;
+ }
+
+ return true;
+}
+
+
+#ifdef CONFIG_ENC_ASSOC
+
+u8 * wpa_auth_eid_key_delivery(u8 *eid, size_t max_len,
+ struct wpa_state_machine *sm, bool is_ml)
+{
+ size_t gtk_len, kde_len = 0;
+ u8 rsc[WPA_KEY_RSC_LEN] = {0}, *gtk;
+ struct wpa_group *gsm = sm->group;
+ u8 hdr[2];
+ u8 *kde, *buf;
+ const u8 *ptr;
+ size_t slice_len;
+ size_t buflen = 1024;
+
+ buflen += 2 + 255; /* extra room for SAE PW IDs KDE */
+
+ /* TODO: Make sure there is sufficient length for the element */
+ buf = os_malloc(buflen);
+ if (!buf)
+ return eid;
+
+ ptr = buf;
+ kde = buf;
+ if (is_ml) {
+#ifdef CONFIG_IEEE80211BE
+ kde = wpa_auth_ml_group_kdes(sm, kde, KDE_ALL_LINKS);
+ kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
+#endif /* CONFIG_IEEE80211BE */
+ } else {
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ hdr[0] = gsm->GN & 0x03; /* KeyID bits */
+ hdr[1] = 0;
+ kde = wpa_add_kde(kde, RSN_KEY_DATA_GROUPKEY, hdr, sizeof(hdr),
+ gtk, gtk_len);
+ kde = ieee80211w_kde_add(sm, kde);
+ /* GTK KDE: 0xdd|len(1B)|RSN Selector(4B)|KeyID(2B)|GTK| */
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+ ieee80211w_kde_len(sm);
+ }
+
+#ifdef CONFIG_SAE
+ /* For EPPKE, the 4-way handshake is skipped, so deliver the SAE
+ * Password Identifiers KDE here in the encrypted (Re)Association
+ * Response frame. */
+ if (sm->auth_alg == WLAN_AUTH_EPPKE &&
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ sm->wpa_auth->conf.sae_pw_id_num &&
+ sm->sae_pw_id &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE)) {
+ u8 *new_kde = add_sae_pw_ids(sm, kde, buf + buflen);
+
+ if (new_kde) {
+ kde_len += new_kde - kde;
+ kde = new_kde;
+ }
+ }
+#endif /* CONFIG_SAE */
+
+ if (!is_ml && sm->group->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+
+ /*
+ * As the Key Delivery element can exceed the size of 255 bytes need to
+ * handle fragmentation.
+ */
+ slice_len = kde_len <= 246 ? kde_len : 246;
+ /* ElementID(0xff)|Length(1B)|ElementID Extn(1B)|RSC(8B)|KDE list */
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = slice_len + 1 + WPA_KEY_RSC_LEN;
+ *eid++ = WLAN_EID_EXT_KEY_DELIVERY;
+ os_memcpy(eid, rsc, WPA_KEY_RSC_LEN);
+ eid += WPA_KEY_RSC_LEN;
+
+ os_memcpy(eid, ptr, slice_len);
+
+ ptr += slice_len;
+ eid += slice_len;
+ kde_len -= slice_len;
+
+ while (kde_len) {
+ slice_len = kde_len <= 255 ? kde_len : 255;
+ *eid++ = WLAN_EID_FRAGMENT;
+ *eid++ = slice_len;
+ os_memcpy(eid, ptr, slice_len);
+
+ ptr += slice_len;
+ eid += slice_len;
+ kde_len -= slice_len;
+ }
+
+ bin_clear_free(buf, buflen);
+ return eid;
+}
+
+
+bool wpa_auth_ap_sta_support_assoc_enc(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return false;
+
+ return sm->wpa_auth->conf.assoc_frame_encryption &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION);
+}
+
+
+void wpa_auth_ensure_group_init(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm)
+ return;
+
+ wpa_group_ensure_init(sm->wpa_auth, sm->group);
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ wpa_group_ensure_init(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+#endif /* CONFIG_IEEE80211BE */
+
+ return;
+}
+
+
+u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len, bool is_ml)
+{
+ int res;
+ u8 *end = pos + max_len;
+
+ if (!sm)
+ return pos;
+
+ res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL);
+ if (res < 0)
+ return pos;
+ pos += res;
+ pos = wpa_auth_eid_key_delivery(pos, end - pos, sm, is_ml);
+
+ return pos;
+}
+
+#endif /* CONFIG_ENC_ASSOC */
+
+
+#ifdef CONFIG_PMKSA_PRIVACY
+
+int wpa_auth_epp_derive_new_pmkid(const u8 *anonce, const u8 *snonce,
+ int akmp, size_t pmk_len, u8 *pmkid)
+{
+ return rsn_pmkid_privacy(anonce, snonce, akmp, pmk_len, pmkid);
+}
+
+
+bool wpa_auth_ap_sta_support_pmkid_privacy(struct wpa_state_machine *sm)
+{
+ struct wpa_auth_config *conf;
+
+ if (!sm)
+ return false;
+
+ conf = &sm->wpa_auth->conf;
+
+ return conf->pmksa_caching_privacy &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY);
+}
+
+#endif /* CONFIG_PMKSA_PRIVACY */
+
+
+void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm,
+ struct wpa_authenticator *wpa_auth,
+ u8 mld_assoc_link_id)
+{
+#ifdef CONFIG_IEEE80211BE
+ assoc_sm->wpa_auth = wpa_auth;
+ assoc_sm->mld_assoc_link_id = mld_assoc_link_id;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+/* wpa_select_vlan_wpa_group - Traverse through the wpa_group list and select
+ * the one that matches the vlan_id.
+ *
+ * @gsm: Head of wpa_group list
+ * @vlan_id: vlan_id used to search the group key state machine data that
+ * corresponds to the specified VLAN group
+ * Returns: Pointer to wpa_group that corresponds to the VLAN group on success,
+ * or pointer to the head of wpa_group list that was passed in.
+ */
+struct wpa_group * wpa_select_vlan_wpa_group(struct wpa_group *gsm, int vlan_id)
+{
+ struct wpa_group *vlan_gsm = gsm;
+
+ while (vlan_gsm) {
+ if (vlan_gsm->vlan_id == vlan_id)
+ break;
+
+ vlan_gsm = vlan_gsm->next;
+ }
+
+ if (!vlan_gsm) {
+ wpa_printf(MSG_DEBUG, "%s: VLAN group not found", __func__);
+ vlan_gsm = gsm;
+ }
+
+ return vlan_gsm;
+}
+
+
+void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm,
+ const struct wpabuf *pw_id,
+ unsigned int counter)
+{
+ if (sm) {
+ wpabuf_free(sm->sae_pw_id);
+ sm->sae_pw_id = wpabuf_dup(pw_id);
+ sm->sae_pw_id_counter = counter;
+ }
+}
+
+
+bool wpa_auth_get_first_sta_seen(struct wpa_authenticator *wpa_auth,
+ int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return false;
+
+ group = wpa_select_vlan_wpa_group(wpa_auth->group, vlan_id);
+ return group->first_sta_seen;
+}
+
+#ifdef CONFIG_IEEE8021X_AUTH
+int wpa_auth_802_1x_get_msk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u8 *msk, size_t *len)
+{
+ return wpa_auth_get_msk(wpa_auth, addr, msk, len);
+}
+
+
+int wpa_auth_802_1x_set_key(struct wpa_authenticator *wpa_auth,
+ enum wpa_alg alg, const u8 *addr,
+ u8 *key, size_t key_len)
+{
+
+ return wpa_auth_set_key(wpa_auth, 0, alg, addr, 0, key,
+ key_len, KEY_FLAG_PAIRWISE_RX_TX);
+
+}
+
+bool wpa_auth_ap_support_secure_ltf(struct wpa_authenticator *wpa_auth)
+{
+ return wpa_auth->conf.secure_ltf;
+}
+#endif /* CONFIG_IEEE8021X_AUTH */
diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h
index b588f9fdcd78..af354b9cd970 100644
--- a/contrib/wpa/src/ap/wpa_auth.h
+++ b/contrib/wpa/src/ap/wpa_auth.h
@@ -17,7 +17,7 @@
struct vlan_description;
struct mld_info;
-#define MAX_OWN_IE_OVERRIDE 256
+#define MAX_OWN_IE_OVERRIDE 257
#ifdef _MSC_VER
#pragma pack(push, 1)
@@ -173,6 +173,8 @@ struct wpa_auth_config {
int wpa;
int extended_key_id;
int wpa_key_mgmt;
+ int rsn_override_key_mgmt;
+ int rsn_override_key_mgmt_2;
int wpa_pairwise;
int wpa_group;
int wpa_group_rekey;
@@ -184,6 +186,8 @@ struct wpa_auth_config {
u32 wpa_pairwise_update_count;
int wpa_disable_eapol_key_retries;
int rsn_pairwise;
+ int rsn_override_pairwise;
+ int rsn_override_pairwise_2;
int rsn_preauth;
int eapol_version;
int wmm_enabled;
@@ -192,6 +196,8 @@ struct wpa_auth_config {
int okc;
int tx_status;
enum mfp_options ieee80211w;
+ enum mfp_options rsn_override_mfp;
+ enum mfp_options rsn_override_mfp_2;
int beacon_prot;
int group_mgmt_cipher;
int sae_require_mfp;
@@ -224,6 +230,21 @@ struct wpa_auth_config {
double corrupt_gtk_rekey_mic_probability;
u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
size_t own_ie_override_len;
+ bool rsne_override_set;
+ u8 rsne_override[MAX_OWN_IE_OVERRIDE];
+ size_t rsne_override_len;
+ bool rsnoe_override_set;
+ u8 rsnoe_override[MAX_OWN_IE_OVERRIDE];
+ size_t rsnoe_override_len;
+ bool rsno2e_override_set;
+ u8 rsno2e_override[MAX_OWN_IE_OVERRIDE];
+ size_t rsno2e_override_len;
+ bool rsnxe_override_set;
+ u8 rsnxe_override[MAX_OWN_IE_OVERRIDE];
+ size_t rsnxe_override_len;
+ bool rsnxoe_override_set;
+ u8 rsnxoe_override[MAX_OWN_IE_OVERRIDE];
+ size_t rsnxoe_override_len;
u8 rsne_override_eapol[MAX_OWN_IE_OVERRIDE];
size_t rsne_override_eapol_len;
u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
@@ -245,6 +266,7 @@ struct wpa_auth_config {
struct wpabuf *eapol_m1_elements;
struct wpabuf *eapol_m3_elements;
bool eapol_m3_no_encrypt;
+ bool eapol_key_reserved_random;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -262,10 +284,21 @@ struct wpa_auth_config {
#endif /* CONFIG_FILS */
enum sae_pwe sae_pwe;
bool sae_pk;
+ bool urnm_mfpr_x20;
+ bool urnm_mfpr;
unsigned int secure_ltf:1;
unsigned int secure_rtt:1;
unsigned int prot_range_neg:1;
+#ifdef CONFIG_ENC_ASSOC
+ unsigned int assoc_frame_encryption:1;
+ unsigned int pmksa_caching_privacy:1;
+ unsigned int eap_using_authentication_frames:1;
+#ifdef CONFIG_PASN
+ /* Whether to allow unauthenticated EPPKE (EPPKE without base AKM) */
+ bool eppke_unauth;
+#endif /* CONFIG_PASN */
+#endif /* CONFIG_ENC_ASSOC */
int owe_ptk_workaround;
u8 transition_disable;
@@ -295,6 +328,14 @@ struct wpa_auth_config {
#endif /* CONFIG_IEEE80211BE */
bool ssid_protection;
+
+ int rsn_override_omit_rsnxe;
+ int rsn_override_mlo_compat;
+
+ bool spp_amsdu;
+
+ unsigned int sae_pw_id_num;
+ u8 sae_pw_id_key[32];
};
typedef enum {
@@ -355,6 +396,12 @@ struct wpa_auth_callbacks {
void *ctx), void *cb_ctx);
int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
void *ctx), void *cb_ctx);
+#ifdef CONFIG_IEEE80211BE
+ int (*for_each_partner_auth)(void *ctx,
+ int (*cb)(struct wpa_authenticator *a,
+ void *ctx),
+ void *cb_ctx);
+#endif /* CONFIG_IEEE80211BE */
int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
size_t data_len);
int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
@@ -389,8 +436,6 @@ struct wpa_auth_callbacks {
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
- int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
- size_t tspec_ielen);
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_MESH
int (*start_ampe)(void *ctx, const u8 *sta_addr);
@@ -400,9 +445,13 @@ struct wpa_auth_callbacks {
size_t ltf_keyseed_len);
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
- int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
+ int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info,
+ bool rekey, int vlan_id);
+ struct wpa_authenticator * (*next_primary_auth)(void *ctx);
#endif /* CONFIG_IEEE80211BE */
int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
+ int (*remove_pmkid)(void *ctx, const u8 *sta_addr, const u8 *pmkid);
+ bool (*first_sta_seen_mbssid)(void *ctx, int vlan_id);
};
struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -429,11 +478,13 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len,
- struct wpa_state_machine *assoc_sm);
+ struct wpa_state_machine *assoc_sm,
+ bool is_ml);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm);
void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
struct wpa_state_machine *
@@ -462,6 +513,8 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+bool wpa_auth_get_first_sta_seen(struct wpa_authenticator *wpa_auth,
+ int vlan_id);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
@@ -481,11 +534,12 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int akmp);
+ int akmp, bool is_ml, int vlan_id);
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp, const u8 *dpp_pkhash);
+ int session_timeout, int akmp, const u8 *dpp_pkhash,
+ bool is_ml);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -500,10 +554,13 @@ wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache *
-wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml);
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid);
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+ const u8 **pmkid);
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr, const u8 *pmkid);
@@ -511,15 +568,17 @@ void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
u8 *pmkid, u8 *pmk, size_t *pmk_len);
-int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
+bool wpa_auth_ap_sta_support_pmkid_privacy(struct wpa_state_machine *sm);
#ifdef CONFIG_IEEE80211R_AP
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len,
- int omit_rsnxe);
+ int omit_rsnxe, bool reassoc, int vlan_id);
void wpa_ft_process_auth(struct wpa_state_machine *sm,
u16 auth_transaction, const u8 *ies, size_t ies_len,
void (*cb)(void *ctx, const u8 *dst,
@@ -597,19 +656,34 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
size_t pmk_len, const u8 *pmkid);
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
- u8 *pos, size_t max_len,
- const u8 *req_ies, size_t req_ies_len);
+ u8 *pos, size_t max_len);
u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
- u8 *pos, size_t max_len,
- const u8 *req_ies, size_t req_ies_len);
+ u8 *pos, size_t max_len);
bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
u8 *fd_rsn_info);
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_hash_alg_sae_ext_key(struct wpa_state_machine *sm,
+ size_t pmk_len);
+void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie,
+ size_t len);
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
u8 val);
+u8 * wpa_auth_eid_key_delivery(u8 *eid, size_t max_len,
+ struct wpa_state_machine *sm,
+ bool is_ml);
+u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len, bool is_ml);
+void wpa_store_eppke_pmk_ptk_sm(struct wpa_state_machine *sm,
+ const struct wpa_ptk *ptk, const u8 *pmk,
+ size_t pmk_len);
+int wpa_auth_epp_derive_new_pmkid(const u8 *anonce, const u8 *snonce,
+ int akmp, size_t pmk_len, u8 *pmkid);
+bool wpa_auth_ap_sta_support_assoc_enc(struct wpa_state_machine *sm);
+void wpa_auth_ensure_group_init(struct wpa_state_machine *sm);
+
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2);
@@ -644,10 +718,18 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
u8 mld_assoc_link_id, struct mld_info *info);
void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
struct wpa_auth_ml_link_key_info *info,
- bool mgmt_frame_prot, bool beacon_prot);
+ bool mgmt_frame_prot, bool beacon_prot,
+ bool rekey, int vlan_id);
+
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id,
+ bool rejected);
-void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
- int release_link_id);
+size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links);
+u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos,
+ u16 req_links);
+void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm,
+ struct wpa_authenticator *wpa_auth,
+ u8 mld_assoc_link_id);
#define for_each_sm_auth(sm, link_id) \
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
@@ -655,4 +737,35 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
sm->mld_links[link_id].wpa_auth && \
sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+static inline bool wpa_auth_pmf_enabled(struct wpa_auth_config *conf)
+{
+ return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+ conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+ conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+ const u8 *timestamp,
+ const u8 *mic, size_t mic_len);
+struct wpa_group * wpa_select_vlan_wpa_group(struct wpa_group *gsm,
+ int vlan_id);
+void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm,
+ const struct wpabuf *pw_id,
+ unsigned int counter);
+
+int wpa_auth_802_1x_get_msk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u8 *msk, size_t *len);
+int wpa_auth_802_1x_set_key(struct wpa_authenticator *wpa_auth,
+ enum wpa_alg alg, const u8 *addr,
+ u8 *key, size_t key_len);
+bool wpa_auth_ap_support_secure_ltf(struct wpa_authenticator *wpa_auth);
+int wpa_write_802_1x_rsne(struct wpa_authenticator *wpa_auth, u8 *buf,
+ size_t len, const u8 *pmkid, int akmp,
+ int pairwise_cipher, int group_cipher,
+ int group_mgmt_cipher, enum mfp_options mfp);
+int wpa_write_eppke_rsne(const u8 *wpa_ie, size_t wpa_ie_len,
+ u8 *buf, size_t len,
+ const u8 *pmkid, int akmp,
+ int pairwise_cipher, enum mfp_options mfp);
+
#endif /* WPA_AUTH_H */
diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c
index de16c314de8e..76b14ddd35cc 100644
--- a/contrib/wpa/src/ap/wpa_auth_ft.c
+++ b/contrib/wpa/src/ap/wpa_auth_ft.c
@@ -765,19 +765,6 @@ wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
}
-static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
- const u8 *sta_addr,
- u8 *tspec_ie, size_t tspec_ielen)
-{
- if (wpa_auth->cb->add_tspec == NULL) {
- wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
- return -1;
- }
- return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
- tspec_ielen);
-}
-
-
#ifdef CONFIG_OCV
static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
struct wpa_channel_info *ci)
@@ -2212,7 +2199,8 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
}
-static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len,
+ bool reassoc, int vlan_id)
{
u8 *subelem;
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
@@ -2224,6 +2212,11 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
const u8 *kek;
size_t kek_len;
+#ifdef CONFIG_IEEE80211BE
+ if (reassoc && vlan_id)
+ gsm = wpa_select_vlan_wpa_group(gsm, vlan_id);
+#endif /* CONFIG_IEEE80211BE */
+
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
kek_len = sm->PTK.kek2_len;
@@ -2247,8 +2240,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
pad_len += 8;
if (pad_len && key_len < sizeof(keybuf)) {
os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
- if (conf->disable_gtk ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2260,7 +2252,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
keybuf[key_len] = 0xdd;
key_len += pad_len;
key = keybuf;
- } else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ } else if (conf->disable_gtk) {
/*
* Provide unique random GTK to each STA to prevent use of GTK
* in the BSS.
@@ -2339,7 +2331,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
pos += 6;
*pos++ = igtk_len;
igtk = gsm->IGTK[gsm->GN_igtk - 4];
- if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ if (conf->disable_gtk) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
@@ -2372,7 +2364,6 @@ static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
const u8 *kek, *bigtk;
size_t kek_len;
size_t bigtk_len;
- u8 stub_bigtk[WPA_IGTK_MAX_LEN];
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
@@ -2400,17 +2391,6 @@ static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
pos += 6;
*pos++ = bigtk_len;
bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
- /*
- * Provide unique random BIGTK to each OSEN STA to prevent use
- * of BIGTK in the BSS.
- */
- if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
- os_free(subelem);
- return NULL;
- }
- bigtk = stub_bigtk;
- }
if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
wpa_printf(MSG_DEBUG,
"FT: BIGTK subelem encryption failed: kek_len=%d",
@@ -2499,24 +2479,6 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
}
#endif /* NEED_AP_MLME */
- if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
- int res;
-
- res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
- sizeof(struct wmm_tspec_element));
- if (res >= 0) {
- if (res)
- rdie->status_code = host_to_le16(res);
- else {
- /* TSPEC accepted; include updated TSPEC in
- * response */
- rdie->descr_count = 1;
- pos += sizeof(struct wmm_tspec_element);
- }
- return pos;
- }
- }
-
wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
@@ -2558,7 +2520,7 @@ static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len,
- int omit_rsnxe)
+ int omit_rsnxe, bool reassoc, int vlan_id)
{
u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
u8 *fte_mic, *elem_count;
@@ -2648,7 +2610,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
/* Fast BSS Transition Information */
if (auth_alg == WLAN_AUTH_FT) {
- subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ subelem = wpa_ft_gtk_subelem(sm, &subelem_len, reassoc,
+ vlan_id);
if (!subelem) {
wpa_printf(MSG_DEBUG,
"FT: Failed to add GTK subelement");
@@ -3204,19 +3167,19 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
sm->wpa_auth->conf.mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- retval = WLAN_STATUS_INVALID_MDIE;
+ retval = WLAN_STATUS_INVALID_MDE;
goto out;
}
if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3314,6 +3277,25 @@ pmk_r1_derived:
os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
sm->pmk_r1_len = pmk_r1_len;
+ /*
+ * Make sure the pairwise suite indicate by the STA matches the one that
+ * was used during initial mobility domain association.
+ */
+ if (!(parse.pairwise_cipher & pairwise)) {
+ wpa_printf(MSG_ERROR,
+ "FT: Pairwise cipher from PMK-R1 cache (0x%x) not used in the RSNE (0x%x) - reject",
+ pairwise, parse.pairwise_cipher);
+ retval = WLAN_STATUS_INVALID_PAIRWISE_CIPHER;
+ goto out;
+ }
+
+ if (sm->pmk_r1_len == SHA512_MAC_LEN)
+ sm->hash_alg = RSN_HASH_SHA512;
+ else if (sm->pmk_r1_len == SHA384_MAC_LEN)
+ sm->hash_alg = RSN_HASH_SHA384;
+ else
+ sm->hash_alg = RSN_HASH_SHA256;
+
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"ANonce");
@@ -3328,7 +3310,7 @@ pmk_r1_derived:
ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3339,7 +3321,7 @@ pmk_r1_derived:
ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3350,7 +3332,7 @@ pmk_r1_derived:
ftie = (const struct rsn_ftie *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3536,7 +3518,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
os_memcmp(mdie->mobility_domain, conf->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
- retval = WLAN_STATUS_INVALID_MDIE;
+ retval = WLAN_STATUS_INVALID_MDE;
goto out;
}
@@ -3555,7 +3537,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
wpa_printf(MSG_DEBUG,
"FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
parse.fte_mic_len, mic_len);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3565,7 +3547,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.fte_snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3575,13 +3557,13 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.fte_anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3594,13 +3576,13 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3612,7 +3594,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
conf->r1_key_holder, FT_R1KH_ID_LEN);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3674,7 +3656,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
@@ -3721,7 +3703,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
OCV_FAILURE "addr=" MACSTR
" frame=ft-reassoc-req error=%s",
MAC2STR(sm->addr), ocv_errorstr);
- retval = WLAN_STATUS_INVALID_FTIE;
+ retval = WLAN_STATUS_INVALID_FTE;
goto out;
}
}
@@ -3818,7 +3800,7 @@ static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst,
wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
MAC2STR(sm->addr));
wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
- WLAN_STATUS_SUCCESS, ies, ies_len);
+ resp, ies, ies_len);
}
diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c
index e725615d932c..ba645d525ea0 100644
--- a/contrib/wpa/src/ap/wpa_auth_glue.c
+++ b/contrib/wpa/src/ap/wpa_auth_glue.c
@@ -35,7 +35,82 @@
#include "wpa_auth_glue.h"
-static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+static void hostapd_wpa_auth_config_update(struct hostapd_data *hapd,
+ struct wpa_auth_config *_conf)
+{
+ struct hostapd_data *tx_bss;
+
+ _conf->msg_ctx = hapd->msg_ctx;
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ if (tx_bss != hapd)
+ _conf->tx_bss_auth = tx_bss->wpa_auth;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+ _conf->tx_status = 1;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+ _conf->ap_mlme = 1;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
+ (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
+ (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "Disable PTK0 rekey support - replaced with disconnect");
+ _conf->wpa_deny_ptk0_rekey = 1;
+ }
+
+ if (_conf->extended_key_id &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
+ else
+ _conf->extended_key_id = 0;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ _conf->beacon_prot = 0;
+
+#ifdef CONFIG_OCV
+ if (!(hapd->iface->drv_flags2 &
+ (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
+ _conf->ocv = 0;
+#endif /* CONFIG_OCV */
+
+ _conf->secure_ltf =
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
+ _conf->secure_rtt =
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
+ _conf->prot_range_neg =
+ !!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+
+#ifdef CONFIG_ENC_ASSOC
+ if (!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION))
+ _conf->assoc_frame_encryption = 0;
+#endif /* CONFIG_ENC_ASSOC */
+
+#ifdef CONFIG_IEEE80211BE
+ _conf->mld_addr = NULL;
+ _conf->link_id = -1;
+ _conf->first_link_auth = NULL;
+
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *lhapd;
+
+ _conf->mld_addr = hapd->mld->mld_addr;
+ _conf->link_id = hapd->mld_link_id;
+
+ for_each_mld_link(lhapd, hapd) {
+ if (lhapd == hapd)
+ continue;
+
+ if (lhapd->wpa_auth)
+ _conf->first_link_auth = lhapd->wpa_auth;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static void hostapd_wpa_auth_conf(struct hostapd_iface *iface,
+ struct hostapd_bss_config *conf,
struct hostapd_config *iconf,
struct wpa_auth_config *wconf)
{
@@ -43,8 +118,16 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
os_memset(wconf, 0, sizeof(*wconf));
wconf->wpa = conf->wpa;
+#ifdef CONFIG_ENC_ASSOC
+ wconf->assoc_frame_encryption = conf->assoc_frame_encryption;
+ wconf->pmksa_caching_privacy = conf->pmksa_caching_privacy;
+ wconf->eap_using_authentication_frames =
+ conf->eap_using_authentication_frames;
+#endif /* CONFIG_ENC_ASSOC */
wconf->extended_key_id = conf->extended_key_id;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+ wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt;
+ wconf->rsn_override_key_mgmt_2 = conf->rsn_override_key_mgmt_2;
wconf->wpa_pairwise = conf->wpa_pairwise;
wconf->wpa_group = conf->wpa_group;
wconf->wpa_group_rekey = conf->wpa_group_rekey;
@@ -56,6 +139,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
conf->wpa_disable_eapol_key_retries;
wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
wconf->rsn_pairwise = conf->rsn_pairwise;
+ wconf->rsn_override_pairwise = conf->rsn_override_pairwise;
+ wconf->rsn_override_pairwise_2 = conf->rsn_override_pairwise_2;
wconf->rsn_preauth = conf->rsn_preauth;
wconf->eapol_version = conf->eapol_version;
#ifdef CONFIG_MACSEC
@@ -70,6 +155,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#endif /* CONFIG_OCV */
wconf->okc = conf->okc;
wconf->ieee80211w = conf->ieee80211w;
+ wconf->rsn_override_mfp = conf->rsn_override_mfp;
+ wconf->rsn_override_mfp_2 = conf->rsn_override_mfp_2;
wconf->beacon_prot = conf->beacon_prot;
wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
wconf->sae_require_mfp = conf->sae_require_mfp;
@@ -103,17 +190,6 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_HS20
wconf->disable_gtk = conf->disable_dgaf;
- if (conf->osen) {
- wconf->disable_gtk = 1;
- wconf->wpa = WPA_PROTO_OSEN;
- wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
- wconf->wpa_pairwise = 0;
- wconf->wpa_group = WPA_CIPHER_CCMP;
- wconf->rsn_pairwise = WPA_CIPHER_CCMP;
- wconf->rsn_preauth = 0;
- wconf->disable_pmksa_caching = 1;
- wconf->ieee80211w = 1;
- }
#endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS
wconf->corrupt_gtk_rekey_mic_probability =
@@ -126,6 +202,46 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wpabuf_head(conf->own_ie_override),
wconf->own_ie_override_len);
}
+ if (conf->rsne_override &&
+ wpabuf_len(conf->rsne_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsne_override_len = wpabuf_len(conf->rsne_override);
+ os_memcpy(wconf->rsne_override,
+ wpabuf_head(conf->rsne_override),
+ wconf->rsne_override_len);
+ wconf->rsne_override_set = true;
+ }
+ if (conf->rsnoe_override &&
+ wpabuf_len(conf->rsnoe_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsnoe_override_len = wpabuf_len(conf->rsnoe_override);
+ os_memcpy(wconf->rsnoe_override,
+ wpabuf_head(conf->rsnoe_override),
+ wconf->rsnoe_override_len);
+ wconf->rsnoe_override_set = true;
+ }
+ if (conf->rsno2e_override &&
+ wpabuf_len(conf->rsno2e_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsno2e_override_len = wpabuf_len(conf->rsno2e_override);
+ os_memcpy(wconf->rsno2e_override,
+ wpabuf_head(conf->rsno2e_override),
+ wconf->rsno2e_override_len);
+ wconf->rsno2e_override_set = true;
+ }
+ if (conf->rsnxe_override &&
+ wpabuf_len(conf->rsnxe_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsnxe_override_len = wpabuf_len(conf->rsnxe_override);
+ os_memcpy(wconf->rsnxe_override,
+ wpabuf_head(conf->rsnxe_override),
+ wconf->rsnxe_override_len);
+ wconf->rsnxe_override_set = true;
+ }
+ if (conf->rsnxoe_override &&
+ wpabuf_len(conf->rsnxoe_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsnxoe_override_len = wpabuf_len(conf->rsnxoe_override);
+ os_memcpy(wconf->rsnxoe_override,
+ wpabuf_head(conf->rsnxoe_override),
+ wconf->rsnxoe_override_len);
+ wconf->rsnxoe_override_set = true;
+ }
if (conf->rsne_override_eapol &&
wpabuf_len(conf->rsne_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
wconf->rsne_override_eapol_set = 1;
@@ -190,6 +306,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
if (conf->eapol_m3_elements)
wconf->eapol_m3_elements = wpabuf_dup(conf->eapol_m3_elements);
wconf->eapol_m3_no_encrypt = conf->eapol_m3_no_encrypt;
+ wconf->eapol_key_reserved_random = conf->eapol_key_reserved_random;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -219,15 +336,35 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->dpp_pfs = conf->dpp_pfs;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_PASN
+#ifdef CONFIG_ENC_ASSOC
+ wconf->eppke_unauth = conf->eppke_unauth;
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_TESTING_OPTIONS
wconf->force_kdk_derivation = conf->force_kdk_derivation;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
+ if (conf->urnm_mfpr_x20 == 1)
+ wconf->urnm_mfpr_x20 = true;
+ if (conf->urnm_mfpr != 0)
+ wconf->urnm_mfpr = true;
wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
wconf->no_disconnect_on_group_keyerror =
conf->bss_max_idle && conf->ap_max_inactivity &&
conf->no_disconnect_on_group_keyerror;
+
+ wconf->rsn_override_omit_rsnxe = conf->rsn_override_omit_rsnxe;
+ wconf->rsn_override_mlo_compat = conf->rsn_override_mlo_compat;
+ wconf->spp_amsdu = conf->spp_amsdu &&
+ (iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU);
+
+ if (conf->sae_pw_id_num && conf->sae_pw_id_key &&
+ wpabuf_len(conf->sae_pw_id_key) == sizeof(wconf->sae_pw_id_key)) {
+ wconf->sae_pw_id_num = conf->sae_pw_id_num;
+ os_memcpy(wconf->sae_pw_id_key,
+ wpabuf_head(conf->sae_pw_id_key),
+ wpabuf_len(conf->sae_pw_id_key));
+ }
}
@@ -371,13 +508,17 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
#endif /* CONFIG_SAE */
#ifdef CONFIG_OWE
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &
+ WPA_KEY_MGMT_OWE) &&
sta && sta->owe_pmk) {
if (psk_len)
*psk_len = sta->owe_pmk_len;
return sta->owe_pmk;
}
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+ if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &
+ WPA_KEY_MGMT_OWE) && sta) {
struct rsn_pmksa_cache_entry *sa;
sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
@@ -459,10 +600,20 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
size_t key_len, enum key_flag key_flag)
{
struct hostapd_data *hapd = ctx;
+ struct hostapd_data *link_bss = hapd;
const char *ifname = hapd->conf->iface;
+ int set_tx = !(key_flag & KEY_FLAG_NEXT);
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ link_bss = hostapd_mld_get_first_bss(hapd);
+ if (!link_bss)
+ link_bss = hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
if (vlan_id > 0) {
- ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+ ifname = hostapd_get_vlan_id_ifname(link_bss->conf->vlan,
+ vlan_id);
if (!ifname) {
if (!(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_VLAN_OFFLOAD))
@@ -512,8 +663,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
hapd->last_gtk_len = key_len;
}
#endif /* CONFIG_TESTING_OPTIONS */
- return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1,
- NULL, 0, key, key_len, key_flag);
+ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id,
+ set_tx, NULL, 0, key, key_len, key_flag);
}
@@ -632,6 +783,29 @@ static int hostapd_wpa_auth_for_each_auth(
}
+#ifdef CONFIG_IEEE80211BE
+static int hostapd_wpa_auth_for_each_partner_auth(
+ void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
+ void *cb_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct hostapd_data *bss;
+
+ if (cb(hapd->wpa_auth, cb_ctx))
+ return 1;
+
+ if (!hapd->mld)
+ return 0;
+
+ for_each_mld_link(bss, hapd) {
+ if (bss != hapd && bss->wpa_auth && cb(bss->wpa_auth, cb_ctx))
+ return 1;
+ }
+ return 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
#ifdef CONFIG_IEEE80211R_AP
struct wpa_ft_rrb_rx_later_data {
@@ -972,7 +1146,7 @@ static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher,
struct hostapd_data *hapd = ctx;
ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
- ptk, NULL, NULL, 0);
+ ptk, NULL, NULL, 0, 0);
}
@@ -1441,15 +1615,6 @@ static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
}
-static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
- u8 *tspec_ie, size_t tspec_ielen)
-{
- struct hostapd_data *hapd = ctx;
- return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
-}
-
-
-
static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
const char *ft_iface)
{
@@ -1535,10 +1700,31 @@ static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
#endif /* CONFIG_PASN */
+static bool hostapd_first_sta_seen_mbssid(void *ctx, int vlan_id)
+{
+ struct hostapd_data *hapd = ctx;
+
+ if (hapd->iconf->mbssid) {
+ size_t i;
+
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *tmp_hapd = hapd->iface->bss[i];
+
+ if (wpa_auth_get_first_sta_seen(tmp_hapd->wpa_auth,
+ vlan_id))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
#ifdef CONFIG_IEEE80211BE
static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
- struct wpa_auth_ml_key_info *info)
+ struct wpa_auth_ml_key_info *info,
+ bool rekey, int vlan_id)
{
struct hostapd_data *hapd = ctx;
unsigned int i;
@@ -1562,7 +1748,8 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
wpa_auth_ml_get_key_info(hapd->wpa_auth,
&info->links[i],
info->mgmt_frame_prot,
- info->beacon_prot);
+ info->beacon_prot,
+ rekey, vlan_id);
continue;
}
@@ -1573,7 +1760,8 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
wpa_auth_ml_get_key_info(bss->wpa_auth,
&info->links[i],
info->mgmt_frame_prot,
- info->beacon_prot);
+ info->beacon_prot,
+ rekey, vlan_id);
link_bss_found = true;
break;
}
@@ -1586,6 +1774,21 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
return 0;
}
+
+static struct wpa_authenticator * hostapd_next_primary_auth(void *cb_ctx)
+{
+ struct hostapd_data *hapd = cb_ctx, *bss;
+
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd)
+ continue;
+ if (bss->wpa_auth)
+ return bss->wpa_auth;
+ }
+
+ return NULL;
+}
+
#endif /* CONFIG_IEEE80211BE */
@@ -1603,6 +1806,15 @@ static int hostapd_wpa_auth_get_drv_flags(void *ctx,
}
+static int hostapd_wpa_auth_remove_pmkid(void *ctx, const u8 *sta_addr,
+ const u8 *pmkid)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_remove_pmkid(hapd, sta_addr, pmkid);
+}
+
+
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
@@ -1621,6 +1833,9 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
.get_sta_count = hostapd_wpa_auth_get_sta_count,
.for_each_sta = hostapd_wpa_auth_for_each_sta,
.for_each_auth = hostapd_wpa_auth_for_each_auth,
+#ifdef CONFIG_IEEE80211BE
+ .for_each_partner_auth = hostapd_wpa_auth_for_each_partner_auth,
+#endif /* CONFIG_IEEE80211BE */
.send_ether = hostapd_wpa_auth_send_ether,
.send_oui = hostapd_wpa_auth_send_oui,
.channel_info = hostapd_channel_info,
@@ -1637,7 +1852,6 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
.send_ft_action = hostapd_wpa_auth_send_ft_action,
.add_sta = hostapd_wpa_auth_add_sta,
.add_sta_ft = hostapd_wpa_auth_add_sta_ft,
- .add_tspec = hostapd_wpa_auth_add_tspec,
.set_vlan = hostapd_wpa_auth_set_vlan,
.get_vlan = hostapd_wpa_auth_get_vlan,
.set_identity = hostapd_wpa_auth_set_identity,
@@ -1655,75 +1869,17 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+ .next_primary_auth = hostapd_next_primary_auth,
#endif /* CONFIG_IEEE80211BE */
.get_drv_flags = hostapd_wpa_auth_get_drv_flags,
+ .remove_pmkid = hostapd_wpa_auth_remove_pmkid,
+ .first_sta_seen_mbssid = hostapd_first_sta_seen_mbssid,
};
const u8 *wpa_ie;
size_t wpa_ie_len;
- struct hostapd_data *tx_bss;
- hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
- _conf.msg_ctx = hapd->msg_ctx;
- tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- if (tx_bss != hapd)
- _conf.tx_bss_auth = tx_bss->wpa_auth;
- if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
- _conf.tx_status = 1;
- if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
- _conf.ap_mlme = 1;
-
- if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
- (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
- (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
- !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- "Disable PTK0 rekey support - replaced with disconnect");
- _conf.wpa_deny_ptk0_rekey = 1;
- }
-
- if (_conf.extended_key_id &&
- (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
- else
- _conf.extended_key_id = 0;
-
- if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
- _conf.beacon_prot = 0;
-
-#ifdef CONFIG_OCV
- if (!(hapd->iface->drv_flags2 &
- (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
- _conf.ocv = 0;
-#endif /* CONFIG_OCV */
-
- _conf.secure_ltf =
- !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
- _conf.secure_rtt =
- !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
- _conf.prot_range_neg =
- !!(hapd->iface->drv_flags2 &
- WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
-
-#ifdef CONFIG_IEEE80211BE
- _conf.mld_addr = NULL;
- _conf.link_id = -1;
- _conf.first_link_auth = NULL;
-
- if (hapd->conf->mld_ap) {
- struct hostapd_data *lhapd;
-
- _conf.mld_addr = hapd->mld->mld_addr;
- _conf.link_id = hapd->mld_link_id;
-
- for_each_mld_link(lhapd, hapd) {
- if (lhapd == hapd)
- continue;
-
- if (lhapd->wpa_auth)
- _conf.first_link_auth = lhapd->wpa_auth;
- }
- }
-#endif /* CONFIG_IEEE80211BE */
+ hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf, &_conf);
+ hostapd_wpa_auth_config_update(hapd, &_conf);
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
@@ -1788,7 +1944,10 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
void hostapd_reconfig_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config wpa_auth_conf;
- hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+
+ hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf,
+ &wpa_auth_conf);
+ hostapd_wpa_auth_config_update(hapd, &wpa_auth_conf);
wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
}
diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h
index 4e5ba3e2ed6f..c9427bb72688 100644
--- a/contrib/wpa/src/ap/wpa_auth_i.h
+++ b/contrib/wpa/src/ap/wpa_auth_i.h
@@ -61,6 +61,7 @@ struct wpa_state_machine {
unsigned int pmk_len;
u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK;
+ enum rsn_hash_alg hash_alg;
u8 keyidx_active;
bool use_ext_key_id;
bool PTK_valid;
@@ -97,6 +98,7 @@ struct wpa_state_machine {
#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1;
+ unsigned int spp_amsdu:1;
unsigned int ptkstart_without_success;
@@ -111,6 +113,8 @@ struct wpa_state_machine {
size_t wpa_ie_len;
u8 *rsnxe;
size_t rsnxe_len;
+ u8 *rsn_selection;
+ size_t rsn_selection_len;
enum {
WPA_VERSION_NO_WPA = 0 /* WPA not used */,
@@ -124,6 +128,9 @@ struct wpa_state_machine {
u32 dot11RSNAStatsTKIPLocalMICFailures;
u32 dot11RSNAStatsTKIPRemoteMICFailures;
+ bool rsn_override;
+ bool rsn_override_2;
+
#ifdef CONFIG_IEEE80211R_AP
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
* first 384 bits of MSK */
@@ -178,6 +185,7 @@ struct wpa_state_machine {
struct mld_link {
bool valid;
+ bool rejected;
u8 peer_addr[ETH_ALEN];
struct wpa_authenticator *wpa_auth;
@@ -185,6 +193,9 @@ struct wpa_state_machine {
#endif /* CONFIG_IEEE80211BE */
bool ssid_protection;
+
+ struct wpabuf *sae_pw_id;
+ unsigned int sae_pw_id_counter;
};
@@ -261,6 +272,8 @@ struct wpa_authenticator {
#endif /* CONFIG_P2P */
#ifdef CONFIG_IEEE80211BE
+ /* MLD-level PMKSA cache for non-AP MLD entries only. */
+ struct rsn_pmksa_cache *ml_pmksa;
bool is_ml;
u8 mld_addr[ETH_ALEN];
u8 link_id;
diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c
index 2efadf896186..8fa424376e43 100644
--- a/contrib/wpa/src/ap/wpa_auth_ie.c
+++ b/contrib/wpa/src/ap/wpa_auth_ie.c
@@ -89,7 +89,8 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
}
-static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
+static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf,
+ enum mfp_options mfp)
{
u16 capab = 0;
@@ -99,9 +100,9 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
/* 4 PTKSA replay counters when using WMM */
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ if (mfp != NO_MGMT_FRAME_PROTECTION) {
capab |= WPA_CAPABILITY_MFPC;
- if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ if (mfp == MGMT_FRAME_PROTECTION_REQUIRED)
capab |= WPA_CAPABILITY_MFPR;
}
#ifdef CONFIG_OCV
@@ -119,24 +120,19 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
}
-int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
- const u8 *pmkid)
+static u8 * rsne_write_data(u8 *buf, size_t len, u8 *pos, int group,
+ int pairwise, int key_mgmt, u16 rsn_capab,
+ const u8 *pmkid, enum mfp_options mfp,
+ int group_mgmt_cipher)
{
- struct rsn_ie_hdr *hdr;
int num_suites, res;
- u8 *pos, *count;
+ u8 *count;
u32 suite;
- hdr = (struct rsn_ie_hdr *) buf;
- hdr->elem_id = WLAN_EID_RSN;
- WPA_PUT_LE16(hdr->version, RSN_VERSION);
- pos = (u8 *) (hdr + 1);
-
- suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group);
if (suite == 0) {
- wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
- conf->wpa_group);
- return -1;
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", group);
+ return NULL;
}
RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
@@ -153,7 +149,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
#endif /* CONFIG_RSN_TESTING */
- res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+ res = rsn_cipher_put_suites(pos, pairwise);
num_suites += res;
pos += res * RSN_SELECTOR_LEN;
@@ -167,8 +163,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
- conf->rsn_pairwise);
- return -1;
+ pairwise);
+ return NULL;
}
WPA_PUT_LE16(count, num_suites);
@@ -184,102 +180,102 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
#endif /* CONFIG_RSN_TESTING */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ if (key_mgmt & WPA_KEY_MGMT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_IEEE80211R_AP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_SHA384
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SHA384 */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SHA384
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SHA384 */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_SAE
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ if (key_mgmt & WPA_KEY_MGMT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+ if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SAE */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_FILS
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_IEEE80211R_AP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
@@ -287,33 +283,33 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
#endif /* CONFIG_IEEE80211R_AP */
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ if (key_mgmt & WPA_KEY_MGMT_OWE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ if (key_mgmt & WPA_KEY_MGMT_DPP) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
- RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
- pos += RSN_SELECTOR_LEN;
- num_suites++;
- }
-#endif /* CONFIG_HS20 */
#ifdef CONFIG_PASN
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) {
+ if (key_mgmt & WPA_KEY_MGMT_PASN) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_PASN */
+#ifdef CONFIG_ENC_ASSOC
+ if (key_mgmt & WPA_KEY_MGMT_EPPKE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing) {
@@ -325,18 +321,18 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
- conf->wpa_key_mgmt);
- return -1;
+ key_mgmt);
+ return NULL;
}
WPA_PUT_LE16(count, num_suites);
/* RSN Capabilities */
- WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ WPA_PUT_LE16(pos, rsn_capab);
pos += 2;
if (pmkid) {
if (2 + PMKID_LEN > buf + len - pos)
- return -1;
+ return NULL;
/* PMKID Count */
WPA_PUT_LE16(pos, 1);
pos += 2;
@@ -344,21 +340,19 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += PMKID_LEN;
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+
+ if (mfp != NO_MGMT_FRAME_PROTECTION &&
+ group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
if (2 + 4 > buf + len - pos)
- return -1;
- if (pmkid == NULL) {
+ return NULL;
+ if (!pmkid) {
/* PMKID Count */
WPA_PUT_LE16(pos, 0);
pos += 2;
}
/* Management Group Cipher Suite */
- switch (conf->group_mgmt_cipher) {
- case WPA_CIPHER_AES_128_CMAC:
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
- break;
+ switch (group_mgmt_cipher) {
case WPA_CIPHER_BIP_GMAC_128:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
break;
@@ -371,8 +365,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
default:
wpa_printf(MSG_DEBUG,
"Invalid group management cipher (0x%x)",
- conf->group_mgmt_cipher);
- return -1;
+ group_mgmt_cipher);
+ return NULL;
}
pos += RSN_SELECTOR_LEN;
}
@@ -384,12 +378,12 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
* the element.
*/
int pmkid_count_set = pmkid != NULL;
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+ if (mfp != NO_MGMT_FRAME_PROTECTION)
pmkid_count_set = 1;
/* PMKID Count */
WPA_PUT_LE16(pos, 0);
pos += 2;
- if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ if (mfp == NO_MGMT_FRAME_PROTECTION) {
/* Management Group Cipher Suite */
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
pos += RSN_SELECTOR_LEN;
@@ -399,6 +393,27 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += 17;
}
#endif /* CONFIG_RSN_TESTING */
+ return pos;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid)
+{
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_pairwise, conf->wpa_key_mgmt,
+ wpa_own_rsn_capab(conf, conf->ieee80211w), pmkid,
+ conf->ieee80211w, conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
hdr->len = (pos - buf) - 2;
@@ -406,16 +421,139 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
-int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+static int wpa_write_rsne_override(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
{
- u8 *pos = buf;
- u32 capab = 0, tmp;
- size_t flen;
+ u8 *pos, *len_pos;
- if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
+ pos = buf;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len_pos = pos++;
+
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
+
+ WPA_PUT_LE16(pos, RSN_VERSION);
+ pos += 2;
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_override_pairwise,
+ conf->rsn_override_key_mgmt,
+ wpa_own_rsn_capab(conf, conf->rsn_override_mfp),
+ NULL, conf->rsn_override_mfp,
+ conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
+
+ *len_pos = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static int wpa_write_rsne_override_2(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
+{
+ u8 *pos, *len_pos;
+
+ pos = buf;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len_pos = pos++;
+
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ pos += 4;
+
+ WPA_PUT_LE16(pos, RSN_VERSION);
+ pos += 2;
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_override_pairwise_2,
+ conf->rsn_override_key_mgmt_2,
+ wpa_own_rsn_capab(conf, conf->rsn_override_mfp_2),
+ NULL, conf->rsn_override_mfp_2,
+ conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
+
+ *len_pos = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+#ifdef CONFIG_IEEE8021X_AUTH
+int wpa_write_802_1x_rsne(struct wpa_authenticator *wpa_auth, u8 *buf,
+ size_t len, const u8 *pmkid, int akmp,
+ int pairwise_cipher, int group_cipher,
+ int group_mgmt_cipher, enum mfp_options mfp)
+{
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ pos = rsne_write_data(buf, len, pos, group_cipher,
+ pairwise_cipher, akmp,
+ wpa_own_rsn_capab(conf, conf->ieee80211w),
+ pmkid, mfp, group_mgmt_cipher);
+ if (!pos)
+ return -1;
+
+ hdr->len = pos - buf - 2;
+
+ return pos - buf;
+}
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+
+#ifdef CONFIG_ENC_ASSOC
+int wpa_write_eppke_rsne(const u8 *wpa_ie, size_t wpa_ie_len,
+ u8 *buf, size_t len,
+ const u8 *pmkid, int akmp,
+ int pairwise_cipher, enum mfp_options mfp)
+{
+ struct rsn_ie_hdr *hdr;
+ struct wpa_ie_data data;
+ const u8 *rsne;
+ u8 *pos;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ os_memset(&data, 0, sizeof(data));
+ rsne = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_RSN);
+ if (!rsne || wpa_parse_wpa_ie_rsn(rsne, 2 + rsne[1], &data) < 0) {
+ wpa_printf(MSG_INFO,
+ "EPPKE: No valid AP RSNE as a starting point");
+ return -1;
+ }
+ pos = rsne_write_data(buf, len, pos, data.group_cipher, pairwise_cipher,
+ akmp, data.capabilities, pmkid, mfp,
+ data.mgmt_group_cipher);
+ if (!pos)
+ return -1;
+
+ hdr->len = pos - buf - 2;
+
+ return pos - buf;
+}
+#endif /* CONFIG_ENC_ASSOC */
+
+
+static u64 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt)
+{
+ u64 capab = 0;
+
+ if (wpa_key_mgmt_sae(key_mgmt) &&
(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
- wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
+ wpa_key_mgmt_sae_ext_key(key_mgmt))) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (conf->sae_pk)
@@ -427,10 +565,46 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
if (conf->secure_rtt)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
- if (conf->prot_range_neg)
- capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (conf->prot_range_neg) {
+ if (conf->urnm_mfpr)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (conf->urnm_mfpr_x20)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20);
+ }
if (conf->ssid_protection)
capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+ if (conf->spp_amsdu)
+ capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
+#ifdef CONFIG_ENC_ASSOC
+ /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy)
+ * a STA that sets the PMKSA Caching Privacy Support
+ * field in the RSNXE to 1 shall set the (Re)Association
+ * Frame Encryption Support field in the RSNXE to 1.
+ */
+ if (conf->assoc_frame_encryption ||
+ conf->pmksa_caching_privacy)
+ capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION);
+ if (key_mgmt & WPA_KEY_MGMT_EPPKE)
+ capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN);
+ if (conf->pmksa_caching_privacy)
+ capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY);
+ if (conf->eap_using_authentication_frames)
+ capab |= BIT(WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES);
+ if (conf->eppke_unauth)
+ capab |= BIT_ULL(WLAN_RSNX_CAPAB_UNAUTH_EPPKE);
+#endif /* CONFIG_ENC_ASSOC */
+
+ return capab;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u64 capab, tmp;
+ size_t flen;
+
+ capab = rsnxe_capab(conf, conf->wpa_key_mgmt);
if (!capab)
return 0; /* no supported extended RSN capabilities */
@@ -455,60 +629,45 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
}
-static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
{
- u8 *len;
- u16 capab;
-
- *eid++ = WLAN_EID_VENDOR_SPECIFIC;
- len = eid++; /* to be filled */
- WPA_PUT_BE24(eid, OUI_WFA);
- eid += 3;
- *eid++ = HS20_OSEN_OUI_TYPE;
+ u8 *pos = buf;
+ u64 capab, tmp;
+ size_t flen;
- /* Group Data Cipher Suite */
- RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
- eid += RSN_SELECTOR_LEN;
+ capab = rsnxe_capab(conf, conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2);
- /* Pairwise Cipher Suite Count and List */
- WPA_PUT_LE16(eid, 1);
- eid += 2;
- RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
- eid += RSN_SELECTOR_LEN;
+ if (!capab)
+ return 0; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
+ if (len < 2 + flen)
+ return -1;
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
- /* AKM Suite Count and List */
- WPA_PUT_LE16(eid, 1);
- eid += 2;
- RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
- eid += RSN_SELECTOR_LEN;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4 + flen;
+ WPA_PUT_BE32(pos, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
- /* RSN Capabilities */
- capab = 0;
- if (conf->wmm_enabled) {
- /* 4 PTKSA replay counters when using WMM */
- capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
- }
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- capab |= WPA_CAPABILITY_MFPC;
- if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
- capab |= WPA_CAPABILITY_MFPR;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
}
-#ifdef CONFIG_OCV
- if (conf->ocv)
- capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
- WPA_PUT_LE16(eid, capab);
- eid += 2;
- *len = eid - len - 1;
-
- return eid;
+ return pos - buf;
}
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
- u8 *pos, buf[128];
+ u8 *pos, buf[1500];
int res;
#ifdef CONFIG_TESTING_OPTIONS
@@ -530,21 +689,55 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
pos = buf;
- if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
- pos = wpa_write_osen(&wpa_auth->conf, pos);
- }
if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.rsne_override_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Forced own RSNE for testing",
+ wpa_auth->conf.rsne_override,
+ wpa_auth->conf.rsne_override_len);
+ if (sizeof(buf) - (pos - buf) <
+ wpa_auth->conf.rsne_override_len)
+ return -1;
+ os_memcpy(pos, wpa_auth->conf.rsne_override,
+ wpa_auth->conf.rsne_override_len);
+ pos += wpa_auth->conf.rsne_override_len;
+ goto rsnxe;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_write_rsn_ie(&wpa_auth->conf,
pos, buf + sizeof(buf) - pos, NULL);
if (res < 0)
return res;
pos += res;
- res = wpa_write_rsnxe(&wpa_auth->conf, pos,
- buf + sizeof(buf) - pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ rsnxe:
+ if (wpa_auth->conf.rsnxe_override_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Forced own RSNXE for testing",
+ wpa_auth->conf.rsnxe_override,
+ wpa_auth->conf.rsnxe_override_len);
+ if (sizeof(buf) - (pos - buf) <
+ wpa_auth->conf.rsnxe_override_len)
+ return -1;
+ os_memcpy(pos, wpa_auth->conf.rsnxe_override,
+ wpa_auth->conf.rsnxe_override_len);
+ pos += wpa_auth->conf.rsnxe_override_len;
+ goto fte;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (wpa_auth->conf.rsn_override_omit_rsnxe)
+ res = 0;
+ else
+ res = wpa_write_rsnxe(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
if (res < 0)
return res;
pos += res;
}
+#ifdef CONFIG_TESTING_OPTIONS
+fte:
+#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
res = wpa_write_mdie(&wpa_auth->conf, pos,
@@ -561,7 +754,87 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
return res;
pos += res;
}
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt) {
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.rsnoe_override_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Forced own RSNOE for testing",
+ wpa_auth->conf.rsnoe_override,
+ wpa_auth->conf.rsnoe_override_len);
+ if (sizeof(buf) - (pos - buf) <
+ wpa_auth->conf.rsnoe_override_len)
+ return -1;
+ os_memcpy(pos, wpa_auth->conf.rsnoe_override,
+ wpa_auth->conf.rsnoe_override_len);
+ pos += wpa_auth->conf.rsnoe_override_len;
+ goto rsno2e;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ res = wpa_write_rsne_override(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+rsno2e:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt_2) {
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.rsno2e_override_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Forced own RSNO2E for testing",
+ wpa_auth->conf.rsno2e_override,
+ wpa_auth->conf.rsno2e_override_len);
+ if (sizeof(buf) - (pos - buf) <
+ wpa_auth->conf.rsno2e_override_len)
+ return -1;
+ os_memcpy(pos, wpa_auth->conf.rsno2e_override,
+ wpa_auth->conf.rsno2e_override_len);
+ pos += wpa_auth->conf.rsno2e_override_len;
+ goto rsnxoe;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ res = wpa_write_rsne_override_2(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+rsnxoe:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ (wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2)) {
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.rsnxoe_override_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Forced own RSNXOE for testing",
+ wpa_auth->conf.rsnxoe_override,
+ wpa_auth->conf.rsnxoe_override_len);
+ if (sizeof(buf) - (pos - buf) <
+ wpa_auth->conf.rsnxoe_override_len)
+ return -1;
+ os_memcpy(pos, wpa_auth->conf.rsnxoe_override,
+ wpa_auth->conf.rsnxoe_override_len);
+ pos += wpa_auth->conf.rsnxoe_override_len;
+ goto done;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ res = wpa_write_rsnxe_override(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+done:
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "RSN: Own IEs", buf, pos - buf);
os_free(wpa_auth->wpa_ie);
wpa_auth->wpa_ie = os_malloc(pos - buf);
if (wpa_auth->wpa_ie == NULL)
@@ -609,6 +882,32 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
}
+#ifdef CONFIG_IEEE80211BE
+
+struct wpa_auth_link_iter_data {
+ struct wpa_authenticator *wpa_auth;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+static int wpa_auth_pmksa_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_link_iter_data *data = ctx;
+
+ if (a == data->wpa_auth ||
+ !ether_addr_equal(a->mld_addr, data->wpa_auth->mld_addr))
+ return 0;
+
+ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+ if (data->pmksa)
+ return 1;
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
enum wpa_validate_result
wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int freq,
@@ -616,7 +915,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len,
- struct wpa_state_machine *assoc_sm)
+ struct wpa_state_machine *assoc_sm, bool is_ml)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data;
@@ -624,6 +923,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
u32 selector;
size_t i;
const u8 *pmkid = NULL;
+ bool ap_pmf_enabled;
if (wpa_auth == NULL || sm == NULL)
return WPA_NOT_ENABLED;
@@ -717,14 +1017,14 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
selector = RSN_AUTH_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
- else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
- selector = RSN_AUTH_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
#ifdef CONFIG_SHA384
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
selector = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_ENC_ASSOC
+ else if (data.key_mgmt & WPA_KEY_MGMT_EPPKE)
+ selector = RSN_AUTH_KEY_MGMT_EPPKE;
+#endif /* CONFIG_ENC_ASSOC */
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -773,7 +1073,26 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_INVALID_GROUP;
}
- key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+ if (sm->rsn_override_2)
+ key_mgmt = data.key_mgmt &
+ wpa_auth->conf.rsn_override_key_mgmt_2;
+ else if (sm->rsn_override)
+ key_mgmt = data.key_mgmt & wpa_auth->conf.rsn_override_key_mgmt;
+ else
+ key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+
+ /* For an MLO partner link, the STA may not include an RSN Selection
+ * element (no RSNO support), so sm->rsn_override* are both false and
+ * key_mgmt is matched against wpa_key_mgmt (e.g., WPA-PSK on 2.4 GHz).
+ * But the STA negotiated via the primary link (e.g., SAE on 6 GHz)
+ * and sends SAE in the per-STA profile RSNE assuming that the same
+ * AKM is enabled on all affiliated links. When rsn_override_mlo_compat
+ * is enabled, fall back to rsn_override_key_mgmt so that SAE is
+ * accepted on the partner link in such cases.
+ */
+ if (!key_mgmt && assoc_sm && wpa_auth->conf.rsn_override_mlo_compat)
+ key_mgmt = data.key_mgmt & wpa_auth->conf.rsn_override_key_mgmt;
+
if (!key_mgmt) {
wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
MACSTR, data.key_mgmt, MAC2STR(sm->addr));
@@ -835,14 +1154,20 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else if (key_mgmt & WPA_KEY_MGMT_DPP)
sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
- else if (key_mgmt & WPA_KEY_MGMT_OSEN)
- sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
+#ifdef CONFIG_ENC_ASSOC
+ else if (key_mgmt & WPA_KEY_MGMT_EPPKE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_EPPKE;
+#endif /* CONFIG_ENC_ASSOC */
else
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
- if (version == WPA_PROTO_RSN)
+ if (version == WPA_PROTO_RSN && sm->rsn_override_2)
+ ciphers = data.pairwise_cipher &
+ wpa_auth->conf.rsn_override_pairwise_2;
+ else if (version == WPA_PROTO_RSN && sm->rsn_override)
+ ciphers = data.pairwise_cipher &
+ wpa_auth->conf.rsn_override_pairwise;
+ else if (version == WPA_PROTO_RSN)
ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
else
ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
@@ -902,8 +1227,16 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
wpa_auth->conf.ocv : 0);
}
#endif /* CONFIG_OCV */
+ if (sm->rsn_override_2)
+ ap_pmf_enabled = conf->rsn_override_mfp_2 !=
+ NO_MGMT_FRAME_PROTECTION;
+ else if (sm->rsn_override)
+ ap_pmf_enabled = conf->rsn_override_mfp !=
+ NO_MGMT_FRAME_PROTECTION;
+ else
+ ap_pmf_enabled = conf->ieee80211w != NO_MGMT_FRAME_PROTECTION;
- if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+ if (!ap_pmf_enabled ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
else
@@ -916,6 +1249,14 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
+ if (wpa_auth->conf.spp_amsdu &&
+ ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU) &&
+ (ciphers & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+ WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+ sm->spp_amsdu = 1;
+ else
+ sm->spp_amsdu = 0;
+
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
@@ -990,10 +1331,39 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
sm->pmksa = NULL;
for (i = 0; i < data.num_pmkid; i++) {
+ struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+
wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
&data.pmkid[i * PMKID_LEN], PMKID_LEN);
- sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+#ifdef CONFIG_IEEE80211BE
+ if (is_ml)
+ pmksa = wpa_auth->ml_pmksa;
+#endif /* CONFIG_IEEE80211BE */
+ sm->pmksa = pmksa_cache_auth_get(pmksa, sm->addr,
&data.pmkid[i * PMKID_LEN]);
+#ifdef CONFIG_IEEE80211BE
+ if (!sm->pmksa && !is_ml && wpa_auth->is_ml)
+ sm->pmksa = pmksa_cache_auth_get(
+ wpa_auth->ml_pmksa, sm->addr,
+ &data.pmkid[i * PMKID_LEN]);
+ if (!sm->pmksa && is_ml) {
+ struct wpa_auth_link_iter_data idata;
+
+ idata.wpa_auth = wpa_auth;
+ idata.pmksa = NULL;
+ idata.spa = sm->addr;
+ idata.pmkid = &data.pmkid[i * PMKID_LEN];
+ wpa_auth_for_each_auth(wpa_auth,
+ wpa_auth_pmksa_iter,
+ &idata);
+ if (idata.pmksa)
+ sm->pmksa = idata.pmksa;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ if (!sm->pmksa && !is_zero_ether_addr(sm->p2p_dev_addr))
+ sm->pmksa = pmksa_cache_auth_get(
+ wpa_auth->pmksa, sm->p2p_dev_addr,
+ &data.pmkid[i * PMKID_LEN]);
if (sm->pmksa) {
pmkid = sm->pmksa->pmkid;
break;
@@ -1025,7 +1395,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKID found from PMKSA cache eap_type=%d vlan=%d%s",
sm->pmksa->eap_type_authsrv,
- vlan ? vlan->untagged : 0,
+ vlan ? vlan->untagged :
+ sm->pmksa->sae_vlan_id,
(vlan && vlan->tagged[0]) ? "+" : "");
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
@@ -1044,7 +1415,21 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
!!(drv_flags2 &
WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP);
- if (!ap_sae_offload && data.num_pmkid && !sm->pmksa) {
+ /* Authenticator needs to have a PMKSA corresponding to a
+ * PMKID (if present) included by the STA in (Re)Association
+ * Request frame if PMKSA caching is attempted to be used. In
+ * case of SAE, this follows Open System authentication. IEEE
+ * Std 802.11 mandates the AP to reject (re)association trying
+ * to use PMKSA caching for SAE authentication. While the
+ * PMKID (if any) in the RSNE in (Re)Association Request frame
+ * following SAE authentication (i.e., in the case of no PMKSA
+ * caching) is not really supposed to include an unknown PMKID,
+ * the standard does not require the AP to reject association.
+ * The PMKSA that was just derived using SAE authentication
+ * can be used regardless of which PMKID(s) are indicated in the
+ * (Re)Association Request frame. */
+ if (!ap_sae_offload && data.num_pmkid && !sm->pmksa &&
+ sm->auth_alg == WLAN_AUTH_OPEN) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"No PMKSA cache entry found for SAE");
return WPA_INVALID_PMKID;
@@ -1086,7 +1471,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
sm->wpa_ie_len = wpa_ie_len;
- if (rsnxe && rsnxe_len) {
+ if (sm->wpa != WPA_VERSION_WPA && rsnxe && rsnxe_len) {
if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
os_free(sm->rsnxe);
sm->rsnxe = os_malloc(rsnxe_len);
@@ -1105,42 +1490,17 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
-#ifdef CONFIG_HS20
-int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- const u8 *osen_ie, size_t osen_ie_len)
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
{
- if (wpa_auth == NULL || sm == NULL)
- return -1;
-
- /* TODO: parse OSEN element */
- sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
- sm->mgmt_frame_prot = 1;
- sm->pairwise = WPA_CIPHER_CCMP;
- sm->wpa = WPA_VERSION_WPA2;
-
- if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
- os_free(sm->wpa_ie);
- sm->wpa_ie = os_malloc(osen_ie_len);
- if (sm->wpa_ie == NULL)
- return -1;
- }
-
- os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
- sm->wpa_ie_len = osen_ie_len;
-
- return 0;
+ return sm ? sm->mgmt_frame_prot : 0;
}
-#endif /* CONFIG_HS20 */
-
-int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm)
{
- return sm ? sm->mgmt_frame_prot : 0;
+ return sm ? sm->spp_amsdu : 0;
}
-
#ifdef CONFIG_OCV
void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
@@ -1160,8 +1520,7 @@ int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
#ifdef CONFIG_OWE
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
- u8 *pos, size_t max_len,
- const u8 *req_ies, size_t req_ies_len)
+ u8 *pos, size_t max_len)
{
int res;
struct wpa_auth_config *conf;
@@ -1194,8 +1553,7 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
#ifdef CONFIG_FILS
u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
- u8 *pos, size_t max_len,
- const u8 *req_ies, size_t req_ies_len)
+ u8 *pos, size_t max_len)
{
int res;
@@ -1229,7 +1587,7 @@ bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
return false;
/* RSN Capability (B0..B15) */
- WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf, conf->ieee80211w));
pos += 2;
/* Group Data Cipher Suite Selector (B16..B21) */
diff --git a/contrib/wpa/src/ap/wpa_auth_kay.c b/contrib/wpa/src/ap/wpa_auth_kay.c
index 625f40512750..20a5aaa10a56 100644
--- a/contrib/wpa/src/ap/wpa_auth_kay.c
+++ b/contrib/wpa/src/ap/wpa_auth_kay.c
@@ -331,6 +331,7 @@ int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
hapd->conf->macsec_port,
hapd->conf->mka_priority,
hapd->conf->macsec_csindex,
+ hapd->conf->macsec_icv_indicator,
hapd->conf->iface,
hapd->own_addr);
/* ieee802_1x_kay_init() frees kay_ctx on failure */
@@ -477,7 +478,7 @@ void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
cak->len = hapd->conf->mka_cak_len;
os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
- ckn->len = hapd->conf->mka_ckn_len;;
+ ckn->len = hapd->conf->mka_ckn_len;
os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true);
diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c
index 82d4d5fdd599..ceaf9a453832 100644
--- a/contrib/wpa/src/ap/wps_hostapd.c
+++ b/contrib/wpa/src/ap/wps_hostapd.c
@@ -20,6 +20,7 @@
#include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h"
#include "wps/wps_attr_parse.h"
+#include "crypto/dh_group5.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
@@ -1071,6 +1072,7 @@ static void hostapd_free_wps(struct wps_context *wps)
wps_device_data_free(&wps->dev);
bin_clear_free(wps->network_key, wps->network_key_len);
hostapd_wps_nfc_clear(wps);
+ dh5_free(wps->dh_ctx);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
forced_memzero(wps->psk, sizeof(wps->psk));
diff --git a/contrib/wpa/src/build.rules b/contrib/wpa/src/build.rules
index c756ccb84e8c..eb6759ac2068 100644
--- a/contrib/wpa/src/build.rules
+++ b/contrib/wpa/src/build.rules
@@ -83,13 +83,16 @@ _make_dirs:
@mkdir -p $(sort $(_DIRS))
$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
+ @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
@$(E) " CC " $<
$(BUILDDIR)/$(PROJ)/%.o: %.c $(CONFIG_FILE) | _make_dirs
+ @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
@$(E) " CC " $<
# for the fuzzing tests
$(BUILDDIR)/$(PROJ)/wpa_supplicant/%.o: $(ROOTDIR)wpa_supplicant/%.c $(CONFIG_FILE) | _make_dirs
+ @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
@$(E) " CC " $<
diff --git a/contrib/wpa/src/common/Makefile b/contrib/wpa/src/common/Makefile
index e2c5f03c2c3b..fef65df6dad8 100644
--- a/contrib/wpa/src/common/Makefile
+++ b/contrib/wpa/src/common/Makefile
@@ -1,3 +1,4 @@
+CFLAGS += -DCONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211R
CFLAGS += -DCONFIG_HS20
CFLAGS += -DCONFIG_SAE
diff --git a/contrib/wpa/src/common/common_module_tests.c b/contrib/wpa/src/common/common_module_tests.c
index a95ae36dc078..bb6b9cf972b7 100644
--- a/contrib/wpa/src/common/common_module_tests.c
+++ b/contrib/wpa/src/common/common_module_tests.c
@@ -416,7 +416,7 @@ static int sae_tests(void)
goto fail;
/* Check that output matches the test vector */
- if (sae_write_commit(&sae, buf, NULL, NULL) < 0)
+ if (sae_write_commit(&sae, buf, NULL, NULL, 0) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
@@ -449,7 +449,9 @@ static int sae_tests(void)
pt_info = sae_derive_pt(pt_groups,
(const u8 *) ssid, os_strlen(ssid),
- (const u8 *) pw, os_strlen(pw), pwid);
+ (const u8 *) pw, os_strlen(pw),
+ (const u8 *) pwid,
+ os_strlen(pwid));
if (!pt_info)
goto fail;
@@ -608,7 +610,8 @@ static int sae_pk_tests(void)
static int pasn_test_pasn_auth(void)
{
- /* Test vector taken from IEEE P802.11az/D2.6, J.12 */
+ /* Test vector taken from IEEE Std 802.11-2024,
+ * J.12 (PASN Test Vectors) */
const u8 pmk[] = {
0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6,
0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef,
@@ -646,12 +649,13 @@ static int pasn_test_pasn_auth(void)
};
struct wpa_ptk ptk;
int ret;
+ enum rsn_hash_alg hash_alg;
ret = pasn_pmk_to_ptk(pmk, sizeof(pmk),
spa_addr, bssid,
dhss, sizeof(dhss),
&ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP,
- WPA_KDK_MAX_LEN);
+ WPA_KDK_MAX_LEN, 0, &hash_alg, false);
if (ret)
return ret;
@@ -680,7 +684,8 @@ static int pasn_test_pasn_auth(void)
static int pasn_test_no_pasn_auth(void)
{
- /* Test vector taken from IEEE P802.11az/D2.6, J.13 */
+ /* Test vector taken from IEEE Std 802.11-2024,
+ * J.13 (KDK Test Vectors when PASN authentication is not used) */
const u8 pmk[] = {
0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6,
0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef,
diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h
index 8cca094e8f14..c88766e7512a 100644
--- a/contrib/wpa/src/common/defs.h
+++ b/contrib/wpa/src/common/defs.h
@@ -39,7 +39,6 @@
#define WPA_KEY_MGMT_WAPI_PSK BIT(12)
#define WPA_KEY_MGMT_WAPI_CERT BIT(13)
#define WPA_KEY_MGMT_CCKM BIT(14)
-#define WPA_KEY_MGMT_OSEN BIT(15)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
@@ -53,6 +52,7 @@
#define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26)
#define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27)
#define WPA_KEY_MGMT_IEEE8021X_SHA384 BIT(28)
+#define WPA_KEY_MGMT_EPPKE BIT(29)
#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
@@ -69,7 +69,6 @@ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
WPA_KEY_MGMT_FT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_CCKM |
- WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
@@ -129,6 +128,22 @@ static inline int wpa_key_mgmt_sae_ext_key(int akm)
WPA_KEY_MGMT_FT_SAE_EXT_KEY));
}
+static inline int wpa_key_mgmt_only_sae(int akm)
+{
+ return wpa_key_mgmt_sae(akm) &&
+ !(akm & ~(WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_SAE_EXT_KEY |
+ WPA_KEY_MGMT_FT_SAE |
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY));
+}
+
+static inline int wpa_key_mgmt_only_sae_ext_key(int akm)
+{
+ return wpa_key_mgmt_sae_ext_key(akm) &&
+ !(akm & ~(WPA_KEY_MGMT_SAE_EXT_KEY |
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY));
+}
+
static inline int wpa_key_mgmt_fils(int akm)
{
return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
@@ -144,7 +159,6 @@ static inline int wpa_key_mgmt_sha256(int akm)
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE |
- WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
WPA_KEY_MGMT_FILS_SHA256 |
WPA_KEY_MGMT_FT_FILS_SHA256));
@@ -172,7 +186,8 @@ static inline int wpa_key_mgmt_wpa(int akm)
wpa_key_mgmt_fils(akm) ||
wpa_key_mgmt_sae(akm) ||
akm == WPA_KEY_MGMT_OWE ||
- akm == WPA_KEY_MGMT_DPP;
+ akm == WPA_KEY_MGMT_DPP ||
+ akm == WPA_KEY_MGMT_EPPKE;
}
static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -193,10 +208,27 @@ static inline int wpa_key_mgmt_cross_akm(int akm)
WPA_KEY_MGMT_SAE_EXT_KEY));
}
+static inline bool wpa_key_mgmt_eppke(int akm)
+{
+ return !!(akm & WPA_KEY_MGMT_EPPKE);
+}
+
+static inline int wpa_key_mgmt_enhanced_open(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_OWE |
+ WPA_KEY_MGMT_EPPKE));
+}
+
+static inline int wpa_key_mgmt_only_enhanced_open(int akm)
+{
+ return wpa_key_mgmt_enhanced_open(akm) &&
+ !(akm & ~(WPA_KEY_MGMT_OWE |
+ WPA_KEY_MGMT_EPPKE));
+}
+
#define WPA_PROTO_WPA BIT(0)
#define WPA_PROTO_RSN BIT(1)
#define WPA_PROTO_WAPI BIT(2)
-#define WPA_PROTO_OSEN BIT(3)
#define WPA_AUTH_ALG_OPEN BIT(0)
#define WPA_AUTH_ALG_SHARED BIT(1)
@@ -205,12 +237,19 @@ static inline int wpa_key_mgmt_cross_akm(int akm)
#define WPA_AUTH_ALG_SAE BIT(4)
#define WPA_AUTH_ALG_FILS BIT(5)
#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
+#define WPA_AUTH_ALG_EPPKE BIT(7)
+#define WPA_AUTH_ALG_802_1X BIT(8)
static inline int wpa_auth_alg_fils(int alg)
{
return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
}
+static inline bool wpa_auth_alg_eppke(int alg)
+{
+ return !!(alg & WPA_AUTH_ALG_EPPKE);
+}
+
enum wpa_alg {
WPA_ALG_NONE,
WPA_ALG_WEP,
@@ -481,6 +520,7 @@ enum key_flag {
KEY_FLAG_GROUP = BIT(4),
KEY_FLAG_PAIRWISE = BIT(5),
KEY_FLAG_PMK = BIT(6),
+ KEY_FLAG_NEXT = BIT(7),
/* Used flag combinations */
KEY_FLAG_RX_TX = KEY_FLAG_RX | KEY_FLAG_TX,
KEY_FLAG_GROUP_RX_TX = KEY_FLAG_GROUP | KEY_FLAG_RX_TX,
@@ -493,8 +533,10 @@ enum key_flag {
KEY_FLAG_PAIRWISE_RX = KEY_FLAG_PAIRWISE | KEY_FLAG_RX,
KEY_FLAG_PAIRWISE_RX_TX_MODIFY = KEY_FLAG_PAIRWISE_RX_TX |
KEY_FLAG_MODIFY,
+ KEY_FLAG_PAIRWISE_NEXT = KEY_FLAG_PAIRWISE_RX | KEY_FLAG_NEXT,
/* Max allowed flags for each key type */
- KEY_FLAG_PAIRWISE_MASK = KEY_FLAG_PAIRWISE_RX_TX_MODIFY,
+ KEY_FLAG_PAIRWISE_MASK = KEY_FLAG_PAIRWISE_RX_TX_MODIFY |
+ KEY_FLAG_NEXT,
KEY_FLAG_GROUP_MASK = KEY_FLAG_GROUP_RX_TX_DEFAULT,
KEY_FLAG_PMK_MASK = KEY_FLAG_PMK,
};
@@ -532,4 +574,15 @@ enum sae_pwe {
SAE_PWE_NOT_SET = 4,
};
+enum wpa_p2p_mode {
+ WPA_P2P_MODE_WFD_R1 = 0,
+ WPA_P2P_MODE_WFD_R2 = 1,
+ WPA_P2P_MODE_WFD_PCC = 2,
+};
+
+#define USEC_80211_TU 1024
+
+#define USEC_TO_TU(m) ((m) / USEC_80211_TU)
+#define TU_TO_USEC(m) ((m) * USEC_80211_TU)
+
#endif /* DEFS_H */
diff --git a/contrib/wpa/src/common/dpp.c b/contrib/wpa/src/common/dpp.c
index 3b9f35e8dfeb..7601b0d72049 100644
--- a/contrib/wpa/src/common/dpp.c
+++ b/contrib/wpa/src/common/dpp.c
@@ -2,7 +2,7 @@
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -299,7 +299,8 @@ int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
}
-int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
+static int dpp_parse_uri_version(struct dpp_bootstrap_info *bi,
+ const char *version)
{
#ifdef CONFIG_DPP2
if (!version || DPP_VERSION < 2)
@@ -1035,6 +1036,10 @@ struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
json_value_sep(json);
json_add_string(json, "pkcs10", csr);
}
+#ifdef CONFIG_DPP3
+ json_value_sep(json);
+ json_add_int(json, "capabilities", DPP_ENROLLEE_CAPAB_SAE_PW_ID);
+#endif /* CONFIG_DPP3 */
if (extra_name && extra_value && extra_name[0] && extra_value[0]) {
json_value_sep(json);
wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value);
@@ -1139,8 +1144,18 @@ int dpp_configuration_valid(const struct dpp_configuration *conf)
return 0;
if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
return 0;
+ if (dpp_akm_psk(conf->akm) && conf->passphrase) {
+ size_t len = os_strlen(conf->passphrase);
+
+ if (len > 63 || len < 8)
+ return 0;
+ }
if (dpp_akm_sae(conf->akm) && !conf->passphrase)
return 0;
+#ifdef CONFIG_DPP3
+ if (conf->idpass && (!conf->passphrase || !dpp_akm_sae(conf->akm)))
+ return 0;
+#endif /* CONFIG_DPP3 */
return 1;
}
@@ -1150,6 +1165,9 @@ void dpp_configuration_free(struct dpp_configuration *conf)
if (!conf)
return;
str_clear_free(conf->passphrase);
+#ifdef CONFIG_DPP3
+ os_free(conf->idpass);
+#endif /* CONFIG_DPP3 */
os_free(conf->group_id);
os_free(conf->csrattrs);
os_free(conf->extra_name);
@@ -1228,14 +1246,28 @@ static int dpp_configuration_parse_helper(struct dpp_authentication *auth,
end = os_strchr(pos, ' ');
pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
pass_len /= 2;
- if (pass_len > 63 || pass_len < 8)
- goto fail;
conf->passphrase = os_zalloc(pass_len + 1);
if (!conf->passphrase ||
hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
goto fail;
}
+#ifdef CONFIG_DPP3
+ pos = os_strstr(cmd, " idpass=");
+ if (pos) {
+ size_t idpass_len;
+
+ pos += 8;
+ end = os_strchr(pos, ' ');
+ idpass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ idpass_len /= 2;
+ conf->idpass = os_zalloc(idpass_len + 1);
+ if (!conf->idpass ||
+ hexstr2bin(pos, (u8 *) conf->idpass, idpass_len) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_DPP3 */
+
pos = os_strstr(cmd, " psk=");
if (pos) {
pos += 5;
@@ -1595,6 +1627,13 @@ static void dpp_build_legacy_cred_params(struct wpabuf *buf,
if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
json_add_string_escape(buf, "pass", conf->passphrase,
os_strlen(conf->passphrase));
+#ifdef CONFIG_DPP3
+ if (conf->idpass) {
+ json_value_sep(buf);
+ json_add_string_escape(buf, "idpass", conf->idpass,
+ os_strlen(conf->idpass));
+ }
+#endif /* CONFIG_DPP3 */
} else if (conf->psk_set) {
char psk[2 * sizeof(conf->psk) + 1];
@@ -1917,6 +1956,16 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth,
const char *akm_str;
size_t len = 1000;
+
+#ifdef CONFIG_DPP3
+ if (conf->idpass &&
+ !(auth->enrollee_capabilities & DPP_ENROLLEE_CAPAB_SAE_PW_ID)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Enrollee does not support SAE Password Identifier - cannot generate config object");
+ return NULL;
+ }
+#endif /* CONFIG_DPP3 */
+
if (conf->extra_name && conf->extra_value)
len += 10 + os_strlen(conf->extra_name) +
os_strlen(conf->extra_value);
@@ -2535,6 +2584,18 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
cont:
#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+ token = json_get_member(root, "capabilities");
+ if (token && token->type == JSON_NUMBER) {
+ wpa_printf(MSG_DEBUG, "DPP: capabilities = 0x%x",
+ token->number);
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_ENROLLEE_CAPABILITY "%d",
+ token->number);
+ auth->enrollee_capabilities = token->number;
+ }
+#endif /* CONFIG_DPP3 */
+
resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole,
cert_req);
@@ -2558,13 +2619,25 @@ static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
if (pass && pass->type == JSON_STRING) {
size_t len = os_strlen(pass->string);
+#ifdef CONFIG_DPP3
+ struct json_token *saepi = json_get_member(cred, "idpass");
+#endif /* CONFIG_DPP3 */
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
pass->string, len);
- if (len < 8 || len > 63)
+ if (dpp_akm_psk(conf->akm) && (len < 8 || len > 63)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected pass length %zu for a config object that includes PSK",
+ len);
return -1;
+ }
os_strlcpy(conf->passphrase, pass->string,
sizeof(conf->passphrase));
+#ifdef CONFIG_DPP3
+ if (saepi && saepi->type == JSON_STRING)
+ os_strlcpy(conf->password_id, saepi->string,
+ sizeof(conf->password_id));
+#endif /* CONFIG_DPP3 */
} else if (psk_hex && psk_hex->type == JSON_STRING) {
if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) {
wpa_printf(MSG_DEBUG,
diff --git a/contrib/wpa/src/common/dpp.h b/contrib/wpa/src/common/dpp.h
index 0f843da6a394..6aef72165e92 100644
--- a/contrib/wpa/src/common/dpp.h
+++ b/contrib/wpa/src/common/dpp.h
@@ -2,7 +2,7 @@
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -134,6 +134,9 @@ enum dpp_connector_key {
#define DPP_MAX_SHARED_SECRET_LEN 66
#define DPP_CP_LEN 64
+/* DPP Configuration Request - Enrollee Capabilities */
+#define DPP_ENROLLEE_CAPAB_SAE_PW_ID BIT(0)
+
struct dpp_curve_params {
const char *name;
size_t hash_len;
@@ -260,6 +263,7 @@ struct dpp_configuration {
/* For legacy configuration */
char *passphrase;
+ char *idpass;
u8 psk[32];
int psk_set;
@@ -356,6 +360,9 @@ struct dpp_authentication {
u8 ssid_len;
int ssid_charset;
char passphrase[64];
+#ifdef CONFIG_DPP3
+ char password_id[64];
+#endif /* CONFIG_DPP3 */
u8 psk[PMK_LEN];
int psk_set;
enum dpp_akm akm;
@@ -393,6 +400,7 @@ struct dpp_authentication {
char *e_name;
char *e_mud_url;
int *e_band_support;
+ unsigned int enrollee_capabilities;
#ifdef CONFIG_TESTING_OPTIONS
char *config_obj_override;
char *discovery_override;
diff --git a/contrib/wpa/src/common/dpp_auth.c b/contrib/wpa/src/common/dpp_auth.c
index f81f1eecbc7e..a6caab97a99b 100644
--- a/contrib/wpa/src/common/dpp_auth.c
+++ b/contrib/wpa/src/common/dpp_auth.c
@@ -1869,7 +1869,7 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
SHA256_MAC_LEN) != 0) {
wpa_hexdump(MSG_DEBUG,
"DPP: Expected Responder Bootstrapping Key Hash",
- auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
dpp_auth_fail(auth,
"Responder Bootstrapping Key Hash mismatch");
return -1;
diff --git a/contrib/wpa/src/common/dpp_backup.c b/contrib/wpa/src/common/dpp_backup.c
index fb3f776d2ea2..25c0bd548c52 100644
--- a/contrib/wpa/src/common/dpp_backup.c
+++ b/contrib/wpa/src/common/dpp_backup.c
@@ -161,7 +161,7 @@ static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
attr = dpp_build_attribute(auth->conf);
attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
- if (!priv_key || !attr || !alg)
+ if (!attr || !alg)
goto fail;
/*
@@ -515,6 +515,7 @@ out:
wpabuf_free(enc_cont_info);
return env;
fail:
+ wpa_printf(MSG_INFO, "DPP: Failed to build DPPEnvelopedData");
wpabuf_free(env);
env = NULL;
goto out;
diff --git a/contrib/wpa/src/common/dpp_crypto.c b/contrib/wpa/src/common/dpp_crypto.c
index f17f95a2ca34..4b5f24378e7b 100644
--- a/contrib/wpa/src/common/dpp_crypto.c
+++ b/contrib/wpa/src/common/dpp_crypto.c
@@ -2371,6 +2371,7 @@ struct crypto_ec_point * dpp_decrypt_e_id(struct crypto_ec_key *ppkey,
crypto_ec_point_invert(ec, e_id) ||
crypto_ec_point_add(ec, e_id, e_prime_id_point, e_id)) {
crypto_ec_point_deinit(e_id, 1);
+ e_id = NULL;
goto fail;
}
diff --git a/contrib/wpa/src/common/dpp_i.h b/contrib/wpa/src/common/dpp_i.h
index dfa4a3cb866f..6f7e8b73cf3b 100644
--- a/contrib/wpa/src/common/dpp_i.h
+++ b/contrib/wpa/src/common/dpp_i.h
@@ -2,7 +2,7 @@
* DPP module internal definitions
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
diff --git a/contrib/wpa/src/common/dpp_reconfig.c b/contrib/wpa/src/common/dpp_reconfig.c
index 452c50247512..e55789c87e44 100644
--- a/contrib/wpa/src/common/dpp_reconfig.c
+++ b/contrib/wpa/src/common/dpp_reconfig.c
@@ -569,7 +569,7 @@ fail:
}
-struct wpabuf *
+static struct wpabuf *
dpp_reconfig_build_conf(struct dpp_authentication *auth)
{
struct wpabuf *msg = NULL, *clear;
diff --git a/contrib/wpa/src/common/dpp_tcp.c b/contrib/wpa/src/common/dpp_tcp.c
index 2fad3e1d7620..5d5a161fd500 100644
--- a/contrib/wpa/src/common/dpp_tcp.c
+++ b/contrib/wpa/src/common/dpp_tcp.c
@@ -1,7 +1,7 @@
/*
* DPP over TCP
* Copyright (c) 2019-2020, The Linux Foundation
- * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -2291,7 +2291,7 @@ void dpp_controller_new_qr_code(struct dpp_global *dpp,
dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
struct dpp_authentication *auth = conn->auth;
- if (!auth->response_pending ||
+ if (!auth || !auth->response_pending ||
dpp_notify_new_qr_code(auth, bi) != 1)
continue;
wpa_printf(MSG_DEBUG,
diff --git a/contrib/wpa/src/common/gas.c b/contrib/wpa/src/common/gas.c
index ba21b225efc9..6a18c455f408 100644
--- a/contrib/wpa/src/common/gas.c
+++ b/contrib/wpa/src/common/gas.c
@@ -79,6 +79,12 @@ struct wpabuf *
gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
u16 comeback_delay, size_t size)
{
+ if (frag_id > 127) {
+ wpa_printf(MSG_INFO,
+ "GAS: Invalid frag_id=%u - cannot build comeback response",
+ frag_id);
+ return NULL;
+ }
return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
status_code, frag_id, more, comeback_delay,
size);
diff --git a/contrib/wpa/src/common/gas_server.c b/contrib/wpa/src/common/gas_server.c
index 107550039fe6..cdf77364348a 100644
--- a/contrib/wpa/src/common/gas_server.c
+++ b/contrib/wpa/src/common/gas_server.c
@@ -2,7 +2,7 @@
* Generic advertisement service (GAS) server
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2020, The Linux Foundation
- * Copyright (c) 2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -44,6 +44,7 @@ struct gas_server_response {
struct gas_server_handler *handler;
u16 comeback_delay;
bool initial_resp_sent;
+ bool timeout_extended;
};
struct gas_server {
@@ -61,6 +62,17 @@ static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx)
{
struct gas_server_response *response = eloop_ctx;
+ if (!response->resp && response->comeback_delay &&
+ !response->timeout_extended) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Extend timeout while waiting for response");
+ response->timeout_extended = true;
+ eloop_register_timeout(GAS_QUERY_TIMEOUT, 0,
+ gas_server_response_timeout,
+ response, user_ctx);
+ return;
+ }
+
wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR
" (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data",
response, MAC2STR(response->dst), response->dialog_token,
diff --git a/contrib/wpa/src/common/gas_server.h b/contrib/wpa/src/common/gas_server.h
index 8d5eaa256bac..6036877a38c7 100644
--- a/contrib/wpa/src/common/gas_server.h
+++ b/contrib/wpa/src/common/gas_server.h
@@ -2,7 +2,7 @@
* Generic advertisement service (GAS) server
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2020, The Linux Foundation
- * Copyright (c) 2022, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c
index 2c47bf812483..e928738ee568 100644
--- a/contrib/wpa/src/common/hw_features_common.c
+++ b/contrib/wpa/src/common/hw_features_common.c
@@ -387,6 +387,7 @@ static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
switch (bitmap) {
case 0x6:
+ case 0x9:
*seg0 = 0;
return;
case 0x8:
@@ -432,6 +433,27 @@ static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
}
+static void punct_update_legacy_bw_320(u16 bitmap, u8 pri,
+ enum oper_chan_width *width, u8 *seg0)
+{
+ if (pri < *seg0) {
+ *seg0 -= 16;
+ if (bitmap & 0x00FF) {
+ *width = 1;
+ punct_update_legacy_bw_160(bitmap & 0xFF, pri, width,
+ seg0);
+ }
+ } else {
+ *seg0 += 16;
+ if (bitmap & 0xFF00) {
+ *width = 1;
+ punct_update_legacy_bw_160((bitmap & 0xFF00) >> 8,
+ pri, width, seg0);
+ }
+ }
+}
+
+
void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
u8 *seg0, u8 *seg1)
{
@@ -446,7 +468,10 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
}
- /* TODO: 320 MHz */
+ if (*width == CONF_OPER_CHWIDTH_320MHZ && (bitmap & 0xFFFF)) {
+ *width = CONF_OPER_CHWIDTH_160MHZ;
+ punct_update_legacy_bw_320(bitmap & 0xFFFF, pri, width, seg0);
+ }
}
@@ -481,6 +506,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
data->sec_channel_offset = sec_channel_offset;
data->center_freq1 = freq + sec_channel_offset * 10;
data->center_freq2 = 0;
+ data->punct_bitmap = punct_bitmap;
if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
data->bandwidth = 80;
else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
@@ -569,7 +595,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
if (data->eht_enabled) switch (oper_chwidth) {
case CONF_OPER_CHWIDTH_320MHZ:
- if (!(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ if (eht_cap &&
+ !(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
wpa_printf(MSG_ERROR,
"320 MHz channel width is not supported in 5 or 6 GHz");
@@ -968,7 +995,7 @@ int chan_pri_allowed(const struct hostapd_channel_data *chan)
}
-/* IEEE P802.11be/D3.0, Table 36-30 - Definition of the Punctured Channel
+/* IEEE Std 802.11be-2024, Table 36-30 - Definition of the Punctured Channel
* Information field in the U-SIG for an EHT MU PPDU using non-OFDMA
* transmissions */
static const u16 punct_bitmap_80[] = { 0xF, 0xE, 0xD, 0xB, 0x7 };
@@ -1033,3 +1060,18 @@ bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap)
return false;
}
+
+
+bool chan_in_current_hw_info(struct hostapd_multi_hw_info *current_hw_info,
+ struct hostapd_channel_data *chan)
+{
+ /* Assuming that if current_hw_info is not set full
+ * iface->current_mode->channels[] can be used to scan for channels,
+ * hence we return true.
+ */
+ if (!current_hw_info)
+ return true;
+
+ return current_hw_info->start_freq <= chan->freq &&
+ current_hw_info->end_freq >= chan->freq;
+}
diff --git a/contrib/wpa/src/common/hw_features_common.h b/contrib/wpa/src/common/hw_features_common.h
index e791c33f1736..80e33adf2d5c 100644
--- a/contrib/wpa/src/common/hw_features_common.h
+++ b/contrib/wpa/src/common/hw_features_common.h
@@ -58,5 +58,7 @@ int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
int ht40_plus, int pri);
int chan_pri_allowed(const struct hostapd_channel_data *chan);
bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap);
+bool chan_in_current_hw_info(struct hostapd_multi_hw_info *current_hw_info,
+ struct hostapd_channel_data *chan);
#endif /* HW_FEATURES_COMMON_H */
diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c
index ba1cb5257b65..8ad201ef763a 100644
--- a/contrib/wpa/src/common/ieee802_11_common.c
+++ b/contrib/wpa/src/common/ieee802_11_common.c
@@ -111,11 +111,6 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->hs20 = pos;
elems->hs20_len = elen;
break;
- case HS20_OSEN_OUI_TYPE:
- /* Hotspot 2.0 OSEN */
- elems->osen = pos;
- elems->osen_len = elen;
- break;
case MBO_OUI_TYPE:
/* MBO-OCE */
elems->mbo = pos;
@@ -140,6 +135,46 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->sae_pk = pos + 4;
elems->sae_pk_len = elen - 4;
break;
+ case WFA_CAPA_OUI_TYPE:
+ elems->wfa_capab = pos + 4;
+ elems->wfa_capab_len = elen - 4;
+ break;
+ case WFA_RSNE_OVERRIDE_OUI_TYPE:
+ elems->rsne_override = pos;
+ elems->rsne_override_len = elen;
+ break;
+ case WFA_RSNE_OVERRIDE_2_OUI_TYPE:
+ elems->rsne_override_2 = pos;
+ elems->rsne_override_2_len = elen;
+ break;
+ case WFA_RSNXE_OVERRIDE_OUI_TYPE:
+ elems->rsnxe_override = pos;
+ elems->rsnxe_override_len = elen;
+ break;
+ case WFA_RSN_SELECTION_OUI_TYPE:
+ if (elen < 4 + 1) {
+ wpa_printf(MSG_DEBUG,
+ "Too short RSN Selection element ignored");
+ return -1;
+ }
+ elems->rsn_selection = pos + 4;
+ elems->rsn_selection_len = elen - 4;
+ break;
+ case P2P2_OUI_TYPE:
+ /* Wi-Fi Alliance - P2P2 IE */
+ elems->p2p2_ie = pos;
+ elems->p2p2_ie_len = elen;
+ break;
+ case PR_OUI_TYPE:
+ /* Wi-Fi Alliance - Proximity Ranging element */
+ elems->proximity_ranging = pos;
+ elems->proximity_ranging_len = elen;
+ break;
+ case NAN_SDF_OUI_TYPE:
+ /* Wi-Fi Alliance - NAN IE */
+ elems->nan_ie = pos;
+ elems->nan_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -320,6 +355,7 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
break;
elems->key_delivery = pos;
elems->key_delivery_len = elen;
+ total_len = &elems->key_delivery_len;
break;
case WLAN_EID_EXT_WRAPPED_DATA:
elems->wrapped_data = pos;
@@ -332,10 +368,11 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->fils_pk = pos;
elems->fils_pk_len = elen;
break;
- case WLAN_EID_EXT_FILS_NONCE:
- if (elen != FILS_NONCE_LEN)
+ case WLAN_EID_EXT_NONCE:
+ if (elen < NONCE_LEN)
break;
- elems->fils_nonce = pos;
+ elems->nonce = pos;
+ elems->nonce_len = elen;
break;
case WLAN_EID_EXT_OWE_DH_PARAM:
if (elen < 2)
@@ -348,10 +385,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->password_id_len = elen;
break;
case WLAN_EID_EXT_HE_CAPABILITIES:
+ if (elen < HE_CAPABILITIES_ELEM_MIN_LEN)
+ break;
elems->he_capabilities = pos;
elems->he_capabilities_len = elen;
break;
case WLAN_EID_EXT_HE_OPERATION:
+ if (elen < HE_OPERATION_ELEM_MIN_LEN)
+ break;
elems->he_operation = pos;
elems->he_operation_len = elen;
break;
@@ -373,10 +414,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->pasn_params_len = elen;
break;
case WLAN_EID_EXT_EHT_CAPABILITIES:
+ if (elen < EHT_CAPABILITIES_ELEM_MIN_LEN)
+ break;
elems->eht_capabilities = pos;
elems->eht_capabilities_len = elen;
break;
case WLAN_EID_EXT_EHT_OPERATION:
+ if (elen < EHT_OPERATION_ELEM_MIN_LEN)
+ break;
elems->eht_operation = pos;
elems->eht_operation_len = elen;
break;
@@ -391,6 +436,22 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->mbssid_known_bss = pos;
elems->mbssid_known_bss_len = elen;
break;
+ case WLAN_EID_EXT_PASN_ENCRYPTED_DATA:
+ elems->pasn_encrypted_data = pos;
+ elems->pasn_encrypted_data_len = elen;
+ break;
+ case WLAN_EID_EXT_AKM_SUITE_SELECTOR:
+ if (elen < RSN_SELECTOR_LEN)
+ break;
+ elems->akm_suite_selector = pos;
+ elems->akm_suite_selector_len = elen;
+ break;
+ case WLAN_EID_EXT_SUPPORTED_GROUPS:
+ if (elen < 2 || elen % 2 != 0)
+ break;
+ elems->supported_groups = pos;
+ elems->supported_groups_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -585,8 +646,12 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
case WLAN_EID_MIC:
elems->mic = pos;
elems->mic_len = elen;
- /* after mic everything is encrypted, so stop. */
- goto done;
+ if (elems->stop_at_mic) {
+ /* After MIC everything is encrypted, so stop.
+ */
+ goto done;
+ }
+ break;
case WLAN_EID_MULTI_BAND:
if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
wpa_printf(MSG_MSGDUMP,
@@ -699,6 +764,13 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
}
+ParseRes ieee802_11_parse_elems_ctrl(const u8 *start, size_t len,
+ struct ieee802_11_elems *elems)
+{
+ return __ieee802_11_parse_elems(start, len, elems, elems->show_errors);
+}
+
+
/**
* ieee802_11_elems_clear_ids - Clear the data for the given element IDs
* @ids: Array of element IDs for which data should be cleared.
@@ -899,8 +971,8 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
elems->fils_pk = NULL;
elems->fils_pk_len = 0;
break;
- case WLAN_EID_EXT_FILS_NONCE:
- elems->fils_nonce = NULL;
+ case WLAN_EID_EXT_NONCE:
+ elems->nonce = NULL;
break;
case WLAN_EID_EXT_OWE_DH_PARAM:
elems->owe_dh = NULL;
@@ -959,14 +1031,15 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
}
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
- struct ieee802_11_elems *elems,
- struct wpabuf *mlbuf,
- u8 link_id, bool show_errors)
+static ParseRes ieee802_11_parse_link_profile(struct ieee802_11_elems *elems,
+ struct wpabuf *mlbuf,
+ u8 link_id, bool show_errors,
+ bool is_assoc_resp)
{
const struct ieee80211_eht_ml *ml;
const u8 *pos;
ParseRes res = ParseFailed;
+ size_t len;
pos = wpabuf_head(mlbuf);
len = wpabuf_len(mlbuf);
@@ -987,14 +1060,26 @@ ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
pos += sizeof(*ml) + pos[sizeof(*ml)];
while (len > 2) {
- size_t sub_elem_len = *(pos + 1);
- size_t sta_info_len;
+ size_t sub_elem_len, sta_info_len;
u16 link_info_control;
const u8 *non_inherit;
+ int num_frag_subelems;
+
+ num_frag_subelems =
+ ieee802_11_defrag_mle_subelem(mlbuf, pos,
+ &sub_elem_len);
+ if (num_frag_subelems < 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed to parse MLE subelem");
+ goto out;
+ }
+ if ((size_t) num_frag_subelems * 2 > len)
+ goto out;
+ len -= num_frag_subelems * 2;
wpa_printf(MSG_DEBUG,
- "MLD: sub element: len=%zu, sub_elem_len=%zu",
- len, sub_elem_len);
+ "MLD: sub element: len=%zu, sub_elem_len=%zu, Fragment subelems=%u",
+ len, sub_elem_len, num_frag_subelems);
if (2 + sub_elem_len > len) {
if (show_errors)
@@ -1048,6 +1133,28 @@ ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
pos += 2;
sub_elem_len -= 2;
+ /* For association response, check status code */
+ if (is_assoc_resp) {
+ u16 status_code;
+
+ if (sub_elem_len < 2) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: missing status code");
+ goto out;
+ }
+
+ status_code = WPA_GET_LE16(pos);
+ if (status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: status code %u", status_code);
+ goto out;
+ }
+
+ pos += 2;
+ sub_elem_len -= 2;
+ }
+
/* Handle non-inheritance */
non_inherit = get_ie_ext(pos, sub_elem_len,
WLAN_EID_EXT_NON_INHERITANCE);
@@ -1107,6 +1214,26 @@ out:
}
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
+ struct wpabuf *mlbuf,
+ u8 link_id, bool show_errors)
+{
+ return ieee802_11_parse_link_profile(elems, mlbuf, link_id,
+ show_errors, false);
+}
+
+
+ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems,
+ struct wpabuf *mlbuf,
+ u8 link_id, bool show_errors)
+{
+ /* ieee802_11_defrag_mle_subelem() handles subelement defragmentation
+ * in-place within mlbuf */
+ return ieee802_11_parse_link_profile(elems, mlbuf, link_id,
+ show_errors, true);
+}
+
+
int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
{
const struct element *elem;
@@ -1524,7 +1651,11 @@ ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
*channel = (freq - 5000) / 5;
- *op_class = 0; /* TODO */
+ if (vht_opclass)
+ *op_class = vht_opclass;
+ else
+ *op_class = 0;
+
return HOSTAPD_MODE_IEEE80211A;
}
@@ -2249,13 +2380,13 @@ const char * status2str(u16 status)
S2S(REQUEST_DECLINED)
S2S(INVALID_PARAMETERS)
S2S(REJECTED_WITH_SUGGESTED_CHANGES)
- S2S(INVALID_IE)
- S2S(GROUP_CIPHER_NOT_VALID)
- S2S(PAIRWISE_CIPHER_NOT_VALID)
- S2S(AKMP_NOT_VALID)
- S2S(UNSUPPORTED_RSN_IE_VERSION)
- S2S(INVALID_RSN_IE_CAPAB)
- S2S(CIPHER_REJECTED_PER_POLICY)
+ S2S(INVALID_ELEMENT)
+ S2S(INVALID_GROUP_CIPHER)
+ S2S(INVALID_PAIRWISE_CIPHER)
+ S2S(INVALID_AKMP)
+ S2S(UNSUPPORTED_RSNE_VERSION)
+ S2S(INVALID_RSNE_CAPABILITIES)
+ S2S(CIPHER_OUT_OF_POLICY)
S2S(TS_NOT_CREATED)
S2S(DIRECT_LINK_NOT_ALLOWED)
S2S(DEST_STA_NOT_PRESENT)
@@ -2263,8 +2394,8 @@ const char * status2str(u16 status)
S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE)
S2S(INVALID_FT_ACTION_FRAME_COUNT)
S2S(INVALID_PMKID)
- S2S(INVALID_MDIE)
- S2S(INVALID_FTIE)
+ S2S(INVALID_MDE)
+ S2S(INVALID_FTE)
S2S(REQUESTED_TCLAS_NOT_SUPPORTED)
S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES)
S2S(TRY_ANOTHER_BSS)
@@ -2277,7 +2408,7 @@ const char * status2str(u16 status)
S2S(ADV_SRV_UNREACHABLE)
S2S(REQ_REFUSED_SSPN)
S2S(REQ_REFUSED_UNAUTH_ACCESS)
- S2S(INVALID_RSNIE)
+ S2S(INVALID_RSNE)
S2S(U_APSD_COEX_NOT_SUPPORTED)
S2S(U_APSD_COEX_MODE_NOT_SUPPORTED)
S2S(BAD_INTERVAL_WITH_U_APSD_COEX)
@@ -2435,7 +2566,7 @@ const struct oper_class_map global_op_class[] = {
{ HOSTAPD_MODE_IEEE80211A, 135, 1, 233, 16, BW80P80, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 136, 2, 2, 4, BW20, NO_P2P_SUPP },
- /* IEEE P802.11be/D5.0, Table E-4 (Global operating classes) */
+ /* IEEE Std 802.11be-2024, Table E-4 (Global operating classes) */
{ HOSTAPD_MODE_IEEE80211A, 137, 31, 191, 32, BW320, NO_P2P_SUPP },
/*
@@ -2475,8 +2606,10 @@ static enum phy_type ieee80211_phy_type_by_freq(int freq)
/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
-enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
+enum phy_type ieee80211_get_phy_type(int freq, bool ht, bool vht, bool he)
{
+ if (he)
+ return PHY_TYPE_HE;
if (vht)
return PHY_TYPE_VHT;
if (ht)
@@ -2541,6 +2674,9 @@ const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
{
const struct element *elem;
+ if (!ies)
+ return NULL;
+
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
@@ -2600,14 +2736,14 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
} else {
wpa_printf(MSG_DEBUG,
"Multi-AP invalid Multi-AP subelement");
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
break;
case MULTI_AP_PROFILE_SUB_ELEM_TYPE:
if (elen < 1) {
wpa_printf(MSG_DEBUG,
"Multi-AP IE invalid Multi-AP profile subelement");
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
multi_ap->profile = *pos;
@@ -2622,12 +2758,12 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
if (multi_ap->profile < MULTI_AP_PROFILE_2) {
wpa_printf(MSG_DEBUG,
"Multi-AP IE invalid profile to read VLAN IE");
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
if (elen < 2) {
wpa_printf(MSG_DEBUG,
"Multi-AP IE invalid Multi-AP VLAN subelement");
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
vlan_id = WPA_GET_LE16(pos);
@@ -2635,7 +2771,7 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
wpa_printf(MSG_INFO,
"Multi-AP IE invalid Multi-AP VLAN ID %d",
vlan_id);
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
multi_ap->vlanid = vlan_id;
break;
@@ -2657,7 +2793,7 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
if (!ext_present) {
wpa_printf(MSG_DEBUG,
"Multi-AP element without Multi-AP Extension subelement");
- return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_INVALID_ELEMENT;
}
return WLAN_STATUS_SUCCESS;
@@ -2884,6 +3020,18 @@ int oper_class_bw_to_int(const struct oper_class_map *map)
}
+bool is_24ghz_freq(int freq)
+{
+ return freq >= 2400 && freq <= 2484;
+}
+
+
+bool is_5ghz_freq(int freq)
+{
+ return freq >= 5150 && freq <= 5885;
+}
+
+
int center_idx_to_bw_6ghz(u8 idx)
{
/* Channel: 2 */
@@ -3104,27 +3252,38 @@ bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len,
{
const u8 *end;
size_t flen, i;
- u32 capabs = 0;
+ u64 capabs = 0;
if (!rsnxe || rsnxe_len == 0)
return false;
+
+ if (capab > 63) {
+ wpa_printf(MSG_INFO, "%s: Unsupported capab=%u",
+ __func__, capab);
+ return false;
+ }
+
end = rsnxe + rsnxe_len;
flen = (rsnxe[0] & 0x0f) + 1;
if (rsnxe + flen > end)
return false;
- if (flen > 4)
- flen = 4;
+ if (flen > 8)
+ flen = 8;
for (i = 0; i < flen; i++)
- capabs |= rsnxe[i] << (8 * i);
+ capabs |= (u64) rsnxe[i] << (8 * i);
- return !!(capabs & BIT(capab));
+ return !!(capabs & BIT_ULL(capab));
}
bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab)
{
- return ieee802_11_rsnx_capab_len(rsnxe ? rsnxe + 2 : NULL,
- rsnxe ? rsnxe[1] : 0, capab);
+ if (!rsnxe)
+ return false;
+ if (rsnxe[0] == WLAN_EID_VENDOR_SPECIFIC && rsnxe[1] >= 4 + 1)
+ return ieee802_11_rsnx_capab_len(rsnxe + 2 + 4, rsnxe[1] - 4,
+ capab);
+ return ieee802_11_rsnx_capab_len(rsnxe + 2, rsnxe[1], capab);
}
@@ -3359,10 +3518,31 @@ int chwidth_freq2_to_ch_width(int chwidth, int freq2)
}
+enum oper_chan_width chan_width_to_oper_chwidth(enum chan_width chan_width)
+{
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ case CHAN_WIDTH_40:
+ return CONF_OPER_CHWIDTH_USE_HT;
+ case CHAN_WIDTH_80:
+ return CONF_OPER_CHWIDTH_80MHZ;
+ case CHAN_WIDTH_80P80:
+ return CONF_OPER_CHWIDTH_80P80MHZ;
+ case CHAN_WIDTH_160:
+ return CONF_OPER_CHWIDTH_160MHZ;
+ case CHAN_WIDTH_320:
+ return CONF_OPER_CHWIDTH_320MHZ;
+ default:
+ return CONF_OPER_CHWIDTH_USE_HT;
+ }
+}
+
+
struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
{
struct wpabuf *buf;
- const u8 *pos, *end = data + len;
+ const u8 *pos, *end;
size_t min_defrag_len = ext_elem ? 255 : 256;
if (!data || !len)
@@ -3376,6 +3556,7 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
return NULL;
pos = &data[min_defrag_len - 1];
+ end = data + len;
len -= min_defrag_len - 1;
while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) {
int ret;
@@ -3399,6 +3580,72 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
}
+/**
+ * ieee802_11_defrag_mle_subelem - Defragment Multi-Link element subelements
+ * @mlbuf: Defragmented mlbuf (defragmented using ieee802_11_defrag())
+ * @parent_subelem: Pointer to the subelement which may be fragmented
+ * @defrag_len: Defragmented length of the subelement
+ * Returns: Number of Fragment subelements parsed on success, -1 otherwise
+ *
+ * This function defragments a subelement present inside an Multi-Link element.
+ * It should be called individually for each subelement.
+ *
+ * Subelements can use the Fragment subelement if they pack more than 255 bytes
+ * of data, see IEEE Std 802.11be-2024, Figure 35-4 - Per-STA Profile subelement
+ * fragmentation within a fragmented Multi-Link element.
+ */
+ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
+ const u8 *parent_subelem,
+ size_t *defrag_len)
+{
+ u8 *buf, *pos, *end;
+ size_t len, subelem_len;
+ const size_t min_defrag_len = 255;
+ int num_frag_subelems = 0;
+
+ if (!mlbuf || !parent_subelem)
+ return -1;
+
+ buf = wpabuf_mhead_u8(mlbuf);
+ len = wpabuf_len(mlbuf);
+ end = buf + len;
+
+ *defrag_len = parent_subelem[1];
+ if (parent_subelem[1] < min_defrag_len)
+ return 0;
+
+ pos = (u8 *) parent_subelem;
+ if (2 + parent_subelem[1] > end - pos)
+ return -1;
+ pos += 2 + parent_subelem[1];
+ subelem_len = parent_subelem[1];
+
+ while (end - pos > 2 &&
+ pos[0] == MULTI_LINK_SUB_ELEM_ID_FRAGMENT && pos[1]) {
+ size_t elen = 2 + pos[1];
+
+ /* This Multi-Link parent subelement has more data and is
+ * fragmented. */
+ num_frag_subelems++;
+
+ if (elen > (size_t) (end - pos))
+ return -1;
+
+ os_memmove(pos, pos + 2, end - (pos + 2));
+ end -= 2;
+ mlbuf->used -= 2;
+ pos += elen - 2;
+ subelem_len += elen - 2;
+
+ /* Deduct Fragment subelement header */
+ len -= 2;
+ }
+
+ *defrag_len = subelem_len;
+ return num_frag_subelems;
+}
+
+
const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type)
{
const struct element *elem;
@@ -3433,3 +3680,637 @@ const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len)
return &buf[mld_addr_pos];
}
+
+
+const u8 * get_basic_mle_eml_capa(const u8 *buf, size_t len)
+{
+ const struct ieee80211_eht_ml *ml =
+ (const struct ieee80211_eht_ml *) buf;
+ u16 ctrl;
+ size_t eml_capa_pos =
+ MULTI_LINK_CONTROL_LEN + /* Multi-Link Control field */
+ 1 + /* Common Info Length field (Basic) */
+ ETH_ALEN; /* MLD MAC Address field (Basic) */
+ size_t common_info_limit;
+ u8 common_info_len;
+
+ if (len < MULTI_LINK_CONTROL_LEN)
+ return NULL;
+
+ ctrl = le_to_host16(ml->ml_control);
+ if ((ctrl & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC)
+ return NULL;
+ if (!(ctrl & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA))
+ return NULL;
+
+ /* Validate Common Info Length against available data */
+ common_info_len = buf[MULTI_LINK_CONTROL_LEN];
+ if (len < (size_t) MULTI_LINK_CONTROL_LEN + common_info_len)
+ return NULL;
+ common_info_limit = MULTI_LINK_CONTROL_LEN + common_info_len;
+
+ if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID)
+ eml_capa_pos += EHT_ML_LINK_ID_LEN;
+
+ if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT)
+ eml_capa_pos++;
+
+ if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO)
+ eml_capa_pos += 2;
+
+ /* Ensure EML Capabilities field fits within the declared Common Info */
+ if (eml_capa_pos + EHT_ML_EML_CAPA_LEN > common_info_limit)
+ return NULL;
+
+ return &buf[eml_capa_pos];
+}
+
+
+int get_basic_mle_link_id(const u8 *buf, size_t len)
+{
+ struct ieee80211_eht_ml *ml = (struct ieee80211_eht_ml *) buf;
+ u16 ctrl;
+ size_t link_id_pos =
+ MULTI_LINK_CONTROL_LEN + /* Multi-Link Control field */
+ 1 + /* Common Info Length field (Basic) */
+ ETH_ALEN; /* MLD MAC Address field (Basic) */
+ size_t common_info_limit;
+ u8 common_info_len;
+ u8 link_id;
+
+ if (len < MULTI_LINK_CONTROL_LEN)
+ return -1;
+
+ ctrl = le_to_host16(ml->ml_control);
+ if ((ctrl & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC)
+ return -1;
+
+ /* Validate Common Info Length against available data */
+ common_info_len = buf[MULTI_LINK_CONTROL_LEN];
+ if (len < (size_t) MULTI_LINK_CONTROL_LEN + common_info_len)
+ return -1;
+ common_info_limit = MULTI_LINK_CONTROL_LEN + common_info_len;
+
+ if (!(ctrl & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
+ return -1;
+
+ if (link_id_pos + EHT_ML_LINK_ID_LEN > common_info_limit)
+ return -1;
+
+ link_id = buf[link_id_pos] & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ return -1;
+
+ return link_id;
+}
+
+
+/* Parse HT capabilities to get maximum number of supported spatial streams */
+static int
+parse_ht_mcs_set_for_max_nss(const struct ieee80211_ht_capabilities *htcaps,
+ bool parse_for_rx)
+{
+ int i, max_nss_rx = 1;
+ u8 supported_tx_mcs_set, tx_mcs_set_defined, tx_rx_mcs_set_not_equal;
+
+ if (!htcaps)
+ return max_nss_rx;
+
+ for (i = 4; i >= 1; i--) {
+ if (htcaps->supported_mcs_set[i - 1] > 0) {
+ max_nss_rx = i;
+ break;
+ }
+ }
+ if (parse_for_rx)
+ return max_nss_rx;
+
+ supported_tx_mcs_set = htcaps->supported_mcs_set[12];
+ tx_mcs_set_defined = supported_tx_mcs_set & 0x1;
+ tx_rx_mcs_set_not_equal = (supported_tx_mcs_set >> 1) & 0x1;
+ if (tx_mcs_set_defined && tx_rx_mcs_set_not_equal) {
+ u8 max_nss_tx_field_value = (supported_tx_mcs_set >> 2) & 0x3;
+
+ /*
+ * The maximum number of Tx streams is 1 more than the field
+ * value.
+ */
+ return max_nss_tx_field_value + 1;
+ }
+
+ return max_nss_rx;
+}
+
+
+/* Parse MCS map to get maximum number of supported spatial streams */
+static unsigned int parse_mcs_map_for_max_nss(u16 mcs_map,
+ unsigned int max_streams_allowed)
+{
+ unsigned int i, max_nss = 1;
+
+ for (i = max_streams_allowed; i >= 1; i--) {
+ unsigned int stream_map = (mcs_map >> ((i - 1) * 2)) & 0x3;
+
+ /* 3 means unsupported */
+ if (stream_map != 3) {
+ max_nss = i;
+ break;
+ }
+ }
+
+ return max_nss;
+}
+
+
+/* Parse capabilities elements to get maximum number of supported spatial
+ * streams */
+unsigned int get_max_nss_capability(struct ieee802_11_elems *elems,
+ bool parse_for_rx, enum chan_width bw)
+{
+ unsigned int max_nss = 1;
+ struct ieee80211_ht_capabilities *htcaps =
+ (struct ieee80211_ht_capabilities *) elems->ht_capabilities;
+ struct ieee80211_vht_capabilities *vhtcaps =
+ (struct ieee80211_vht_capabilities *) elems->vht_capabilities;
+ struct ieee80211_he_capabilities *hecaps =
+ (struct ieee80211_he_capabilities *) elems->he_capabilities;
+ le16 mcs_map;
+
+ if (hecaps) {
+ unsigned int max_nss_he;
+ const u8 *optional = hecaps->optional;
+
+ if (bw == CHAN_WIDTH_160) {
+ mcs_map = host_to_le16(
+ WPA_GET_LE16(parse_for_rx ?
+ &optional[0] : &optional[2]));
+ } else if (bw == CHAN_WIDTH_80P80) {
+ mcs_map = host_to_le16(
+ WPA_GET_LE16(parse_for_rx ?
+ &optional[4] : &optional[6]));
+ } else {
+ mcs_map = parse_for_rx ?
+ hecaps->he_basic_supported_mcs_set.rx_map :
+ hecaps->he_basic_supported_mcs_set.tx_map;
+ }
+
+ max_nss_he = parse_mcs_map_for_max_nss(
+ le_to_host16(mcs_map), HE_NSS_MAX_STREAMS);
+ if (max_nss_he > max_nss)
+ max_nss = max_nss_he;
+ } else if (vhtcaps) {
+ unsigned int max_nss_vht;
+
+ mcs_map = parse_for_rx ?
+ vhtcaps->vht_supported_mcs_set.rx_map :
+ vhtcaps->vht_supported_mcs_set.tx_map;
+ max_nss_vht = parse_mcs_map_for_max_nss(
+ le_to_host16(mcs_map), VHT_RX_NSS_MAX_STREAMS);
+ if (max_nss_vht > max_nss)
+ max_nss = max_nss_vht;
+ } else if (htcaps) {
+ unsigned int max_nss_ht;
+
+ max_nss_ht = parse_ht_mcs_set_for_max_nss(htcaps, parse_for_rx);
+ if (max_nss_ht > max_nss)
+ max_nss = max_nss_ht;
+ }
+
+ return max_nss;
+}
+
+
+/* Parse VHT/HE capabilities elements to get supported channel width */
+struct supported_chan_width
+get_supported_channel_width(struct ieee802_11_elems *elems, int freq)
+{
+ struct supported_chan_width supported_width;
+ struct ieee80211_ht_capabilities *htcaps;
+ struct ieee80211_vht_capabilities *vhtcaps;
+ struct ieee80211_he_capabilities *hecaps;
+ struct ieee80211_eht_capabilities *ehtcaps;
+
+ supported_width.is_40_supported = false;
+ supported_width.is_160_supported = false;
+ supported_width.is_80p80_supported = false;
+ supported_width.is_320_supported = false;
+ if (!elems)
+ return supported_width;
+
+ htcaps = (struct ieee80211_ht_capabilities *) elems->ht_capabilities;
+ vhtcaps = (struct ieee80211_vht_capabilities *) elems->vht_capabilities;
+ hecaps = (struct ieee80211_he_capabilities *) elems->he_capabilities;
+ ehtcaps = (struct ieee80211_eht_capabilities *) elems->eht_capabilities;
+
+ if (htcaps &&
+ (le_to_host16(htcaps->ht_capabilities_info) &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ supported_width.is_40_supported = true;
+
+ if (vhtcaps) {
+ u32 vht_capabilities_info =
+ le_to_host32(vhtcaps->vht_capabilities_info);
+
+ if (vht_capabilities_info & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
+ supported_width.is_160_supported = true;
+ if (vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+ supported_width.is_160_supported = true;
+ supported_width.is_80p80_supported = true;
+ }
+ }
+
+ if (hecaps) {
+ u8 channel_width_set = hecaps->he_phy_capab_info[
+ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+
+ /*
+ * The 40 MHz capability is band specific in HE: a bit for 2.4
+ * GHz vs. the shared 40/80 MHz bit for 5 GHz.
+ */
+ if (is_24ghz_freq(freq)) {
+ if (channel_width_set &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)
+ supported_width.is_40_supported = true;
+ } else if (channel_width_set &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) {
+ supported_width.is_40_supported = true;
+ }
+
+ if (channel_width_set &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
+ supported_width.is_160_supported = true;
+ if (channel_width_set &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
+ supported_width.is_80p80_supported = true;
+ }
+
+ if (ehtcaps) {
+ if (ehtcaps->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)
+ supported_width.is_320_supported = true;
+ }
+
+ return supported_width;
+}
+
+
+/*
+ * Parse VHT operation info fields to get operation channel width
+ * note that VHT operation info fields could come from the VHT Operation element
+ * or from the HE Operation element.
+ */
+static enum chan_width get_vht_operation_channel_width(
+ const struct ieee80211_vht_operation *vht_oper_info)
+{
+ enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+ u8 seg0, seg1;
+
+ switch (vht_oper_info->vht_op_info_chwidth) {
+ case 1:
+ seg0 = vht_oper_info->vht_op_info_chan_center_freq_seg0_idx;
+ seg1 = vht_oper_info->vht_op_info_chan_center_freq_seg1_idx;
+ if (seg1 && abs(seg1 - seg0) == 8)
+ channel_width = CHAN_WIDTH_160;
+ else if (seg1)
+ channel_width = CHAN_WIDTH_80P80;
+ else
+ channel_width = CHAN_WIDTH_80;
+ break;
+ case 2:
+ channel_width = CHAN_WIDTH_160;
+ break;
+ case 3:
+ channel_width = CHAN_WIDTH_80P80;
+ break;
+ }
+
+ return channel_width;
+}
+
+
+/* Parse 6 GHz operation info fields to get operation channel width */
+static enum chan_width get_6ghz_operation_channel_width(
+ const struct ieee80211_he_6ghz_oper_info *six_ghz_oper_info)
+{
+ enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+ u8 seg0, seg1;
+
+ switch (six_ghz_oper_info->control &
+ HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK) {
+ case 0:
+ channel_width = CHAN_WIDTH_20;
+ break;
+ case 1:
+ channel_width = CHAN_WIDTH_40;
+ break;
+ case 2:
+ channel_width = CHAN_WIDTH_80;
+ break;
+ case 3:
+ seg0 = six_ghz_oper_info->chan_center_freq_seg0;
+ seg1 = six_ghz_oper_info->chan_center_freq_seg1;
+ if (abs(seg1 - seg0) == 8)
+ channel_width = CHAN_WIDTH_160;
+ else
+ channel_width = CHAN_WIDTH_80P80;
+ break;
+ }
+
+ return channel_width;
+}
+
+
+/* Parse HE Operation element to get HE operation channel width */
+static enum chan_width get_he_operation_channel_width(
+ const struct ieee80211_he_operation *he_oper, size_t he_oper_len)
+{
+ enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+ const u8 *he_oper_u8 = (const u8 *) he_oper;
+ bool is_6ghz_info_present, is_vht_info_present, is_cohosted_bss_present;
+ size_t expected_len;
+
+ if (he_oper_len < HE_OPERATION_ELEM_MIN_LEN)
+ return channel_width;
+
+ is_6ghz_info_present =
+ he_oper->he_oper_params & HE_OPERATION_6GHZ_OPER_INFO;
+ is_vht_info_present =
+ he_oper->he_oper_params & HE_OPERATION_VHT_OPER_INFO;
+ is_cohosted_bss_present =
+ he_oper->he_oper_params & HE_OPERATION_COHOSTED_BSS;
+ expected_len = HE_OPERATION_ELEM_MIN_LEN +
+ (is_6ghz_info_present ? HE_OPERATION_6GHZ_OPER_INFO_LEN : 0) +
+ (is_vht_info_present ? HE_OPERATION_VHT_OPER_INFO_LEN : 0) +
+ (is_cohosted_bss_present ?
+ HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0);
+
+ if (he_oper_len < expected_len)
+ return channel_width;
+
+ if (is_6ghz_info_present) {
+ struct ieee80211_he_6ghz_oper_info *six_ghz_oper_info =
+ (struct ieee80211_he_6ghz_oper_info *)
+ (he_oper_u8 + HE_OPERATION_ELEM_MIN_LEN +
+ (is_vht_info_present ?
+ HE_OPERATION_VHT_OPER_INFO_LEN : 0) +
+ (is_cohosted_bss_present ?
+ HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0));
+
+ channel_width =
+ get_6ghz_operation_channel_width(six_ghz_oper_info);
+ }
+
+ if (channel_width == CHAN_WIDTH_UNKNOWN && is_vht_info_present) {
+ struct ieee80211_vht_operation *vht_oper_info =
+ (struct ieee80211_vht_operation *)
+ (he_oper_u8 + HE_OPERATION_ELEM_MIN_LEN);
+
+ channel_width = get_vht_operation_channel_width(vht_oper_info);
+ }
+
+ return channel_width;
+}
+
+
+/* Parse EHT Operation element to get EHT operation channel width */
+static enum chan_width get_eht_operation_channel_width(
+ const struct ieee80211_eht_operation *eht_oper, size_t eht_oper_len)
+{
+ if (eht_oper_len < EHT_OPERATION_ELEM_MIN_LEN + EHT_OPER_INFO_MIN_LEN ||
+ !(eht_oper->oper_params & EHT_OPER_INFO_PRESENT))
+ return CHAN_WIDTH_UNKNOWN;
+
+ switch (eht_oper->oper_info.control & EHT_OPER_CHANNEL_WIDTH_MASK) {
+ case EHT_OPER_CHANNEL_WIDTH_20MHZ:
+ return CHAN_WIDTH_20;
+ case EHT_OPER_CHANNEL_WIDTH_40MHZ:
+ return CHAN_WIDTH_40;
+ case EHT_OPER_CHANNEL_WIDTH_80MHZ:
+ return CHAN_WIDTH_80;
+ case EHT_OPER_CHANNEL_WIDTH_160MHZ:
+ return CHAN_WIDTH_160;
+ case EHT_OPER_CHANNEL_WIDTH_320MHZ:
+ return CHAN_WIDTH_320;
+ default:
+ return CHAN_WIDTH_UNKNOWN;
+ }
+}
+
+
+/* Parse HT/VHT/HE operation elements to get operation channel width */
+enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems)
+{
+ enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+ struct ieee80211_ht_operation *ht_oper;
+ struct ieee80211_vht_operation *vht_oper_info;
+ struct ieee80211_he_operation *he_oper;
+ struct ieee80211_eht_operation *eht_oper;
+
+ if (!elems)
+ return channel_width;
+
+ ht_oper = (struct ieee80211_ht_operation *) elems->ht_operation;
+ vht_oper_info = (struct ieee80211_vht_operation *) elems->vht_operation;
+ he_oper = (struct ieee80211_he_operation *) elems->he_operation;
+ eht_oper = (struct ieee80211_eht_operation *) elems->eht_operation;
+
+ if (eht_oper)
+ channel_width = get_eht_operation_channel_width(
+ eht_oper, elems->eht_operation_len);
+
+ if (channel_width == CHAN_WIDTH_UNKNOWN && he_oper)
+ channel_width = get_he_operation_channel_width(
+ he_oper, elems->he_operation_len);
+
+ if (channel_width == CHAN_WIDTH_UNKNOWN && vht_oper_info)
+ channel_width = get_vht_operation_channel_width(vht_oper_info);
+
+ if (channel_width == CHAN_WIDTH_UNKNOWN && ht_oper) {
+ u8 sec_chan_offset = ht_oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+
+ channel_width = sec_chan_offset == 0 ?
+ CHAN_WIDTH_20 : CHAN_WIDTH_40;
+ }
+
+ return channel_width;
+}
+
+
+/*
+ * Get STA operation channel width from AP's operation channel width and
+ * STA's supported channel width
+ */
+enum chan_width get_sta_operation_chan_width(
+ enum chan_width ap_operation_chan_width,
+ struct supported_chan_width sta_supported_chan_width)
+{
+ if (ap_operation_chan_width == CHAN_WIDTH_320 &&
+ sta_supported_chan_width.is_320_supported)
+ return CHAN_WIDTH_320;
+
+ if (ap_operation_chan_width == CHAN_WIDTH_160 ||
+ ap_operation_chan_width == CHAN_WIDTH_320)
+ return sta_supported_chan_width.is_160_supported ?
+ CHAN_WIDTH_160 : CHAN_WIDTH_80;
+
+ if (ap_operation_chan_width == CHAN_WIDTH_80P80)
+ return sta_supported_chan_width.is_80p80_supported ?
+ CHAN_WIDTH_80P80 : CHAN_WIDTH_80;
+
+ if (ap_operation_chan_width == CHAN_WIDTH_40)
+ return sta_supported_chan_width.is_40_supported ?
+ CHAN_WIDTH_40 : CHAN_WIDTH_20;
+
+ return ap_operation_chan_width;
+}
+
+
+static const u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 };
+static const u8 channels_160mhz[] = { 50, 114, 163 };
+
+
+static u8 op_class_idx_to_chan_vht(u8 op_class, u8 idx)
+{
+ const u8 *chans_array;
+ unsigned int size;
+
+ if (op_class == 128 || op_class == 130) {
+ chans_array = channels_80mhz;
+ size = ARRAY_SIZE(channels_80mhz);
+ } else if (op_class == 129) {
+ chans_array = channels_160mhz;
+ size = ARRAY_SIZE(channels_160mhz);
+ } else {
+ return 0;
+ }
+
+ if (idx >= size)
+ return 0;
+
+ return chans_array[idx];
+}
+
+
+/**
+ * op_class_idx_to_chan - Channel index in the operating class to channel number
+ * @op: A pointer to the operating class object
+ * @idx: The channel index within the operating class. The channels are ordered
+ * from the lowest number to the highest, index starting from 0.
+ */
+u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx)
+{
+ u8 chan;
+
+ if (op->bw == BW80 || op->bw == BW80P80 || op->bw == BW160)
+ return op_class_idx_to_chan_vht(op->op_class, idx);
+
+ chan = op->min_chan + idx * op->inc;
+ if (chan > op->max_chan)
+ return 0;
+
+ return chan;
+}
+
+
+static int op_class_chan_to_idx_vht(u8 op_class, u8 chan)
+{
+ const u8 *chans_array;
+ unsigned int i, size;
+
+ if (op_class == 128 || op_class == 130) {
+ chans_array = channels_80mhz;
+ size = ARRAY_SIZE(channels_80mhz);
+ } else if (op_class == 129) {
+ chans_array = channels_160mhz;
+ size = ARRAY_SIZE(channels_160mhz);
+ } else {
+ return -1;
+ }
+
+ for (i = 0; i < size; i++)
+ if (chan == chans_array[i])
+ return i;
+
+ return -1;
+}
+
+
+/**
+ * op_class_chan_to_idx - Channel number to channel index in the operating class
+ * @op: A pointer to the operating class object
+ * @chan: The channel number
+ * Returns: Channel index or -1 if channel not found
+ */
+int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan)
+{
+ if (op->bw == BW80 || op->bw == BW80P80 || op->bw == BW160)
+ return op_class_chan_to_idx_vht(op->op_class, chan);
+
+ if (chan < op->min_chan || chan > op->max_chan ||
+ (chan - op->min_chan) % op->inc)
+ return -1;
+
+ return (chan - op->min_chan) / op->inc;
+}
+
+
+static int get_center_freq_80mhz(int ctrl_freq)
+{
+ static const int center_freqs[] =
+ { 5210, 5290, 5530, 5610, 5690, 5775 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(center_freqs); i++) {
+ if (ctrl_freq >= center_freqs[i] - 30 &&
+ ctrl_freq <= center_freqs[i] + 30)
+ return center_freqs[i];
+ }
+
+ return 0;
+}
+
+
+static int get_center_freq_160mhz(int ctrl_freq)
+{
+ static const int center_freqs[] = { 5250, 5570 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(center_freqs); i++) {
+ if (ctrl_freq >= center_freqs[i] - 70 &&
+ ctrl_freq <= center_freqs[i] + 70)
+ return center_freqs[i];
+ }
+
+ return 0;
+}
+
+
+/**
+ * ieee80211_get_center_freq - Get center frequency based on control
+ * frequency and operating class information
+ *
+ * @ctrl_freq: Control frequency in MHz
+ * @bw: The bandwidth as defined in struct oper_class_map
+ */
+int ieee80211_get_center_freq(int ctrl_freq, u32 bw)
+{
+ switch (bw) {
+ case BW20:
+ return ctrl_freq;
+ case BW40PLUS:
+ return ctrl_freq + 10;
+ case BW40MINUS:
+ return ctrl_freq - 10;
+ case BW80:
+ case BW80P80:
+ return get_center_freq_80mhz(ctrl_freq);
+ case BW160:
+ return get_center_freq_160mhz(ctrl_freq);
+ default:
+ return -1;
+ }
+}
diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h
index 9e9684c113f5..2e8565db4506 100644
--- a/contrib/wpa/src/common/ieee802_11_common.h
+++ b/contrib/wpa/src/common/ieee802_11_common.h
@@ -38,6 +38,11 @@ struct multi_ap_params {
/* Parsed Information Elements */
struct ieee802_11_elems {
+ /* Control of parsing operations */
+ bool show_errors;
+ bool stop_at_mic; /* Whether to stop parsing after MIC element */
+
+ /* Parsed data */
const u8 *ssid;
const u8 *supp_rates;
const u8 *ds_params;
@@ -65,6 +70,8 @@ struct ieee802_11_elems {
const u8 *vendor_ht_cap;
const u8 *vendor_vht;
const u8 *p2p;
+ const u8 *p2p2_ie;
+ const u8 *pasn_encrypted_data;
const u8 *wfd;
const u8 *link_id;
const u8 *interworking;
@@ -73,7 +80,6 @@ struct ieee802_11_elems {
const u8 *ext_capab;
const u8 *bss_max_idle_period;
const u8 *ssid_list;
- const u8 *osen;
const u8 *mbo;
const u8 *ampe;
const u8 *mic;
@@ -93,7 +99,7 @@ struct ieee802_11_elems {
const u8 *key_delivery;
const u8 *wrapped_data;
const u8 *fils_pk;
- const u8 *fils_nonce;
+ const u8 *nonce;
const u8 *owe_dh;
const u8 *power_capab;
const u8 *roaming_cons_sel;
@@ -116,6 +122,15 @@ struct ieee802_11_elems {
const u8 *prior_access_mle;
const u8 *mbssid_known_bss;
const u8 *mbssid;
+ const u8 *rsne_override;
+ const u8 *rsne_override_2;
+ const u8 *rsnxe_override;
+ const u8 *rsn_selection;
+ const u8 *wfa_capab;
+ const u8 *proximity_ranging;
+ const u8 *akm_suite_selector;
+ const u8 *supported_groups;
+ const u8 *nan_ie;
u8 ssid_len;
u8 supp_rates_len;
@@ -136,13 +151,14 @@ struct ieee802_11_elems {
u8 vendor_ht_cap_len;
u8 vendor_vht_len;
u8 p2p_len;
+ u8 p2p2_ie_len;
+ u8 pasn_encrypted_data_len;
u8 wfd_len;
u8 interworking_len;
u8 qos_map_set_len;
u8 hs20_len;
u8 ext_capab_len;
u8 ssid_list_len;
- u8 osen_len;
u8 mbo_len;
u8 ampe_len;
u8 mic_len;
@@ -156,9 +172,10 @@ struct ieee802_11_elems {
u8 fils_key_confirm_len;
size_t fils_hlp_len;
u8 fils_ip_addr_assign_len;
- u8 key_delivery_len;
+ size_t key_delivery_len;
size_t wrapped_data_len;
u8 fils_pk_len;
+ u8 nonce_len;
u8 owe_dh_len;
u8 power_capab_len;
u8 roaming_cons_sel_len;
@@ -179,6 +196,15 @@ struct ieee802_11_elems {
size_t prior_access_mle_len;
u8 mbssid_known_bss_len;
u8 mbssid_len;
+ size_t rsne_override_len;
+ size_t rsne_override_2_len;
+ size_t rsnxe_override_len;
+ size_t rsn_selection_len;
+ u8 wfa_capab_len;
+ size_t proximity_ranging_len;
+ u8 akm_suite_selector_len;
+ u8 supported_groups_len;
+ size_t nan_len;
struct mb_ies_info mb_ies;
@@ -196,14 +222,18 @@ typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
struct ieee802_11_elems *elems,
int show_errors);
+ParseRes ieee802_11_parse_elems_ctrl(const u8 *start, size_t len,
+ struct ieee802_11_elems *elems);
void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems,
const u8 *ids, size_t num);
void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
const u8 *ids, size_t num);
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
- struct ieee802_11_elems *elems,
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
struct wpabuf *mlbuf,
u8 link_id, bool show_errors);
+ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems,
+ struct wpabuf *mlbuf,
+ u8 link_id, bool show_errors);
int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type);
@@ -244,7 +274,7 @@ int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes);
int is_dfs_global_op_class(u8 op_class);
bool is_80plus_op_class(u8 op_class);
-enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
+enum phy_type ieee80211_get_phy_type(int freq, bool ht, bool vht, bool he);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
@@ -289,6 +319,8 @@ u8 country_to_global_op_class(const char *country, u8 op_class);
const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
int oper_class_bw_to_int(const struct oper_class_map *map);
+bool is_24ghz_freq(int freq);
+bool is_5ghz_freq(int freq);
int center_idx_to_bw_6ghz(u8 idx);
bool is_6ghz_freq(int freq);
bool is_6ghz_op_class(u8 op_class);
@@ -299,6 +331,10 @@ bool is_same_band(int freq1, int freq2);
#define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
#define IS_5GHZ(n) (n > 4000 && n < 5895)
+u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx);
+int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan);
+int ieee80211_get_center_freq(int ctrl_freq, u32 bw);
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len);
@@ -309,6 +345,7 @@ bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab);
int op_class_to_bandwidth(u8 op_class);
enum oper_chan_width op_class_to_ch_width(u8 op_class);
int chwidth_freq2_to_ch_width(int chwidth, int freq2);
+enum oper_chan_width chan_width_to_oper_chwidth(enum chan_width chan_width);
/* element iteration helpers */
#define for_each_element(_elem, _data, _datalen) \
@@ -367,7 +404,31 @@ int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested);
struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem);
+ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
+ const u8 *parent_subelem,
+ size_t *defrag_len);
const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
+const u8 * get_basic_mle_eml_capa(const u8 *buf, size_t len);
+int get_basic_mle_link_id(const u8 *buf, size_t len);
+
+unsigned int get_max_nss_capability(struct ieee802_11_elems *elems,
+ bool parse_for_rx, enum chan_width bw);
+
+struct supported_chan_width {
+ bool is_40_supported;
+ bool is_160_supported;
+ bool is_80p80_supported;
+ bool is_320_supported;
+};
+
+struct supported_chan_width
+get_supported_channel_width(struct ieee802_11_elems *elems, int freq);
+
+enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems);
+
+enum chan_width get_sta_operation_chan_width(
+ enum chan_width ap_operation_chan_width,
+ struct supported_chan_width sta_supported_width);
#endif /* IEEE802_11_COMMON_H */
diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h
index 0fe63102603f..2d3b4677447b 100644
--- a/contrib/wpa/src/common/ieee802_11_defs.h
+++ b/contrib/wpa/src/common/ieee802_11_defs.h
@@ -21,7 +21,7 @@
#define WLAN_FC_RETRY 0x0800
#define WLAN_FC_PWRMGT 0x1000
#define WLAN_FC_MOREDATA 0x2000
-#define WLAN_FC_ISWEP 0x4000
+#define WLAN_FC_PROTECTED 0x4000
#define WLAN_FC_HTC 0x8000
#define WLAN_FC_S1G_BEACON_NEXT_TBTT 0x0100
@@ -98,28 +98,39 @@
#define WLAN_AUTH_FILS_SK_PFS 5
#define WLAN_AUTH_FILS_PK 6
#define WLAN_AUTH_PASN 7
+#define WLAN_AUTH_802_1X 8
+#define WLAN_AUTH_EPPKE 9
#define WLAN_AUTH_LEAP 128
+/* Authentication transaction sequence number */
+#define WLAN_AUTH_TR_SEQ_SAE_COMMIT 1
+#define WLAN_AUTH_TR_SEQ_SAE_CONFIRM 2
+
+#define WLAN_AUTH_TR_SEQ_PASN_AUTH1 1
+#define WLAN_AUTH_TR_SEQ_PASN_AUTH2 2
+#define WLAN_AUTH_TR_SEQ_PASN_AUTH3 3
+
#define WLAN_AUTH_CHALLENGE_LEN 128
+/* IEEE Std 802.11-2024, 9.4.1.4 (Capability Information field) */
#define WLAN_CAPABILITY_ESS BIT(0)
#define WLAN_CAPABILITY_IBSS BIT(1)
-#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
-#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+/* bit 2 is reserved (was WLAN_CAPABILITY_CF_POLLABLE) */
+/* bit 3 is reserved (was WLAN_CAPABILITY_CF_POLL_REQUEST) */
#define WLAN_CAPABILITY_PRIVACY BIT(4)
#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
-#define WLAN_CAPABILITY_PBCC BIT(6)
-#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
+/* bit 6 is reserved (was WLAN_CAPABILITY_PBCC) */
+/* bit 7 is reserved (was WLAN_CAPABILITY_CHANNEL_AGILITY) */
#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
#define WLAN_CAPABILITY_QOS BIT(9)
#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
#define WLAN_CAPABILITY_APSD BIT(11)
#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12)
-#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
-#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
-#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
+#define WLAN_CAPABILITY_EPD BIT(13)
+/* bit 14 is reserved (was WLAN_CAPABILITY_DELAYED_BLOCK_ACK) */
+/* bit 15 is reserved (was WLAN_CAPABILITY_IMM_BLOCK_ACK) */
-/* Status codes (IEEE Std 802.11-2020, 9.4.1.9, Table 9-50) */
+/* Status codes (IEEE Std 802.11-2024, 9.4.1.9, Table 9-80) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
@@ -153,13 +164,13 @@
#define WLAN_STATUS_REQUEST_DECLINED 37
#define WLAN_STATUS_INVALID_PARAMETERS 38
#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
-#define WLAN_STATUS_INVALID_IE 40
-#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
-#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
-#define WLAN_STATUS_AKMP_NOT_VALID 43
-#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
-#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
-#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+#define WLAN_STATUS_INVALID_ELEMENT 40
+#define WLAN_STATUS_INVALID_GROUP_CIPHER 41
+#define WLAN_STATUS_INVALID_PAIRWISE_CIPHER 42
+#define WLAN_STATUS_INVALID_AKMP 43
+#define WLAN_STATUS_UNSUPPORTED_RSNE_VERSION 44
+#define WLAN_STATUS_INVALID_RSNE_CAPABILITIES 45
+#define WLAN_STATUS_CIPHER_OUT_OF_POLICY 46
#define WLAN_STATUS_TS_NOT_CREATED 47
#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
@@ -167,8 +178,8 @@
#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
#define WLAN_STATUS_INVALID_PMKID 53
-#define WLAN_STATUS_INVALID_MDIE 54
-#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_INVALID_MDE 54
+#define WLAN_STATUS_INVALID_FTE 55
#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56
#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
#define WLAN_STATUS_TRY_ANOTHER_BSS 58
@@ -181,7 +192,7 @@
#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
#define WLAN_STATUS_REQ_REFUSED_SSPN 67
#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
-#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_INVALID_RSNE 72
#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73
#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74
#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75
@@ -233,6 +244,8 @@
#define WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED 139
#define WLAN_STATUS_EPCS_DENIED_VERIFICATION_FAILURE 140
#define WLAN_STATUS_DENIED_OPERATION_PARAMETER_UPDATE 141
+#define WLAN_STATUS_802_1_X_AUTH_FAILED 152
+#define WLAN_STATUS_802_1_X_AUTH_SUCCESS 153
/* Reason codes (IEEE Std 802.11-2020, 9.4.1.7, Table 9-90) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -495,7 +508,7 @@
#define WLAN_EID_EXT_EXTENDED_REQUEST 10
#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
-#define WLAN_EID_EXT_FILS_NONCE 13
+#define WLAN_EID_EXT_NONCE 13
#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
#define WLAN_EID_EXT_OWE_DH_PARAM 32
#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
@@ -504,6 +517,7 @@
#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
+#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52
#define WLAN_EID_EXT_OCV_OCI 54
#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
#define WLAN_EID_EXT_NON_INHERITANCE 56
@@ -524,6 +538,10 @@
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
+#define WLAN_EID_EXT_BANDWIDTH_INDICATION 135
+#define WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION 136
+#define WLAN_EID_EXT_PASN_ENCRYPTED_DATA 140
+#define WLAN_EID_EXT_SUPPORTED_GROUPS 161
/* Extended Capabilities field */
#define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -606,6 +624,7 @@
#define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
#define WLAN_EXT_CAPAB_MSCS 85
#define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
+#define WLAN_EXT_CAPAB_KNOWN_STA_IDENTIFICATION 102
/* Extended RSN Capabilities */
/* bits 0-3: Field length (n-1) */
@@ -615,8 +634,15 @@
#define WLAN_RSNX_CAPAB_SECURE_LTF 8
#define WLAN_RSNX_CAPAB_SECURE_RTT 9
#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
+#define WLAN_RSNX_CAPAB_SPP_A_MSDU 14
#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_KEK_IN_PASN 18
#define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
+#define WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION 27
+#define WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES 28
+#define WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY 29
+#define WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE 34
+#define WLAN_RSNX_CAPAB_UNAUTH_EPPKE 36
/* Multiple BSSID element subelements */
#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
@@ -700,7 +726,7 @@
#define WLAN_PA_FILS_DISCOVERY 34
#define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47
-/* HT Action field values (IEEE P802.11-REVme/D4.0, 9.6.11.1, Table 9-491) */
+/* HT Action field values (IEEE Std 802.11-2024, 9.6.11.1, Table 9-517) */
#define WLAN_HT_ACTION_NOTIFY_CHANWIDTH 0
#define WLAN_HT_ACTION_SMPS 1
#define WLAN_HT_ACTION_CSI 4
@@ -708,7 +734,7 @@
#define WLAN_HT_ACTION_COMPRESSED_BF 6
#define WLAN_HT_ACTION_ASEL_IDX_FEEDBACK 7
-/* VHT Action field values (IEEE P802.11-REVme/D4.0, 9.6.22.1, Table 9-579) */
+/* VHT Action field values (IEEE Std 802.11-2024, 9.6.22.1, Table 9-605) */
#define WLAN_VHT_ACTION_COMPRESSED_BF 0
#define WLAN_VHT_ACTION_GROUP_ID_MGMT 1
#define WLAN_VHT_ACTION_OPMODE_NOTIF 2
@@ -774,6 +800,21 @@
#define WLAN_PROT_FTM 2
#define WLAN_PROT_FTM_REPORT 3
+/* Protected EHT Action field values (IEEE Std 802.11be-2024, Table 9-658d) */
+#define WLAN_PROT_EHT_T2L_MAPPING_REQUEST 0
+#define WLAN_PROT_EHT_T2L_MAPPING_RESPONSE 1
+#define WLAN_PROT_EHT_T2L_MAPPING_TEARDOWN 2
+#define WLAN_PROT_EHT_EPCS_ENABLE_REQUEST 3
+#define WLAN_PROT_EHT_EPCS_ENABLE_RESPONSE 4
+#define WLAN_PROT_EHT_EPCS_ENABLE_TEARDOWN 5
+#define WLAN_PROT_EHT_EML_OPMODE_NOTIF 6
+#define WLAN_PROT_EHT_LINK_RECOMMENDATION 7
+#define WLAN_PROT_EHT_MLO_UPDATE_REQUEST 8
+#define WLAN_PROT_EHT_MLO_UPDATE_RESPONSE 9
+#define WLAN_PROT_EHT_LINK_RECONFIG_NOTIFY 10
+#define WLAN_PROT_EHT_LINK_RECONFIG_REQUEST 11
+#define WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE 12
+
/* Radio Measurement capabilities (from RM Enabled Capabilities element)
* IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */
/* byte 1 (out of 5) */
@@ -786,6 +827,7 @@
#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
/* byte 5 (out of 5) */
#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2)
+#define WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT BIT(3)
/*
* IEEE Std 802.11-2020, 9.4.2.20.19 (Fine Timing Measurement Range
@@ -941,7 +983,7 @@ enum lci_req_subelem {
LCI_REQ_SUBELEM_MAX_AGE = 4,
};
-#define FILS_NONCE_LEN 16
+#define NONCE_LEN 16
#define FILS_SESSION_LEN 8
#define FILS_CACHE_ID_LEN 2
#define FILS_MAX_KEY_AUTH_LEN 48
@@ -1160,12 +1202,40 @@ struct ieee80211_mgmt {
u8 dialog_token;
u8 variable[];
} STRUCT_PACKED rrm;
+ struct {
+ u8 action; /* Protected EHT - 11 */
+ u8 dialog_token;
+ /* Reconfiguration Multi-Link element,
+ * OCI element (optional) */
+ u8 variable[];
+ } STRUCT_PACKED link_reconf_req;
+ struct {
+ u8 action; /* Protected EHT - 12 */
+ u8 dialog_token;
+ u8 count;
+ /* Reconfiguration status list,
+ * Group Key Data (optional),
+ * OCI element (optional),
+ * Basic Multi-Link element (optional) */
+ u8 variable[];
+ } STRUCT_PACKED link_reconf_resp;
+ struct {
+ u8 action;
+ u8 oui[3];
+ u8 oui_type;
+ u8 subtype;
+ u8 variable[0];
+ } STRUCT_PACKED naf;
} u;
} STRUCT_PACKED action;
} u;
} STRUCT_PACKED;
+#define IEEE80211_MIN_ACTION_LEN(type) \
+ (offsetof(struct ieee80211_mgmt, u.action.u.type) + \
+ sizeof(((struct ieee80211_mgmt *) 0)->u.action.u.type))
+
#define IEEE80211_MAX_MMPDU_SIZE 2304
/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
@@ -1343,6 +1413,7 @@ struct ieee80211_ampe_ie {
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
+#define BSS_MEMBERSHIP_SELECTOR_EHT_PHY 121
#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
#define BSS_MEMBERSHIP_SELECTOR_EPD 124
@@ -1425,15 +1496,16 @@ struct ieee80211_ampe_ie {
#define WPS_IE_VENDOR_TYPE 0x0050f204
#define OUI_WFA 0x506f9a
#define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define P2P2_IE_VENDOR_TYPE 0x506f9a28
#define WFD_IE_VENDOR_TYPE 0x506f9a0a
#define WFD_OUI_TYPE 10
#define HS20_IE_VENDOR_TYPE 0x506f9a10
-#define OSEN_IE_VENDOR_TYPE 0x506f9a12
#define NAN_IE_VENDOR_TYPE 0x506f9a13
#define NAN_SDF_VENDOR_TYPE 0x506f9a13
-#define NAN_OUI_TYPE 0x13
+#define NAN_SDF_OUI_TYPE 0x13
#define MBO_IE_VENDOR_TYPE 0x506f9a16
#define NAN_NAF_VENDOR_TYPE 0x506f9a18
+#define NAN_NAF_OUI_TYPE 0x18
#define MBO_OUI_TYPE 22
#define OWE_IE_VENDOR_TYPE 0x506f9a1c
#define OWE_OUI_TYPE 28
@@ -1446,6 +1518,17 @@ struct ieee80211_ampe_ie {
#define QM_IE_OUI_TYPE 0x22
#define WFA_CAPA_IE_VENDOR_TYPE 0x506f9a23
#define WFA_CAPA_OUI_TYPE 0x23
+#define WFA_RSNE_OVERRIDE_OUI_TYPE 0x29
+#define WFA_RSNE_OVERRIDE_2_OUI_TYPE 0x2a
+#define WFA_RSNXE_OVERRIDE_OUI_TYPE 0x2b
+#define WFA_RSN_SELECTION_OUI_TYPE 0x2c
+#define RSNE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a29
+#define RSNE_OVERRIDE_2_IE_VENDOR_TYPE 0x506f9a2a
+#define RSNXE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a2b
+#define RSN_SELECTION_IE_VENDOR_TYPE 0x506f9a2c
+/* Proximity Ranging (PR) */
+#define PR_IE_VENDOR_TYPE 0x506f9a2e
+#define PR_OUI_TYPE 0x2e
#define MULTI_AP_SUB_ELEM_TYPE 0x06
#define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07
@@ -1579,7 +1662,6 @@ enum wmm_ac {
#define HS20_INDICATION_OUI_TYPE 16
#define HS20_ANQP_OUI_TYPE 17
-#define HS20_OSEN_OUI_TYPE 18
#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
#define HS20_STYPE_QUERY_LIST 1
#define HS20_STYPE_CAPABILITY_LIST 2
@@ -1588,11 +1670,6 @@ enum wmm_ac {
#define HS20_STYPE_CONNECTION_CAPABILITY 5
#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
#define HS20_STYPE_OPERATING_CLASS 7
-#define HS20_STYPE_OSU_PROVIDERS_LIST 8
-#define HS20_STYPE_ICON_REQUEST 10
-#define HS20_STYPE_ICON_BINARY_FILE 11
-#define HS20_STYPE_OPERATOR_ICON_METADATA 12
-#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
#define HS20_DGAF_DISABLED 0x01
#define HS20_PPS_MO_ID_PRESENT 0x02
@@ -1602,7 +1679,6 @@ enum wmm_ac {
#endif /* HS20_VERSION */
/* WNM-Notification WFA vendors specific subtypes */
-#define HS20_WNM_SUB_REM_NEEDED 0
#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2
#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3
@@ -1710,6 +1786,7 @@ enum mbo_transition_reject_reason {
/* Wi-Fi Direct (P2P) */
#define P2P_OUI_TYPE 9
+#define P2P2_OUI_TYPE 0x28
enum p2p_attr_id {
P2P_ATTR_STATUS = 0,
@@ -1740,6 +1817,13 @@ enum p2p_attr_id {
P2P_ATTR_SESSION_ID = 26,
P2P_ATTR_FEATURE_CAPABILITY = 27,
P2P_ATTR_PERSISTENT_GROUP = 28,
+ P2P_ATTR_CAPABILITY_EXTENSION = 29,
+ P2P_ATTR_WLAN_AP_INFORMATION = 30,
+ P2P_ATTR_DEVICE_IDENTITY_KEY = 31,
+ P2P_ATTR_DEVICE_IDENTITY_RESOLUTION = 32,
+ P2P_ATTR_PAIRING_AND_BOOTSTRAPPING = 33,
+ P2P_ATTR_PASSWORD = 34,
+ P2P_ATTR_ACTION_FRAME_WRAPPER = 35,
P2P_ATTR_VENDOR_SPECIFIC = 221
};
@@ -1764,6 +1848,31 @@ enum p2p_attr_id {
#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
+/* P2P Capability Extension attribute - Capability info */
+#define P2P_PCEA_LEN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+#define P2P_PCEA_6GHZ BIT(4)
+#define P2P_PCEA_REG_INFO BIT(5)
+#define P2P_PCEA_DFS_OWNER BIT(6)
+#define P2P_PCEA_CLI_REQ_CS BIT(7)
+#define P2P_PCEA_PAIRING_CAPABLE BIT(8)
+#define P2P_PCEA_PAIRING_SETUP_ENABLED BIT(9)
+#define P2P_PCEA_PMK_CACHING BIT(10)
+#define P2P_PCEA_PASN_TYPE BIT(11)
+#define P2P_PCEA_TWT_POWER_MGMT BIT(12)
+
+/* P2P Pairing Bootstrapping Method attribute - Bootstrapping Method */
+#define P2P_PBMA_OPPORTUNISTIC BIT(0)
+#define P2P_PBMA_PIN_CODE_DISPLAY BIT(1)
+#define P2P_PBMA_PASSPHRASE_DISPLAY BIT(2)
+#define P2P_PBMA_QR_DISPLAY BIT(3)
+#define P2P_PBMA_NFC_TAG BIT(4)
+#define P2P_PBMA_PIN_CODE_KEYPAD BIT(5)
+#define P2P_PBMA_PASSPHRASE_KEYPAD BIT(6)
+#define P2P_PBMA_QR_SCAN BIT(7)
+#define P2P_PBMA_NFC_READER BIT(8)
+#define P2P_PBMA_SERVICE_MANAGED BIT(14)
+#define P2P_PBMA_HANDSHAKE_SKIP BIT(15)
+
/* P2PS Coordination Protocol Transport Bitmap */
#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
@@ -1795,6 +1904,7 @@ enum p2p_status_code {
P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
P2P_SC_FAIL_REJECTED_BY_USER = 11,
P2P_SC_SUCCESS_DEFERRED = 12,
+ P2P_SC_COMEBACK = 13,
};
enum p2p_role_indication {
@@ -1989,6 +2099,7 @@ enum bss_trans_mgmt_reason {
#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
#define WNM_NEIGHBOR_VHT_CAPAB 191
#define WNM_NEIGHBOR_VHT_OPER 192
+#define WNM_NEIGHBOR_MULTI_LINK 201
/* QoS action */
enum qos_action {
@@ -2322,9 +2433,10 @@ enum phy_type {
PHY_TYPE_S1G = 11,
PHY_TYPE_CDMG = 12,
PHY_TYPE_CMMG = 13,
+ PHY_TYPE_HE = 14,
};
-/* IEEE Std 802.11-2020, 9.4.2.36 - Neighbor Report element */
+/* IEEE Std 802.11-2024, 9.4.2.35 - Neighbor Report element */
/* BSSID Information Field */
#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
@@ -2335,14 +2447,21 @@ enum phy_type {
#define NEI_REP_BSSID_INFO_QOS BIT(5)
#define NEI_REP_BSSID_INFO_APSD BIT(6)
#define NEI_REP_BSSID_INFO_RM BIT(7)
-#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8)
-#define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
+/* bit 8 is reserved (was NEI_REP_BSSID_INFO_DELAYED_BA) */
+/* bit 9 is reserved (was NEI_REP_BSSID_INFO_IMM_BA) */
#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
#define NEI_REP_BSSID_INFO_HT BIT(11)
#define NEI_REP_BSSID_INFO_VHT BIT(12)
#define NEI_REP_BSSID_INFO_FTM BIT(13)
#define NEI_REP_BSSID_INFO_HE BIT(14)
+#define NEI_REP_BSSID_INFO_ER_BSS BIT(15)
+#define NEI_REP_BSSID_INFO_COLOCATED_AP BIT(16)
+#define NEI_REP_BSSID_INFO_UNSOLIC_PROBE_RESP BIT(17)
+#define NEI_REP_BSSID_INFO_MEMBER_OF_ESS_WITH_COLOC_AP BIT(18)
+#define NEI_REP_BSSID_INFO_OCT_WITH_REPORTING_AP BIT(19)
+#define NEI_REP_BSSID_INFO_COLOCATED_WITH_6GHZ BIT(20)
#define NEI_REP_BSSID_INFO_EHT BIT(21)
+#define NEI_REP_BSSID_INFO_DMG_POSITIONING BIT(22)
/*
* IEEE Std 802.11-2020, Table 9-175 - HT/VHT Operation Information
@@ -2360,9 +2479,16 @@ enum nr_chan_width {
struct ieee80211_he_capabilities {
u8 he_mac_capab_info[6];
u8 he_phy_capab_info[11];
- /* Followed by 4, 8, or 12 octets of Supported HE-MCS And NSS Set field
- * and optional variable length PPE Thresholds field. */
- u8 optional[37];
+ /* Supported HE-MCS And NSS Set field */
+ struct {
+ le16 rx_map; /* Rx HE-MCS Map <= 80 MHz */
+ le16 tx_map; /* Tx HE-MCS Map <= 80 MHz */
+ /* Followed by 0, 4, or 8 octets of optional Rx/Tx HE-MCS maps
+ * for 160 MHz and 80+80 MHz. These are included in the
+ * optional[] below. */
+ } he_basic_supported_mcs_set;
+ /* Followed by optional variable length PPE Thresholds field. */
+ u8 optional[33];
} STRUCT_PACKED;
#define IEEE80211_HE_CAPAB_MIN_LEN (6 + 11)
@@ -2439,6 +2565,8 @@ struct ieee80211_spatial_reuse {
u8 params[19];
} STRUCT_PACKED;
+#define HE_CAPABILITIES_ELEM_MIN_LEN 21
+
/* HE Capabilities Information defines */
#define HE_MACCAP_TWT_RESPONDER ((u8) BIT(2))
@@ -2490,10 +2618,17 @@ struct ieee80211_spatial_reuse {
#define HE_OPERATION_BSS_COLOR_OFFSET 24
#define HE_OPERATION_BSS_COLOR_MAX 64
+/* HE operation fields length */
+#define HE_OPERATION_ELEM_MIN_LEN 6
+#define HE_OPERATION_VHT_OPER_INFO_LEN 3
+#define HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN 1
+#define HE_OPERATION_6GHZ_OPER_INFO_LEN 5
+
/**
* enum he_reg_info_6ghz_ap_type - Allowed Access Point types for 6 GHz Band
*
- * IEEE P802.11-REVme/D4.0, Table E-12 (Regulatory Info subfield encoding)
+ * IEEE Std 802.11-2024, Table E-12 (Regulatory Info subfield interpretation by
+ * non-AP STAs with dot11ExtendedRegInfoSupport not set to true)
*/
enum he_reg_info_6ghz_ap_type {
HE_REG_INFO_6GHZ_AP_TYPE_INDOOR = 0,
@@ -2583,7 +2718,7 @@ struct ieee80211_he_mu_edca_parameter_set {
* transmit power is specified for the corresponding 20 MHz channel. */
#define RNR_20_MHZ_PSD_MAX_TXPOWER 127
-/* IEEE P802.11be/D5.0, Figure 9-704c - MLD Parameters subfield format */
+/* IEEE Std 802.11be-2024, Figure 9-734c - MLD Parameters subfield format */
/* B0..B7: AP MLD ID */
/* B8..B11: Link ID */
/* B12..B19: BSS Parameters Change Count */
@@ -2592,24 +2727,30 @@ struct ieee80211_he_mu_edca_parameter_set {
/* B21: Disabled Link Indication */
#define RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED 0x20
-/* IEEE P802.11be/D2.3, 9.4.2.311 - EHT Operation element */
+/* IEEE Std 802.11be-2024, 9.4.2.321 - EHT Operation element */
+#define EHT_OPERATION_ELEM_MIN_LEN 5
-/* Figure 9-1002b: EHT Operation Parameters field subfields */
+/* Figure 9-1074j: EHT Operation Parameters field format */
#define EHT_OPER_INFO_PRESENT BIT(0)
#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1)
#define EHT_OPER_DEFAULT_PE_DURATION BIT(2)
#define EHT_OPER_GROUP_ADDR_BU_INDICATION_LIMIT BIT(3)
#define EHT_OPER_GROUP_ADDR_BU_INDICATION_EXPONENT (BIT(4) | BIT(5))
+#define EHT_OPER_MCS15_DISABLE BIT(6)
#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE 2
-/* Control subfield: Channel Width subfield; see Table 9-401b */
+/* Control subfield: Channel Width subfield; see Table 9-417e (Channel width,
+ * CCFS0, and CCFS1 subfields) */
+#define EHT_OPER_CHANNEL_WIDTH_MASK 0x7
#define EHT_OPER_CHANNEL_WIDTH_20MHZ 0
#define EHT_OPER_CHANNEL_WIDTH_40MHZ 1
#define EHT_OPER_CHANNEL_WIDTH_80MHZ 2
#define EHT_OPER_CHANNEL_WIDTH_160MHZ 3
#define EHT_OPER_CHANNEL_WIDTH_320MHZ 4
-/* Figure 9-1002c: EHT Operation Information field format */
+/* Figure 9-1074k: EHT Operation Information field format */
+#define EHT_OPER_INFO_MIN_LEN 3
+
struct ieee80211_eht_oper_info {
u8 control; /* B0..B2: Channel Width */
u8 ccfs0;
@@ -2617,16 +2758,17 @@ struct ieee80211_eht_oper_info {
le16 disabled_chan_bitmap; /* 0 or 2 octets */
} STRUCT_PACKED;
-/* Figure 9-1002a: EHT Operation element format */
+/* Figure 9-1074i: EHT Operation element format */
struct ieee80211_eht_operation {
u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */
u8 basic_eht_mcs_nss_set[4];
struct ieee80211_eht_oper_info oper_info; /* 0 or 3 or 5 octets */
} STRUCT_PACKED;
-/* IEEE P802.11be/D1.5, 9.4.2.313 - EHT Capabilities element */
+/* IEEE Std 802.11be-2024, 9.4.2.323 - EHT Capabilities element */
+#define EHT_CAPABILITIES_ELEM_MIN_LEN 11
-/* Figure 9-1002af: EHT MAC Capabilities Information field */
+/* Figure 9-1074ap: EHT MAC Capabilities Information field format */
#define EHT_MACCAP_EPCS_PRIO BIT(0)
#define EHT_MACCAP_OM_CONTROL BIT(1)
#define EHT_MACCAP_TRIGGERED_TXOP_MODE1 BIT(2)
@@ -2639,7 +2781,7 @@ struct ieee80211_eht_operation {
#define EHT_MACCAP_MAX_MPDU_LEN_11454 BIT(7)
#define EHT_MACCAP_MAX_AMPDU_LEN_EXP_EXT BIT(8)
-/* Figure 9-1002ag: EHT PHY Capabilities Information field format
+/* Figure 9-1074aq: EHT PHY Capabilities Information field format
* _IDX indicates the octet index within the field */
#define EHT_PHY_CAPAB_LEN 9
@@ -2662,20 +2804,20 @@ struct ieee80211_eht_operation {
EHT_PHYCAP_MU_BEAMFORMER_160MHZ | \
EHT_PHYCAP_MU_BEAMFORMER_320MHZ)
-/* Figure 9-1002ah: Supported EHT-MCS and NSS Set field format */
+/* Figure 9-1074ar: Supported EHT-MCS and NSS Set field format */
#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY 4
#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS 3
#define EHT_MCS_NSS_CAPAB_LEN 9
/*
- * Figure 9-1002ak: EHT PPE Thresholds field format
+ * Figure 9-1074au: EHT PPE Thresholds field format
* Maximum PPE threshold length: 62 octets
* NSS: 4 bits (maximum NSS: 16), RU index: 5 bits, each pair: 6 bits
* 4 + 5 + 5 * 16 * 6 = 489 bits, Padding: 7 bits
*/
#define EHT_PPE_THRESH_CAPAB_LEN 62
-/* 9.4.2.313.5: EHT PPE Thresholds field */
+/* 9.4.2.323.5: EHT PPE Thresholds field */
#define EHT_PPE_THRES_NSS_SHIFT 0
#define EHT_PPE_THRES_NSS_MASK ((u8) (BIT(0) | BIT(1) | \
BIT(2) | BIT(3)))
@@ -2686,7 +2828,7 @@ struct ieee80211_eht_operation {
#define EHT_NSS_MAX_STREAMS 8
-/* Figure 9-1002ae: EHT Capabilities element format */
+/* Figure 9-1074ao: EHT Capabilities element format */
struct ieee80211_eht_capabilities {
/* EHT MAC Capabilities Information */
le16 mac_cap;
@@ -2698,13 +2840,13 @@ struct ieee80211_eht_capabilities {
#define IEEE80211_EHT_CAPAB_MIN_LEN (2 + 9)
-/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */
+/* IEEE Std P802.11be-2024, 9.4.2.322 - Multi-Link element */
-/* Figure 9-1002f: Multi-Link Control field */
+/* Figure 9-1074n: Multi-Link Control field */
#define MULTI_LINK_CONTROL_TYPE_MASK 0x07
#define MULTI_LINK_CONTROL_LEN 2
-/* Table 9-401c: Mult-Link element Type subfield encoding */
+/* Table 9-417f: [Multi-Link element] Type subfield encoding */
#define MULTI_LINK_CONTROL_TYPE_BASIC 0
#define MULTI_LINK_CONTROL_TYPE_PROBE_REQ 1
#define MULTI_LINK_CONTROL_TYPE_RECONF 2
@@ -2712,26 +2854,28 @@ struct ieee80211_eht_capabilities {
#define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS 4
/*
- * IEEE P802.11be/D2.2, Table 9-401c: Optional subelement IDs for Link Info
- * field of the Multi-Link element
+ * Table 9-417g: Optional subelement IDs for Link Info field of the Multi-Link
+ * element
*/
#define MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE 0
#define MULTI_LINK_SUB_ELEM_ID_VENDOR 221
#define MULTI_LINK_SUB_ELEM_ID_FRAGMENT 254
-/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */
+/* IEEE Std 802.11be-2024, 9.4.2.322.2 - Basic Multi-Link element */
-/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+/* Figure 9-1074o: Presence Bitmap subfield of the Basic Multi-Link element */
#define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID 0x0010
#define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT 0x0020
#define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO 0x0040
#define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0080
#define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0100
#define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID 0x0200
+#define BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP 0x0400
/*
* STA Control field definitions of Per-STA Profile subelement in Basic
- * Multi-Link element as described in Figure 9-1002n: STA Control field format.
+ * Multi-Link element as described in Figure 9-1074v: STA Control field format
+ * of the Basic Multi-Link element.
*/
#define BASIC_MLE_STA_CTRL_LEN 2
#define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F
@@ -2746,7 +2890,7 @@ struct ieee80211_eht_capabilities {
#define BASIC_MLE_STA_PROF_STA_MAC_IDX 3
-/* IEEE P802.11be/D2.2, 9.4.2.312.2.3 - Common Info field of the Basic
+/* IEEE Std 802.11be-2024, 9.4.2.322.2.3 - Common Info field of the Basic
* Multi-Link element */
struct eht_ml_basic_common_info {
u8 len;
@@ -2762,10 +2906,17 @@ struct eht_ml_basic_common_info {
* EML Capabilities: 2 octets
* MLD Capabilities and Operations: 2 octets
* AP MLD ID: 1 octet
+ * Extended MLD Capabilities And Operations: 2 octets
*/
u8 variable[];
} STRUCT_PACKED;
+/* Length of the EML Capabilities field in the Common Info field (in octets) */
+#define EHT_ML_EML_CAPA_LEN 2
+
+/* Length of the Link ID Info field in the Common Info field (in octets) */
+#define EHT_ML_LINK_ID_LEN 1
+
#define EHT_ML_LINK_ID_MSK 0x0f
#define EHT_ML_MEDIUM_SYNC_DELAY_DURATION 0x00ff
@@ -2776,39 +2927,29 @@ struct eht_ml_basic_common_info {
#define EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK 0x000e
#define EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK 0x0070
#define EHT_ML_EML_CAPA_EMLMR_SUPP 0x0080
-#define EHT_ML_EML_CAPA_EMLMR_DELAY_MASK 0x0700
#define EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK 0x7800
+#define EHT_ML_EML_CAPA_RESERVED 0x8700
#define EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK 0x000f
#define EHT_ML_MLD_CAPA_SRS_SUPP 0x0010
#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL 0x0020
#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE 0x0040
#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK 0x0060
+#define EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK 0x0080
#define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80
#define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000
+#define EHT_ML_MLD_CAPA_LINK_RECONF_OP_SUPPORT 0x2000
+#define EHT_ML_MLD_CAPA_ALIGNED_TWT_SUPPORT 0x4000
+#define EHT_ML_MLD_CAPA_RESERVED 0x8000
-#define EHT_PER_STA_CTRL_LINK_ID_MSK 0x000f
-#define EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK 0x0010
-#define EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK 0x0020
-#define EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK 0x0040
-#define EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK 0x0080
-#define EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK 0x0100
-#define EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK 0x0200
-#define EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK 0x0400
-#define EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK 0x0800
+#define EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001
+#define EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e
+#define EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020
+#define EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ONE_LINK 0x0040
+#define EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080
-/* IEEE P802.11be/D4.1, Figure 9-1001x - STA Control field format for the
- * Reconfiguration Multi-Link element */
-#define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f
-#define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010
-#define EHT_PER_STA_RECONF_CTRL_MAC_ADDR 0x0020
-#define EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER 0x0040
-#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780
-#define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800
-#define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000
-#define EHT_PER_STA_RECONF_CTRL_NSTR_INDIC_BITMAP 0x2000
-
-/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */
+/* IEEE Std 802.11be-2024, Figure 9-1074u - Per-STA Profile subelement format
+ * of the Basic Multi-Link element*/
struct ieee80211_eht_per_sta_profile {
le16 sta_control;
@@ -2816,11 +2957,14 @@ struct ieee80211_eht_per_sta_profile {
u8 variable[];
} STRUCT_PACKED;
-/* IEEE P802.11be/D4.0, 9.4.2.312.3 - Probe Request Multi-Link element
+/* IEEE Std 802.11be-2024, 9.4.2.322.3 - Probe Request Multi-Link element
+ * Figure 9-1074y - Presence Bitmap field format of the Probe Request
+ * Multi-Link element
* Presence Bitmap field is B4..B15 of the Multi-Link Control field, i.e.,
* B0 in the presence bitmap is B4 in the control field. */
#define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0010
+#define EHT_ML_PRES_BM_PROBE_REQ_MLD_MAC_ADDR 0x0020
struct eht_ml_probe_req_common_info {
u8 len;
@@ -2830,13 +2974,17 @@ struct eht_ml_probe_req_common_info {
* bitmap
*
* AP MLD ID: 1 octet
+ * MLD MAC Address: 6 cotets
*/
u8 variable[];
} STRUCT_PACKED;
-/* IEEE P802.11be/D4.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+/* IEEE Std 802.11be-2024, 9.4.2.322.4 - Reconfiguration Multi-Link element */
#define RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR 0x0001
+#define RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0002
+#define RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0004
+#define RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP 0x0008
#define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f
#define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010
@@ -2845,8 +2993,54 @@ struct eht_ml_probe_req_common_info {
#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780
#define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800
#define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000
+#define EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION 0x2000
+
+#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_VAL(x) ((x) >> 7)
+
+/* IEEE Std 802.11be-2024, Figure 9-1074ad - Common Info field format of the
+ * Reconfiguration Multi-Link element */
+struct eht_ml_reconf_common_info {
+ u8 len;
+
+ /*
+ * Followed by optional fields based on the multi link reconf presence
+ * bitmap
+ *
+ * MLD MAC Address: 6 octets
+ * EML Capabilities: 2 octets
+ * MLD Capabilities and Operations: 2 octets
+ * Extended MLD Capabilities and Operations: 2 octets
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
-/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
+/* IEEE Std 802.11be-2024, Table 9-417p - Reconfiguration Operation Type
+ * subfield encoding */
+enum ieee80211_eht_reconf_mle_op_type {
+ EHT_RECONF_TYPE_AP_REMOVAL = 0,
+ EHT_RECONF_TYPE_OP_PARAM_UPDATE = 1,
+ EHT_RECONF_TYPE_ADD_LINK = 2,
+ EHT_RECONF_TYPE_DELETE_LINK = 3,
+ EHT_RECONF_TYPE_NSTR_STATUS_UPDATE = 4,
+};
+
+/* IEEE Std 802.11be-2024, Figure 9-1074ag — STA Info field format for the
+ * Reconfiguration Multi-Link element */
+struct eht_ml_reconf_sta_info {
+ u8 len;
+
+ /* Followed by optional fields based on STA control presence bitmap.
+ *
+ * STA MAC Address: 6 octets
+ * AP Removal Timer: 2 octets
+ * Operation Parameters: 3 octets
+ * NSTR Indication Bitmap: 1 or 2 octets
+ */
+ u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11be-2024, 9.4.2.322.1 - Multi-Link element / General */
struct ieee80211_eht_ml {
le16 ml_control;
@@ -2855,13 +3049,32 @@ struct ieee80211_eht_ml {
u8 variable[];
} STRUCT_PACKED;
-/* Table 9-401c - Optional subelement IDs for Link Info field of the
- * Multi-Link element */
-enum ieee80211_eht_ml_sub_elem {
- EHT_ML_SUB_ELEM_PER_STA_PROFILE = 0,
- EHT_ML_SUB_ELEM_VENDOR = 221,
- EHT_ML_SUB_ELEM_FRAGMENT = 254,
-};
+/* IEEE Std 802.11be-2024, 9.4.2.329 (Bandwidth Indication element) defines the
+ * Bandwidth Indication Information field to have the same definition as the
+ * EHT Operation Information field in the EHT Operation element.
+ */
+struct ieee80211_bw_ind_info {
+ u8 control; /* B0..B2: Channel Width */
+ u8 ccfs0;
+ u8 ccfs1;
+ le16 disabled_chan_bitmap; /* 0 or 2 octets */
+} STRUCT_PACKED;
+
+/* Control subfield: Channel Width subfield; see Table 9-417e (Channel width,
+ * CCFS0, and CCFS1 subfields) in IEEE Std 802.11be-2024. */
+#define BW_IND_CHANNEL_WIDTH_20MHZ EHT_OPER_CHANNEL_WIDTH_20MHZ
+#define BW_IND_CHANNEL_WIDTH_40MHZ EHT_OPER_CHANNEL_WIDTH_40MHZ
+#define BW_IND_CHANNEL_WIDTH_80MHZ EHT_OPER_CHANNEL_WIDTH_80MHZ
+#define BW_IND_CHANNEL_WIDTH_160MHZ EHT_OPER_CHANNEL_WIDTH_160MHZ
+#define BW_IND_CHANNEL_WIDTH_320MHZ EHT_OPER_CHANNEL_WIDTH_320MHZ
+
+/* IEEE Std 802.11be-2024, 9.4.2.329 (Bandwidth Indication element) */
+struct ieee80211_bw_ind_element {
+ u8 bw_ind_params; /* Bandwidth Indication Parameters */
+ struct ieee80211_bw_ind_info bw_ind_info; /* 3 or 5 octets */
+} STRUCT_PACKED;
+
+#define BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1)
/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
#define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6
@@ -2915,9 +3128,20 @@ enum scs_request_type {
SCS_REQ_CHANGE = 2,
};
+/* IEEE Std 802.11be-2024, 9.4.2.324 (TID-To-Link Mapping element)
+ * Figure 9-1074ax (TID-TO-Link Mapping Control field format) */
+#define EHT_TID_TO_LINK_MAP_DIRECTION_DOWN 0
+#define EHT_TID_TO_LINK_MAP_DIRECTION_UP 1
+#define EHT_TID_TO_LINK_MAP_DIRECTION_BOTH 2
+#define EHT_TID_TO_LINK_MAP_DIRECTION_MSK 0x03
+#define EHT_TID_TO_LINK_MAP_DEFAULT 0x04
+#define EHT_TID_TO_LINK_MAP_SWITCH_TIME_PRESENT 0x08
+#define EHT_TID_TO_LINK_MAP_EXPECT_DUR_PRESENT 0x10
+#define EHT_TID_TO_LINK_MAP_LINK_MAPPING_SIZE 0x20
+
/*
- * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
- * Table 9-404s (Direction subfield encoding)
+ * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element,
+ * Table 9-417w (Direction subfield encoding)
*/
enum scs_direction {
SCS_DIRECTION_UP = 0,
@@ -2926,8 +3150,8 @@ enum scs_direction {
};
/*
- * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
- * Figure 9-1001av (Control Info field format)
+ * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element,
+ * Figure 9-1074bd (Control Info field format)
*/
#define EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET 0
#define EHT_QOS_CONTROL_INFO_TID_OFFSET 2
@@ -2936,7 +3160,7 @@ enum scs_direction {
#define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25
/*
- * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
+ * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element,
* Presence Bitmap Of Additional Parameters
*/
#define SCS_QOS_BIT_MAX_MSDU_SIZE ((u16) BIT(0))
@@ -3040,6 +3264,15 @@ enum dscp_policy_request_type {
#define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1)
#define WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC BIT(2)
+/* Wi-Fi Alliance Capabilities attributes */
+enum wfa_capa_attr_id {
+ WFA_CAPA_ATTR_GENERATIONAL_CAPAB = 1,
+ WFA_CAPA_ATTR_VENDOR_SPECIFIC = 221,
+};
+
+/* Wi-Fi Alliance Capabilities frame */
+#define WFA_CAPAB_VENDOR_TYPE 0x506f9a1b
+
struct ieee80211_neighbor_ap_info {
u8 tbtt_info_hdr;
u8 tbtt_info_len;
diff --git a/contrib/wpa/src/common/nan.h b/contrib/wpa/src/common/nan.h
deleted file mode 100644
index 19ab7468711e..000000000000
--- a/contrib/wpa/src/common/nan.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * NAN (Wi-Fi Aware) definitions
- * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef NAN_H
-#define NAN_H
-
-enum nan_attr_id {
- NAN_ATTR_MASTER_INDICATION = 0x00,
- NAN_ATTR_CLUSTER = 0x01,
- NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST = 0x02,
- NAN_ATTR_SDA = 0x03, /* Service Descriptor attribute */
- NAN_ATTR_CONN_CAPA = 0x04,
- NAN_ATTR_WLAN_INFRA = 0x05,
- NAN_ATTR_P2P_OPER = 0x06,
- NAN_ATTR_IBSS = 0x07,
- NAN_ATTR_MESH = 0x08,
- NAN_ATTR_FURTHER_NAN_SD = 0x09,
- NAN_ATTR_FURTHER_AVAIL_MAP = 0x0A,
- NAN_ATTR_COUNTRY_CODE = 0x0B,
- NAN_ATTR_RANGIN = 0x0C,
- NAN_ATTR_CLUSTER_DISCOVERY = 0x0D,
- NAN_ATTR_SDEA = 0x0E, /* Service Descriptor Extension attribute */
- NAN_ATTR_DEVICE_CAPABILITY = 0x0F,
- NAN_ATTR_NDP = 0x10,
- NAN_ATTR_NAN_AVAILABILITY = 0x12,
- NAN_ATTR_NDC = 0x13,
- NAN_ATTR_NDL = 0x14,
- NAN_ATTR_NDL_QOS = 0x15,
- NAN_ATTR_UNALIGNED_SCHEDULE = 0x17,
- NAN_ATTR_RANGING_INFO = 0x1A,
- NAN_ATTR_RANGING_SETUP = 0x1B,
- NAN_ATTR_FTM_RANGING_REPORT = 0x1C,
- NAN_ATTR_ELEM_CONTAINER = 0x1D,
- NAN_ATTR_EXT_WLAN_INFRA = 0x1E,
- NAN_ATTR_EXT_P2P_OPER = 0x1FE,
- NAN_ATTR_EXT_IBSS = 0x20,
- NAN_ATTR_EXT_MESH = 0x21,
- NAN_ATTR_CSIA = 0x22, /* Cipher Suite Info attribute */
- NAN_ATTR_SCIA = 0x23, /* Security Context Info attribute */
- NAN_ATTR_SHARED_KEY_DESCR = 0x24,
- NAN_ATTR_PUBLIC_AVAILABILITY = 0x27,
- NAN_ATTR_SUBSC_SERVICE_ID_LIST = 0x28,
- NAN_ATTR_NDP_EXT = 0x29,
- NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */
- NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */
- NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
- NAN_ATTR_S3 = 0x2D,
- NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
- NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
-};
-
-/* Service Descriptor attribute (SDA) */
-
-/* Service Control field */
-#define NAN_SRV_CTRL_TYPE_MASK (BIT(0) | BIT(1))
-#define NAN_SRV_CTRL_MATCHING_FILTER BIT(2)
-#define NAN_SRV_CTRL_RESP_FILTER BIT(3)
-#define NAN_SRV_CTRL_SRV_INFO BIT(4)
-#define NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED BIT(5)
-#define NAN_SRV_CTRL_BINDING_BITMAP BIT(6)
-
-enum nan_service_control_type {
- NAN_SRV_CTRL_PUBLISH = 0,
- NAN_SRV_CTRL_SUBSCRIBE = 1,
- NAN_SRV_CTRL_FOLLOW_UP = 2,
-};
-
-/* Service Descriptor Extension attribute (SDEA) */
-
-/* SDEA Control field */
-#define NAN_SDEA_CTRL_FSD_REQ BIT(0)
-#define NAN_SDEA_CTRL_FSD_GAS BIT(1)
-#define NAN_SDEA_CTRL_DATA_PATH_REQ BIT(2)
-#define NAN_SDEA_CTRL_DATA_PATH_TYPE BIT(3)
-#define NAN_SDEA_CTRL_QOS_REQ BIT(5)
-#define NAN_SDEA_CTRL_SECURITY_REQ BIT(6)
-#define NAN_SDEA_CTRL_RANGING_REQ BIT(7)
-#define NAN_SDEA_CTRL_RANGE_LIMIT BIT(8)
-#define NAN_SDEA_CTRL_SRV_UPD_INDIC BIT(9)
-#define NAN_SDEA_CTRL_GTK_REQ BIT(10)
-
-enum nan_service_protocol_type {
- NAN_SRV_PROTO_BONJOUR = 1,
- NAN_SRV_PROTO_GENERIC = 2,
- NAN_SRV_PROTO_CSA_MATTER = 3,
-};
-
-#define NAN_ATTR_HDR_LEN 3
-#define NAN_SERVICE_ID_LEN 6
-
-#define NAN_USD_DEFAULT_FREQ 2437
-
-#endif /* NAN_H */
diff --git a/contrib/wpa/src/common/nan_de.c b/contrib/wpa/src/common/nan_de.c
index 12fad3112bdc..42a61c49bc76 100644
--- a/contrib/wpa/src/common/nan_de.c
+++ b/contrib/wpa/src/common/nan_de.c
@@ -1,6 +1,6 @@
/*
* NAN Discovery Engine
- * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,22 +10,29 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "utils/crc32.h"
+#include "utils/list.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "ieee802_11_defs.h"
-#include "nan.h"
+#include "nan/nan.h"
+#include "nan_defs.h"
#include "nan_de.h"
-static const u8 nan_network_id[ETH_ALEN] =
+const u8 nan_network_id[ETH_ALEN] =
{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
-static const u8 wildcard_bssid[ETH_ALEN] =
-{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
enum nan_de_service_type {
NAN_DE_PUBLISH,
NAN_DE_SUBSCRIBE,
};
+const u8 p2p_network_id[ETH_ALEN] =
+{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
+
+static const u8 wildcard_bssid[ETH_ALEN] =
+{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
struct nan_de_service {
int id;
enum nan_de_service_type type;
@@ -40,11 +47,16 @@ struct nan_de_service {
struct os_reltime end_time;
struct os_reltime last_multicast;
struct os_reltime first_discovered;
- struct os_reltime last_followup;
bool needs_fsd;
unsigned int freq;
unsigned int default_freq;
int *freq_list;
+ u8 a3[ETH_ALEN];
+ bool a3_set;
+
+ /* Source MAC address for this service (optional) */
+ u8 forced_addr[ETH_ALEN];
+ bool forced_addr_set;
/* pauseState information for Publish function */
struct os_reltime pause_state_end;
@@ -58,11 +70,62 @@ struct nan_de_service {
struct os_reltime next_publish_state;
struct os_reltime next_publish_chan;
unsigned int next_publish_duration;
+ bool is_p2p;
+ bool is_pr;
+ bool listen_stopped;
+ bool sync;
+
+ /* Filters */
+ struct wpabuf *matching_filter_tx;
+ struct wpabuf *matching_filter_rx;
+
+ bool srf_include;
+ bool srf_type_bloom_filter;
+ u8 srf_bf_idx;
+ struct wpabuf *srf;
+ bool close_proximity;
+ bool gtk_required;
+ bool data_path;
+ bool security_required;
+
+ /* Bootstrapping methods */
+ u16 pbm;
+
+ /* For Publish - int_array of supported cipher suites */
+ int *cipher_suites_list;
+
+ /* Bitmap of NAN_CS_INFO_CAPA_* */
+ u8 security_capab;
+
+ /* PMKID list for this service */
+ struct dl_list pmkid_list;
+};
+
+#define NAN_DE_N_MIN 5
+#define NAN_DE_N_MAX 10
+
+#define NAN_DE_RSSI_CLOSE_PROXIMITY (-70) /* dBm */
+
+struct nan_de_tracked_tx {
+ struct dl_list list;
+ u8 dst[ETH_ALEN];
+ u32 cookie;
+ u8 digest[SHA256_MAC_LEN];
+ bool with_wait;
+};
+
+enum nan_de_flush_tracked_tx_reason {
+ NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL,
+ NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED,
};
struct nan_de {
u8 nmi[ETH_ALEN];
+ u8 cluster_id[ETH_ALEN];
+ bool cluster_id_set;
+ bool offload;
bool ap;
+ unsigned int max_listen;
struct nan_callbacks cb;
struct nan_de_service *service[NAN_DE_MAX_SERVICE];
@@ -74,10 +137,47 @@ struct nan_de {
unsigned int listen_freq;
unsigned int tx_wait_status_freq;
unsigned int tx_wait_end_freq;
+
+ struct nan_de_cfg cfg;
+ struct os_reltime suspend_cycle_start;
+
+ int dw_freq;
+
+ /* RSSI threshold for close proximity, or zero if not limited */
+ int rssi_threshold;
+
+ /*
+ * List of transmit requests (struct nan_de_tracked_tx::list) for which
+ * the caller requested status indicating whether the frame was
+ * acknowledged
+ */
+ struct dl_list tracked_tx;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * When set, multicast follow-up SDFs will be sent as Protected Dual of
+ * Public Action frames. This can be used to test protection of NAN
+ * multicast Management frames.
+ */
+ bool tx_mcast_follow_up_prot;
+#endif /* CONFIG_TESTING_OPTIONS */
};
-struct nan_de * nan_de_init(const u8 *nmi, bool ap,
+bool nan_de_is_nan_network_id(const u8 *addr)
+{
+ return ether_addr_equal(addr, nan_network_id);
+}
+
+
+bool nan_de_is_p2p_network_id(const u8 *addr)
+{
+ return ether_addr_equal(addr, p2p_network_id);
+}
+
+
+struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+ unsigned int max_listen,
const struct nan_callbacks *cb)
{
struct nan_de *de;
@@ -87,9 +187,17 @@ struct nan_de * nan_de_init(const u8 *nmi, bool ap,
return NULL;
os_memcpy(de->nmi, nmi, ETH_ALEN);
+ de->offload = offload;
de->ap = ap;
+ de->max_listen = max_listen ? max_listen : 1000;
os_memcpy(&de->cb, cb, sizeof(*cb));
+ de->cfg.n_min = NAN_DE_N_MIN;
+ de->cfg.n_max = NAN_DE_N_MAX;
+
+ de->rssi_threshold = NAN_DE_RSSI_CLOSE_PROXIMITY;
+ dl_list_init(&de->tracked_tx);
+
return de;
}
@@ -99,7 +207,14 @@ static void nan_de_service_free(struct nan_de_service *srv)
os_free(srv->service_name);
wpabuf_free(srv->ssi);
wpabuf_free(srv->elems);
+ wpabuf_free(srv->matching_filter_tx);
+ wpabuf_free(srv->matching_filter_rx);
+ wpabuf_free(srv->srf);
os_free(srv->freq_list);
+ os_free(srv->cipher_suites_list);
+#ifdef CONFIG_NAN
+ nan_crypto_clear_pmkid_list(&srv->pmkid_list);
+#endif /* CONFIG_NAN */
os_free(srv);
}
@@ -117,14 +232,136 @@ static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv,
}
+static void nan_de_flush_tracked_tx(struct nan_de *de,
+ enum nan_de_flush_tracked_tx_reason reason)
+{
+ struct nan_de_tracked_tx *tx, *tmp;
+
+ dl_list_for_each_safe(tx, tmp, &de->tracked_tx,
+ struct nan_de_tracked_tx, list) {
+ if (reason == NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED &&
+ !tx->with_wait)
+ continue;
+
+ de->cb.transmit_req_status(de->cb.ctx, tx->cookie, false);
+ dl_list_del(&tx->list);
+ os_free(tx);
+ }
+}
+
+
static void nan_de_clear_pending(struct nan_de *de)
{
+ nan_de_flush_tracked_tx(de, NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL);
+
de->listen_freq = 0;
de->tx_wait_status_freq = 0;
de->tx_wait_end_freq = 0;
}
+static int nan_de_track_tx_digest(const u8 *data, size_t len, u8 *digest)
+{
+ return sha256_vector(1, &data, &len, digest);
+}
+
+
+static struct nan_de_tracked_tx *
+nan_de_add_tracked_tx(struct nan_de *de, const u8 *dst, bool with_wait,
+ u32 cookie, const struct wpabuf *buf)
+{
+ struct nan_de_tracked_tx *tx;
+ u8 digest[SHA256_MAC_LEN];
+
+ if (!de->cb.transmit_req_status) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: No tx_status callback, cannot track Tx");
+ return NULL;
+ }
+
+ if (!cookie) {
+ wpa_printf(MSG_DEBUG, "NAN: Invalid cookie for Tx tracking");
+ return NULL;
+ }
+
+ if (nan_de_track_tx_digest(wpabuf_head(buf), wpabuf_len(buf), digest)) {
+ wpa_printf(MSG_INFO, "NAN: Failed to compute Tx digest");
+ return NULL;
+ }
+
+ dl_list_for_each(tx, &de->tracked_tx, struct nan_de_tracked_tx, list) {
+ if (!ether_addr_equal(tx->dst, dst))
+ continue;
+
+ if (tx->cookie == cookie) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Already tracking Tx cookie %u to "
+ MACSTR, cookie, MAC2STR(dst));
+ return NULL;
+ }
+
+ if (os_memcmp(tx->digest, digest, SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Already tracking identical payload to "
+ MACSTR " (cookie %u)",
+ MAC2STR(dst), tx->cookie);
+ return NULL;
+ }
+ }
+
+ tx = os_zalloc(sizeof(*tx));
+ if (!tx)
+ return NULL;
+
+ os_memcpy(tx->dst, dst, ETH_ALEN);
+ tx->cookie = cookie;
+ tx->with_wait = with_wait;
+ os_memcpy(tx->digest, digest, SHA256_MAC_LEN);
+
+ dl_list_add(&de->tracked_tx, &tx->list);
+
+ wpa_printf(MSG_DEBUG, "NAN: Track Tx cookie %u", tx->cookie);
+ wpa_hexdump(MSG_DEBUG, "NAN: Track Tx digest",
+ tx->digest, SHA256_MAC_LEN);
+
+ return tx;
+}
+
+
+static void nan_de_tx_status_match(struct nan_de *de, const u8 *data,
+ size_t len, u8 acked)
+{
+ struct nan_de_tracked_tx *tx;
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+ const u8 *pos = (const u8 *) &mgmt->u.action;
+ u8 digest[SHA256_MAC_LEN];
+
+ if (len <= offsetof(struct ieee80211_mgmt, u.action))
+ return;
+
+ len = data + len - pos;
+
+ if (nan_de_track_tx_digest(pos, len, digest))
+ return;
+
+ dl_list_for_each(tx, &de->tracked_tx,
+ struct nan_de_tracked_tx, list) {
+ if (!ether_addr_equal(tx->dst, mgmt->da) ||
+ os_memcmp(tx->digest, digest, SHA256_MAC_LEN) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "NAN: Tx status for cookie=%u ack=%u",
+ tx->cookie, acked);
+
+ de->cb.transmit_req_status(de->cb.ctx, tx->cookie, acked);
+ dl_list_del(&tx->list);
+ os_free(tx);
+ return;
+ }
+}
+
+
void nan_de_flush(struct nan_de *de)
{
unsigned int i;
@@ -149,6 +386,9 @@ static void nan_de_pause_state(struct nan_de_service *srv, const u8 *peer_addr,
wpa_printf(MSG_DEBUG, "NAN: Start pauseState");
os_get_reltime(&srv->pause_state_end);
srv->pause_state_end.sec += 60;
+ if (os_reltime_initialized(&srv->end_time) &&
+ os_reltime_before(&srv->end_time, &srv->pause_state_end))
+ srv->pause_state_end = srv->end_time;
os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN);
srv->sel_peer_id = peer_id;
}
@@ -164,13 +404,29 @@ static void nan_de_unpause_state(struct nan_de_service *srv)
}
-static struct wpabuf * nan_de_alloc_sdf(size_t len)
+static struct wpabuf * nan_de_alloc_sdf(struct nan_de *de, const u8 *dst,
+ size_t len,
+ enum nan_service_control_type type)
{
struct wpabuf *buf;
+ u8 category = WLAN_ACTION_PUBLIC;
+
+ if (de->cb.is_peer_paired && de->cb.is_peer_paired(de->cb.ctx, dst))
+ category = WLAN_ACTION_PROTECTED_DUAL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (de->tx_mcast_follow_up_prot &&
+ is_multicast_ether_addr(dst) &&
+ type == NAN_SRV_CTRL_FOLLOW_UP) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Send multicast follow-up as protected");
+ category = WLAN_ACTION_PROTECTED_DUAL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
buf = wpabuf_alloc(2 + 4 + len);
if (buf) {
- wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, category);
wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
wpabuf_put_be32(buf, NAN_SDF_VENDOR_TYPE);
}
@@ -182,16 +438,29 @@ static struct wpabuf * nan_de_alloc_sdf(size_t len)
static int nan_de_tx(struct nan_de *de, unsigned int freq,
unsigned int wait_time,
const u8 *dst, const u8 *src, const u8 *bssid,
- const struct wpabuf *buf)
+ const struct wpabuf *buf, u32 *cookie)
{
+ struct nan_de_tracked_tx *tracked_tx = NULL;
int res;
if (!de->cb.tx)
return -1;
+ if (cookie) {
+ tracked_tx = nan_de_add_tracked_tx(de, dst, !!wait_time,
+ *cookie, buf);
+ if (!tracked_tx)
+ return -1;
+ }
+
res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf);
- if (res < 0)
+ if (res < 0) {
+ if (tracked_tx) {
+ dl_list_del(&tracked_tx->list);
+ os_free(tracked_tx);
+ }
return res;
+ }
de->tx_wait_status_freq = freq;
de->tx_wait_end_freq = wait_time ? freq : 0;
@@ -200,19 +469,57 @@ static int nan_de_tx(struct nan_de *de, unsigned int freq,
}
+static void nan_buf_add_npba(const struct nan_de *de,
+ const struct nan_de_service *srv,
+ struct wpabuf *buf)
+{
+ u8 type_and_status = NAN_PBA_TYPE_ADVERTISE |
+ (NAN_PBA_STATUS_ACCEPTED << NAN_PBA_STATUS_POS);
+
+ wpa_printf(MSG_DEBUG, "NAN: Add NPBA");
+
+ wpabuf_put_u8(buf, NAN_ATTR_NPBA);
+ wpabuf_put_le16(buf, 5);
+
+ /* Dialog token is reserved (0) for advertise */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_u8(buf, type_and_status);
+ wpabuf_put_u8(buf, NAN_REASON_RESERVED);
+ wpabuf_put_le16(buf, srv->pbm);
+}
+
+
static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
unsigned int wait_time,
enum nan_service_control_type type,
- const u8 *dst, u8 req_instance_id,
- const struct wpabuf *ssi)
+ const u8 *dst, const u8 *a3, u8 req_instance_id,
+ const struct wpabuf *ssi,
+ const struct wpabuf *attrs, u32 *cookie)
{
struct wpabuf *buf;
size_t len = 0, sda_len, sdea_len;
u8 ctrl = type;
u16 sdea_ctrl = 0;
+ const u8 *forced_addr;
+ size_t cs_num = int_array_len(srv->cipher_suites_list);
/* Service Descriptor attribute */
sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1;
+ if (srv->matching_filter_tx && wpabuf_len(srv->matching_filter_tx)) {
+ sda_len += wpabuf_len(srv->matching_filter_tx) + 1;
+ ctrl |= NAN_SRV_CTRL_MATCHING_FILTER;
+ }
+
+ if (srv->srf && wpabuf_len(srv->srf)) {
+ /* SRF length + SRF control */
+ sda_len += 1 + 1 + wpabuf_len(srv->srf);
+ ctrl |= NAN_SRV_CTRL_RESP_FILTER;
+ }
+
+ if ((srv->type == NAN_DE_SUBSCRIBE || srv->type == NAN_DE_PUBLISH) &&
+ srv->close_proximity)
+ ctrl |= NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED;
+
len += NAN_ATTR_HDR_LEN + sda_len;
/* Service Descriptor Extension attribute */
@@ -225,7 +532,32 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
if (srv->elems)
len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
- buf = nan_de_alloc_sdf(len);
+ /* NPBA (dialog token, type and status, reason, pbm) */
+ if (srv->pbm && type != NAN_SRV_CTRL_FOLLOW_UP)
+ len += NAN_ATTR_HDR_LEN + 1 + 1 + 1 + 2;
+
+ /* Reserve some additional space for extra attributes */
+ if (de->cb.add_extra_attrs)
+ len += 256;
+
+ len += attrs ? wpabuf_len(attrs) : 0;
+
+ /* Cipher Suite Information Attribute */
+ if (srv->type == NAN_DE_PUBLISH && srv->cipher_suites_list) {
+ len += NAN_ATTR_HDR_LEN + sizeof(struct nan_cipher_suite_info) +
+ cs_num * sizeof(struct nan_cipher_suite);
+ }
+
+ /* Security Context Information Attribute */
+ if (srv->type == NAN_DE_PUBLISH && !dl_list_empty(&srv->pmkid_list)) {
+ unsigned int list_len = dl_list_len(&srv->pmkid_list);
+
+ /* Each entry: sizeof(nan_sec_ctxt) + PMKID_LEN */
+ len += NAN_ATTR_HDR_LEN +
+ list_len * (sizeof(struct nan_sec_ctxt) + PMKID_LEN);
+ }
+
+ buf = nan_de_alloc_sdf(de, dst, len, type);
if (!buf)
return;
@@ -237,23 +569,55 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
wpabuf_put_u8(buf, req_instance_id); /* Requestor Instance ID */
wpabuf_put_u8(buf, ctrl);
+ if (ctrl & NAN_SRV_CTRL_MATCHING_FILTER) {
+ wpabuf_put_u8(buf, wpabuf_len(srv->matching_filter_tx));
+ wpabuf_put_buf(buf, srv->matching_filter_tx);
+ }
+
+ if (ctrl & NAN_SRV_CTRL_RESP_FILTER) {
+ u8 srf_ctrl = 0;
+
+ if (srv->srf_type_bloom_filter)
+ srf_ctrl = NAN_SRF_CTRL_BF;
+
+ if (srv->srf_include)
+ srf_ctrl |= NAN_SRF_CTRL_INCLUDE;
+
+ srf_ctrl |= (srv->srf_bf_idx & NAN_SRF_CTRL_BF_IDX_MSK) <<
+ NAN_SRF_CTRL_BF_IDX_POS;
+ wpabuf_put_u8(buf, wpabuf_len(srv->srf) + 1);
+ wpabuf_put_u8(buf, srf_ctrl);
+ wpabuf_put_buf(buf, srv->srf);
+ }
+
/* Service Descriptor Extension attribute */
if (srv->type == NAN_DE_PUBLISH || ssi) {
- wpabuf_put_u8(buf, NAN_ATTR_SDEA);
- wpabuf_put_le16(buf, sdea_len);
- wpabuf_put_u8(buf, srv->id); /* Instance ID */
if (srv->type == NAN_DE_PUBLISH) {
if (srv->publish.fsd)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ;
if (srv->publish.fsd_gas)
sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS;
+ if (srv->gtk_required)
+ sdea_ctrl |= NAN_SDEA_CTRL_GTK_REQ;
+ if (srv->data_path) {
+ sdea_ctrl |= NAN_SDEA_CTRL_DATA_PATH_REQ;
+ if (srv->cipher_suites_list &&
+ srv->security_required)
+ sdea_ctrl |= NAN_SDEA_CTRL_SECURITY_REQ;
+ }
}
- wpabuf_put_le16(buf, sdea_ctrl);
- if (ssi) {
- wpabuf_put_le16(buf, 4 + wpabuf_len(ssi));
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, srv->srv_proto_type);
- wpabuf_put_buf(buf, ssi);
+
+ if (sdea_ctrl || ssi) {
+ wpabuf_put_u8(buf, NAN_ATTR_SDEA);
+ wpabuf_put_le16(buf, sdea_len);
+ wpabuf_put_u8(buf, srv->id); /* Instance ID */
+ wpabuf_put_le16(buf, sdea_ctrl);
+ if (ssi) {
+ wpabuf_put_le16(buf, 4 + wpabuf_len(ssi));
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, srv->srv_proto_type);
+ wpabuf_put_buf(buf, ssi);
+ }
}
}
@@ -265,10 +629,53 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
wpabuf_put_buf(buf, srv->elems);
}
- /* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
- * but there is no synchronization in USD as as such, no NAN Cluster
- * either. Use Wildcard BSSID instead. */
- nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, wildcard_bssid, buf);
+ /* Use per-service source address if configured, otherwise use NMI */
+ forced_addr = srv->forced_addr_set ? srv->forced_addr : de->nmi;
+
+ if (srv->pbm && type != NAN_SRV_CTRL_FOLLOW_UP)
+ nan_buf_add_npba(de, srv, buf);
+
+ if (de->cb.add_extra_attrs)
+ de->cb.add_extra_attrs(de->cb.ctx, buf);
+
+ if (attrs) {
+ wpa_printf(MSG_DEBUG, "NAN: Add extra NAN attributes");
+ wpabuf_put_buf(buf, attrs);
+ }
+
+ if (srv->type == NAN_DE_PUBLISH && srv->cipher_suites_list) {
+ size_t i;
+
+ wpabuf_put_u8(buf, NAN_ATTR_CSIA);
+ wpabuf_put_le16(buf, sizeof(struct nan_cipher_suite_info) +
+ cs_num * sizeof(struct nan_cipher_suite));
+ wpabuf_put_u8(buf, srv->security_capab);
+ for (i = 0; i < cs_num; i++) {
+ wpabuf_put_u8(buf, (u8) srv->cipher_suites_list[i]);
+ wpabuf_put_u8(buf, srv->id);
+ }
+ }
+
+ if (srv->type == NAN_DE_PUBLISH && !dl_list_empty(&srv->pmkid_list)) {
+ struct nan_de_pmkid *pmkid;
+ u8 *len_ptr;
+
+ wpabuf_put_u8(buf, NAN_ATTR_SCIA);
+ len_ptr = wpabuf_put(buf, 2); /* length filled later */
+
+ dl_list_for_each(pmkid, &srv->pmkid_list, struct nan_de_pmkid,
+ list) {
+ wpabuf_put_le16(buf, PMKID_LEN);
+ wpabuf_put_u8(buf, NAN_SEC_CTX_TYPE_ND_PMKID);
+ wpabuf_put_u8(buf, srv->id);
+ wpabuf_put_data(buf, pmkid->pmkid, PMKID_LEN);
+ }
+
+ WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2);
+ }
+
+ nan_de_tx(de, srv->sync ? 0 : srv->freq, srv->sync ? 0 : wait_time,
+ dst, forced_addr, a3, buf, cookie);
wpabuf_free(buf);
}
@@ -332,25 +739,48 @@ static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv,
{
enum nan_service_control_type type;
unsigned int wait_time = 100;
+ const u8 *network_id;
+ const u8 *bssid;
if (srv->type == NAN_DE_PUBLISH) {
int ms;
type = NAN_SRV_CTRL_PUBLISH;
- nan_de_check_chan_change(srv);
- ms = nan_de_time_to_next_chan_change(srv);
- if (ms < 100)
- ms = 100;
- wait_time = ms;
+ if (!srv->sync) {
+ nan_de_check_chan_change(srv);
+ ms = nan_de_time_to_next_chan_change(srv);
+ if (ms < 100)
+ ms = 100;
+ wait_time = ms;
+ }
} else if (srv->type == NAN_DE_SUBSCRIBE) {
type = NAN_SRV_CTRL_SUBSCRIBE;
} else {
return;
}
- nan_de_tx_sdf(de, srv, wait_time, type, nan_network_id,
- req_instance_id, srv->ssi);
+ if (srv->is_p2p) {
+ network_id = p2p_network_id;
+ bssid = wildcard_bssid;
+ } else {
+ network_id = nan_network_id;
+ bssid = nan_network_id;
+ }
+
+ if (srv->sync) {
+ if (!de->cluster_id_set || !de->dw_freq) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Cluster ID or DW frequency are not set - skip sync TX");
+ return;
+ }
+
+ wait_time = 0;
+ bssid = de->cluster_id;
+ }
+
+ nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid,
+ req_instance_id, srv->ssi, NULL, NULL);
os_get_reltime(&srv->last_multicast);
}
@@ -395,11 +825,6 @@ static bool nan_de_srv_expired(struct nan_de_service *srv,
return false;
if (!srv->publish.fsd)
return true;
- if (os_reltime_initialized(&srv->last_followup) &&
- !os_reltime_expired(now, &srv->last_followup, 1))
- return false;
- if (os_reltime_expired(now, &srv->last_multicast, 1))
- return true;
}
if (srv->type == NAN_DE_SUBSCRIBE) {
@@ -409,11 +834,6 @@ static bool nan_de_srv_expired(struct nan_de_service *srv,
return false;
if (!srv->needs_fsd)
return true;
- if (os_reltime_initialized(&srv->last_followup) &&
- !os_reltime_expired(now, &srv->last_followup, 1))
- return false;
- if (os_reltime_expired(now, &srv->first_discovered, 1))
- return true;
}
return false;
@@ -478,6 +898,16 @@ static int nan_de_srv_time_to_next(struct nan_de *de,
next = tmp;
}
+ if (srv->type == NAN_DE_PUBLISH &&
+ srv->publish.fsd &&
+ os_reltime_initialized(&srv->pause_state_end)) {
+ os_reltime_sub(&srv->pause_state_end, now, &diff);
+ tmp = os_reltime_in_ms(&diff);
+ if (next == -1 || tmp < next)
+ next = tmp;
+ return next;
+ }
+
tmp = nan_de_next_multicast(de, srv, now);
if (tmp >= 0 && (next == -1 || tmp < next))
next = tmp;
@@ -513,19 +943,22 @@ static int nan_de_srv_time_to_next(struct nan_de *de,
}
-static void nan_de_start_new_publish_state(struct nan_de_service *srv,
+static void nan_de_start_new_publish_state(struct nan_de *de,
+ struct nan_de_service *srv,
bool force_single)
{
unsigned int n;
+ if (srv->sync)
+ return;
+
if (force_single || !srv->freq_list || srv->freq_list[0] == 0)
srv->in_multi_chan = false;
else
srv->in_multi_chan = !srv->in_multi_chan;
- /* Use hardcoded Nmin=5 and Nmax=10 and pick a random N from that range.
- * Use same values for M. */
- n = 5 + os_random() % 5;
+ /* Use same values for N and M. */
+ n = de->cfg.n_min + os_random() % (de->cfg.n_max - de->cfg.n_min);
srv->next_publish_duration = n * 100;
nan_de_set_publish_times(srv);
@@ -550,6 +983,26 @@ static void nan_de_start_new_publish_state(struct nan_de_service *srv,
}
+static u32 nan_de_listen_duration(struct nan_de *de, struct nan_de_service *srv)
+{
+ u32 duration = 1000;
+ u32 max_duration = de->max_listen;
+
+ /* Limit the listen duration based on the maximal 'N' value */
+ if (de->cfg.n_max && de->cfg.n_max * 100 < max_duration)
+ max_duration = de->cfg.n_max * 100;
+
+ if (srv->type == NAN_DE_PUBLISH) {
+ nan_de_check_chan_change(srv);
+ duration = nan_de_time_to_next_chan_change(srv);
+ if (duration < 150)
+ duration = 150;
+ }
+
+ return MIN(duration, max_duration);
+}
+
+
static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
{
struct nan_de *de = eloop_ctx;
@@ -560,6 +1013,55 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
os_get_reltime(&now);
+ /* Based on the USD specification, the device should always be either on
+ * the default channel or one of the configured channels. However, to
+ * allow operation of other interfaces, suspend the USD functionality
+ * based on the cycle and suspend parameters. This would lower the
+ * probability of service discovery, but would allow functionality of
+ * other interfaces.
+ */
+ if (!de->listen_freq && de->cfg.cycle) {
+ u32 diff_ms;
+
+ if (os_reltime_initialized(&de->suspend_cycle_start)) {
+ struct os_reltime diff;
+
+ os_reltime_sub(&now, &de->suspend_cycle_start, &diff);
+ diff_ms = os_reltime_in_ms(&diff);
+ } else {
+ /* We want to start a new cycle */
+ diff_ms = de->cfg.cycle;
+ }
+
+ if (diff_ms < de->cfg.suspend) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: USD: Suspend in progress: diff_ms=%u",
+ diff_ms);
+
+ /* Set the timer to fire at the end of the suspend */
+ diff_ms = de->cfg.suspend - diff_ms;
+ } else if (diff_ms >= de->cfg.cycle) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Suspend USD for %u ms (passed=%u ms)",
+ de->cfg.suspend, diff_ms);
+ de->suspend_cycle_start = now;
+
+ /* Set the timer to fire at the end of the suspend */
+ diff_ms = de->cfg.suspend;
+ } else {
+ diff_ms = 0;
+ }
+
+ if (diff_ms) {
+ wpa_printf(MSG_DEBUG, "NAN: diff_ms=%u ms", diff_ms);
+
+ eloop_register_timeout(diff_ms / 1000,
+ (diff_ms % 1000) * 1000,
+ nan_de_timer, de, NULL);
+ return;
+ }
+ }
+
for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
struct nan_de_service *srv = de->service[i];
int srv_next;
@@ -570,27 +1072,65 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
if (nan_de_srv_expired(srv, &now)) {
wpa_printf(MSG_DEBUG, "NAN: Service id %d expired",
srv->id);
+ if (srv->type == NAN_DE_PUBLISH &&
+ de->cb.offload_cancel_publish)
+ de->cb.offload_cancel_publish(de->cb.ctx,
+ srv->id);
+ if (srv->type == NAN_DE_SUBSCRIBE &&
+ de->cb.offload_cancel_subscribe)
+ de->cb.offload_cancel_subscribe(de->cb.ctx,
+ srv->id);
nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT);
continue;
}
+ if (srv->sync)
+ continue;
+
if (os_reltime_initialized(&srv->next_publish_state) &&
os_reltime_before(&srv->next_publish_state, &now))
- nan_de_start_new_publish_state(srv, false);
+ nan_de_start_new_publish_state(de, srv, false);
if (srv->type == NAN_DE_PUBLISH &&
os_reltime_initialized(&srv->pause_state_end) &&
- (os_reltime_before(&srv->pause_state_end, &now) ||
- (srv->publish.fsd &&
- os_reltime_initialized(&srv->last_followup) &&
- os_reltime_expired(&now, &srv->last_followup, 1))))
+ (os_reltime_before(&srv->pause_state_end, &now)))
nan_de_unpause_state(srv);
srv_next = nan_de_srv_time_to_next(de, srv, &now);
if (srv_next >= 0 && (next == -1 || srv_next < next))
next = srv_next;
- if (srv_next == 0 && !started &&
+ if (srv->type == NAN_DE_PUBLISH &&
+ srv->publish.fsd &&
+ os_reltime_initialized(&srv->pause_state_end) &&
+ de->tx_wait_end_freq == 0 &&
+ de->listen_freq == 0 && de->ext_listen_freq == 0) {
+ struct os_reltime diff;
+ int duration;
+
+ os_reltime_sub(&srv->pause_state_end, &now, &diff);
+ duration = os_reltime_in_ms(&diff);
+ if (duration < 0)
+ continue;
+ if (srv->listen_stopped) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publisher listen stopped temporarily - do not start driver listen operation");
+ continue;
+ }
+ if ((unsigned int) duration > de->max_listen)
+ duration = de->max_listen;
+ if (de->cb.listen(de->cb.ctx, srv->freq, duration,
+ srv->forced_addr_set ?
+ srv->forced_addr : NULL) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publisher in pauseState - started listen on %u MHz",
+ srv->freq);
+ de->listen_freq = srv->freq;
+ return;
+ }
+ }
+
+ if (srv_next == 0 && !started && !de->offload &&
de->listen_freq == 0 && de->ext_listen_freq == 0 &&
de->tx_wait_end_freq == 0 &&
nan_de_next_multicast(de, srv, &now) == 0) {
@@ -598,24 +1138,27 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
nan_de_tx_multicast(de, srv, 0);
}
- if (!started && de->cb.listen &&
+ if (!started && !de->offload && de->cb.listen &&
de->listen_freq == 0 && de->ext_listen_freq == 0 &&
de->tx_wait_end_freq == 0 &&
((srv->type == NAN_DE_PUBLISH &&
!srv->publish.unsolicited && srv->publish.solicited) ||
(srv->type == NAN_DE_SUBSCRIBE &&
!srv->subscribe.active))) {
- int duration = 1000;
+ u32 duration;
- if (srv->type == NAN_DE_PUBLISH) {
- nan_de_check_chan_change(srv);
- duration = nan_de_time_to_next_chan_change(srv);
- if (duration < 150)
- duration = 150;
+ if (srv->listen_stopped) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Listen stopped temporarily - do not start driver listen operation");
+ continue;
}
+ duration = nan_de_listen_duration(de, srv);
+
started = true;
- if (de->cb.listen(de->cb.ctx, srv->freq, duration) == 0)
+ if (de->cb.listen(de->cb.ctx, srv->freq, duration,
+ srv->forced_addr_set ?
+ srv->forced_addr : NULL) == 0)
de->listen_freq = srv->freq;
}
@@ -626,7 +1169,7 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
if (next == 0)
next = 1;
- wpa_printf(MSG_DEBUG, "NAN: Next timer in %u ms", next);
+
eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer,
de, NULL);
}
@@ -667,15 +1210,32 @@ void nan_de_listen_ended(struct nan_de *de, unsigned int freq)
}
-void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst)
+void nan_de_update_nmi(struct nan_de *de, const u8 *nmi)
+{
+ if (de)
+ os_memcpy(de->nmi, nmi, ETH_ALEN);
+}
+
+
+void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst,
+ const u8 *data, size_t data_len, bool ack)
{
if (freq == de->tx_wait_status_freq)
de->tx_wait_status_freq = 0;
+
+ nan_de_tx_status_match(de, data, data_len, ack);
}
void nan_de_tx_wait_ended(struct nan_de *de)
{
+ if (de->tx_wait_end_freq)
+ wpa_printf(MSG_DEBUG,
+ "NAN: TX wait for response ended (freq=%u)",
+ de->tx_wait_end_freq);
+
+ nan_de_flush_tracked_tx(de, NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED);
+
de->tx_wait_end_freq = 0;
nan_de_run_timer(de);
}
@@ -774,12 +1334,348 @@ static void nan_de_get_sdea(const u8 *buf, size_t len, u8 instance_id,
}
-static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
- const u8 *peer_addr, u8 instance_id,
+static unsigned int nan_de_parse_csia(const u8 *buf, size_t len, u8 instance_id,
+ u8 *cipher_suites,
+ unsigned int max_cipher_suites,
+ u8 *capabilities)
+{
+ const u8 *csia, *pos, *end;
+ u16 csia_len;
+ unsigned int cs_count = 0;
+ const struct nan_cipher_suite_info *cs_info;
+
+ csia = nan_de_get_attr(buf, len, NAN_ATTR_CSIA, 0);
+ if (!csia)
+ return 0;
+
+ csia++;
+ csia_len = WPA_GET_LE16(csia);
+ csia += 2;
+
+ if (csia_len < 1)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Parsing Cipher Suite Information attribute (len=%u)",
+ csia_len);
+
+ cs_info = (const struct nan_cipher_suite_info *) csia;
+
+ if (capabilities)
+ *capabilities = cs_info->capab;
+
+ pos = cs_info->cs;
+ end = csia + csia_len;
+
+ /* Parse cipher suite list. Each entry is 2 bytes (csid + publish_id) */
+ while (end - pos >= 2 && cs_count < max_cipher_suites) {
+ u8 csid = *pos++;
+ u8 publish_id = *pos++;
+
+ if (csid == NAN_CS_NONE || csid >= NAN_CS_MAX) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Invalid cipher suite ID %u for publish ID %u",
+ csid, publish_id);
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Cipher suite ID %u for publish ID %u",
+ csid, publish_id);
+
+ /* Only include cipher suites for the matching publish ID */
+ if (publish_id == instance_id) {
+ cipher_suites[cs_count++] = csid;
+ wpa_printf(MSG_DEBUG,
+ "NAN: Added cipher suite %u for matching publish ID %u",
+ csid, instance_id);
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Parsed %u cipher suites from CSIA for publish ID %u",
+ cs_count, instance_id);
+
+ return cs_count;
+}
+
+
+static unsigned int nan_de_parse_scia(const u8 *buf, size_t len, u8 instance_id,
+ u8 *pmkid_list, unsigned int max_pmkids)
+{
+ const u8 *scia, *end;
+ u16 scia_len;
+ unsigned int pmkid_count = 0;
+
+ scia = nan_de_get_attr(buf, len, NAN_ATTR_SCIA, 0);
+ if (!scia)
+ return 0;
+
+ scia++;
+ scia_len = WPA_GET_LE16(scia);
+ scia += 2;
+
+ end = scia + scia_len;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Parsing Security Context Information attribute (len=%u)",
+ scia_len);
+
+ /* Parse list of Security Context Identifiers */
+ while ((size_t) (end - scia) >= sizeof(struct nan_sec_ctxt)) {
+ const struct nan_sec_ctxt *sec_ctx =
+ (const struct nan_sec_ctxt *) scia;
+ u16 scid_len = le_to_host16(sec_ctx->len);
+
+ if (scid_len + sizeof(*sec_ctx) > (size_t) (end - scia)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Invalid SCID length %u (remaining %zu)",
+ scid_len, (size_t) (end - scia));
+ break;
+ }
+
+ /* Check if this is for our instance_id and is a PMKID type */
+ if (sec_ctx->scid == NAN_SEC_CTX_TYPE_ND_PMKID &&
+ sec_ctx->instance_id == instance_id) {
+ if (scid_len == PMKID_LEN && pmkid_count < max_pmkids) {
+ os_memcpy(&pmkid_list[pmkid_count * PMKID_LEN],
+ sec_ctx->ctxt, PMKID_LEN);
+ pmkid_count++;
+ wpa_hexdump(MSG_DEBUG, "NAN: Parsed PMKID",
+ sec_ctx->ctxt, PMKID_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Unexpected SCID length %u or max PMKIDs reached",
+ scid_len);
+ }
+ }
+
+ scia += scid_len + sizeof(*sec_ctx);
+ }
+
+ wpa_printf(MSG_DEBUG, "NAN: Parsed %u PMKIDs from SCIA", pmkid_count);
+
+ return pmkid_count;
+}
+
+
+static void nan_de_process_elem_container(struct nan_de *de, const u8 *buf,
+ size_t len, const u8 *peer_addr,
+ unsigned int freq, bool p2p, bool pr)
+{
+ const u8 *elem;
+ u16 elem_len;
+
+ elem = nan_de_get_attr(buf, len, NAN_ATTR_ELEM_CONTAINER, 0);
+ if (!elem)
+ return;
+
+ elem++;
+ elem_len = WPA_GET_LE16(elem);
+ elem += 2;
+ /* Skip the attribute if there is not enough froom for an element. */
+ if (elem_len < 1 + 2)
+ return;
+
+ /* Skip Map ID */
+ elem++;
+ elem_len--;
+
+ if (p2p && de->cb.process_p2p_usd_elems)
+ de->cb.process_p2p_usd_elems(de->cb.ctx, elem, elem_len,
+ peer_addr, freq);
+ if (pr && de->cb.process_pr_usd_elems)
+ de->cb.process_pr_usd_elems(de->cb.ctx, elem, elem_len,
+ peer_addr, freq);
+}
+
+
+static void nan_de_parse_dcea(const u8 *buf, size_t len, bool *pairing_setup,
+ bool *npk_nik_caching)
+{
+ const u8 *dcea;
+ u16 dcea_len;
+
+ *pairing_setup = false;
+ *npk_nik_caching = false;
+
+ dcea = nan_de_get_attr(buf, len, NAN_ATTR_DCEA, 0);
+ if (!dcea)
+ return;
+
+ dcea_len = WPA_GET_LE16(dcea + 1);
+ if (dcea_len < 2) {
+ wpa_printf(MSG_DEBUG, "NAN: DCEA length=%u too short",
+ dcea_len);
+ return;
+ }
+
+ *pairing_setup = !!(dcea[4] & NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP);
+ *npk_nik_caching = !!(dcea[4] &
+ NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING);
+}
+
+
+static u16 nan_de_get_advertise_pbm(const u8 *buf, size_t len)
+{
+ const u8 *npba;
+ u16 npba_len;
+
+ npba = nan_de_get_attr(buf, len, NAN_ATTR_NPBA, 0);
+ if (!npba)
+ return 0;
+
+ npba_len = WPA_GET_LE16(npba + 1);
+ if (npba_len < 5) {
+ wpa_printf(MSG_DEBUG, "NAN: Invalid NPBA length %u", npba_len);
+ return 0;
+ }
+
+ /* Skip the attribute ID and length */
+ npba += NAN_ATTR_HDR_LEN;
+ if ((npba[1] & NAN_PBA_TYPE_MASK) != NAN_PBA_TYPE_ADVERTISE)
+ return 0;
+
+ return WPA_GET_LE16(npba + 3);
+}
+
+
+static bool nan_de_filter_match(struct nan_de_service *srv,
+ const u8 *matching_filter,
+ size_t matching_filter_len)
+{
+ const u8 *spos, *spos_end, *ppos, *ppos_end;
+ const u8 *publish_filter = NULL, *subscribe_filter = NULL;
+ u8 publish_filter_len = 0, subscribe_filter_len = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Check matching filter for service id %d type %d",
+ srv->id, srv->type);
+
+ if (srv->type == NAN_DE_PUBLISH) {
+ if (srv->matching_filter_rx) {
+ publish_filter =
+ wpabuf_head_u8(srv->matching_filter_rx);
+ publish_filter_len =
+ wpabuf_len(srv->matching_filter_rx);
+ }
+ subscribe_filter = matching_filter;
+ subscribe_filter_len = matching_filter_len;
+ } else if (srv->type == NAN_DE_SUBSCRIBE) {
+ if (srv->matching_filter_rx) {
+ subscribe_filter =
+ wpabuf_head_u8(srv->matching_filter_rx);
+ subscribe_filter_len =
+ wpabuf_len(srv->matching_filter_rx);
+ }
+ publish_filter = matching_filter;
+ publish_filter_len = matching_filter_len;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Unsupported service type %d for matching filter",
+ srv->type);
+ return false;
+ }
+
+ if (!subscribe_filter)
+ return true;
+
+ spos = subscribe_filter;
+ spos_end = subscribe_filter + subscribe_filter_len;
+
+ ppos = publish_filter;
+ ppos_end = publish_filter ? publish_filter + publish_filter_len : NULL;
+
+ wpa_hexdump(MSG_DEBUG, "NAN: subscribe filter",
+ spos, spos_end - spos);
+ if (ppos)
+ wpa_hexdump(MSG_DEBUG, "NAN: publish filter",
+ ppos, ppos_end - ppos);
+
+ while (spos < spos_end) {
+ u8 slen, plen = 0;
+
+ slen = *spos++;
+
+ /* Invalid filter length - do not match */
+ if (slen > spos_end - spos)
+ return false;
+
+ /* Read publish filter */
+ if (ppos && ppos < ppos_end) {
+ plen = *ppos++;
+ if (plen > ppos_end - ppos)
+ return false;
+ }
+
+ if (slen > 0) {
+ if (!ppos)
+ return false;
+
+ /* For non zero filters, compare */
+ if (plen &&
+ (plen != slen || os_memcmp(spos, ppos, plen) != 0))
+ return false;
+
+ /* Filter matches */
+ }
+
+ spos += slen;
+
+ /*
+ * If ppos is NULL we can still have match if the subscribe
+ * filter is <0><0>...
+ */
+ if (!ppos)
+ continue;
+
+ ppos += plen;
+
+ /* Publish filter is over */
+ if (ppos >= ppos_end && spos < spos_end)
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
+ const u8 *peer_addr, const u8 *a3, u8 instance_id,
+ const u8 *matching_filter,
+ size_t matching_filter_len,
u8 req_instance_id, u16 sdea_control,
enum nan_service_protocol_type srv_proto_type,
- const u8 *ssi, size_t ssi_len)
+ const u8 *ssi, size_t ssi_len,
+ bool range_limit, int rssi,
+ const u8 *buf, size_t buf_len)
{
+ struct nan_discovery_result res;
+
+ /* The SCIA can potentially contain a PMKID for each cipher suite */
+ u8 pmkid_list[(NAN_CS_MAX - 1) * PMKID_LEN];
+ unsigned int pmkid_count = 0;
+ /* Cipher suites from CSIA */
+ u8 cipher_suites[NAN_CS_MAX - 1];
+ unsigned int cipher_suite_count = 0;
+
+ if (!nan_de_filter_match(srv, matching_filter, matching_filter_len))
+ return false;
+
+ /* Skip USD logic */
+ if (srv->sync)
+ goto send_event;
+
+ if ((range_limit || srv->close_proximity) &&
+ de->rssi_threshold && rssi) {
+ if (rssi < de->rssi_threshold) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Discard SDA with RSSI %d below threshold %d",
+ rssi, de->rssi_threshold);
+ return false;
+ }
+ }
+
/* Subscribe function processing of a receive Publish message */
if (!os_reltime_initialized(&srv->first_discovered)) {
os_get_reltime(&srv->first_discovered);
@@ -787,167 +1683,142 @@ static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
nan_de_run_timer(de);
}
- if (srv->subscribe.active && req_instance_id == 0) {
+ if (!de->offload && srv->subscribe.active && req_instance_id == 0) {
/* Active subscriber replies with a Subscribe message if it
* received a matching unsolicited Publish message. */
nan_de_tx_multicast(de, srv, instance_id);
}
- if (!srv->subscribe.active && req_instance_id == 0) {
+ if (!de->offload && !srv->subscribe.active && req_instance_id == 0) {
/* Passive subscriber replies with a Follow-up message without
* Service Specific Info field if it received a matching
* unsolicited Publish message. */
nan_de_transmit(de, srv->id, NULL, NULL, peer_addr,
- instance_id);
+ instance_id, NULL, NULL);
}
- if (de->cb.discovery_result)
- de->cb.discovery_result(
- de->cb.ctx, srv->id, srv_proto_type,
- ssi, ssi_len, instance_id,
- peer_addr,
- sdea_control & NAN_SDEA_CTRL_FSD_REQ,
- sdea_control & NAN_SDEA_CTRL_FSD_GAS);
-}
-
-
-static bool nan_de_filter_match(struct nan_de_service *srv,
- const u8 *matching_filter,
- size_t matching_filter_len)
-{
- const u8 *pos, *end;
+send_event:
+ os_memset(&res, 0, sizeof(res));
+ if (buf && buf_len > 0) {
+ /* Parse Cipher Suite Information Attribute */
+ cipher_suite_count = nan_de_parse_csia(
+ buf, buf_len, instance_id, cipher_suites,
+ ARRAY_SIZE(cipher_suites), NULL);
- /* Since we do not currently support matching_filter_rx values for the
- * local Publish function, any matching filter with at least one
- * <length,value> pair with length larger than zero implies a mismatch.
- */
+ /* Parse Security Context Information attribute */
+ pmkid_count = nan_de_parse_scia(buf, buf_len, instance_id,
+ pmkid_list,
+ sizeof(pmkid_list) / PMKID_LEN);
- if (!matching_filter)
- return true;
+ /*
+ * Parse Device Capability Extension attribute for pairing
+ * setup and NPK/NIK caching support
+ */
+ nan_de_parse_dcea(buf, buf_len,
+ &res.pairing_setup_supp,
+ &res.npk_nik_caching_supp);
- pos = matching_filter;
- end = matching_filter + matching_filter_len;
+ /* Get the bootstrapping methods */
+ res.pbm = nan_de_get_advertise_pbm(buf, buf_len);
+ }
- while (pos < end) {
- u8 len;
+ res.subscribe_id = srv->id;
+ res.srv_proto_type = srv_proto_type;
+ res.ssi = ssi;
+ res.ssi_len = ssi_len;
+ res.peer_publish_id = instance_id;
+ res.peer_addr = peer_addr;
+ res.fsd = !!(sdea_control & NAN_SDEA_CTRL_FSD_REQ);
+ res.fsd_gas = !!(sdea_control & NAN_SDEA_CTRL_FSD_GAS);
+ res.data_path = !!(sdea_control & NAN_SDEA_CTRL_DATA_PATH_REQ);
+ res.security_required = !!(sdea_control & NAN_SDEA_CTRL_SECURITY_REQ);
+ res.cipher_suites = cipher_suite_count > 0 ? cipher_suites : NULL;
+ res.n_cipher_suites = cipher_suite_count;
+ res.pmkid_list = pmkid_count > 0 ? pmkid_list : NULL;
+ res.pmkid_count = pmkid_count;
- len = *pos++;
- if (len > end - pos)
- break;
- if (len) {
- /* A non-empty Matching Filter entry: no match since
- * there is no local matching_filter_rx. */
- return false;
- }
- }
+ if (de->cb.discovery_result)
+ de->cb.discovery_result(de->cb.ctx, &res);
return true;
}
-static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
- const u8 *peer_addr, u8 instance_id,
+static bool nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
+ const u8 *peer_addr, const u8 *a3,
+ u8 instance_id,
const u8 *matching_filter,
size_t matching_filter_len,
enum nan_service_protocol_type srv_proto_type,
- const u8 *ssi, size_t ssi_len)
+ const u8 *ssi, size_t ssi_len,
+ bool range_limit, int rssi)
{
- struct wpabuf *buf;
- size_t len = 0, sda_len, sdea_len;
- u8 ctrl = 0;
- u16 sdea_ctrl = 0;
+ const u8 *network_id;
/* Publish function processing of a receive Subscribe message */
if (!nan_de_filter_match(srv, matching_filter, matching_filter_len))
- return;
+ return false;
+
+ if ((range_limit || srv->close_proximity) &&
+ de->rssi_threshold && rssi) {
+ if (rssi < de->rssi_threshold) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Discard SDA with RSSI %d below threshold %d",
+ rssi, de->rssi_threshold);
+ return false;
+ }
+ }
if (!srv->publish.solicited)
- return;
+ return false;
if (os_reltime_initialized(&srv->pause_state_end) &&
(!ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
instance_id != srv->sel_peer_id)) {
wpa_printf(MSG_DEBUG,
"NAN: In pauseState - ignore Subscribe message from another subscriber");
- return;
+ return false;
}
- /* Reply with a solicited Publish message */
- /* Service Descriptor attribute */
- sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1;
- len += NAN_ATTR_HDR_LEN + sda_len;
-
- /* Service Descriptor Extension attribute */
- sdea_len = 1 + 2;
- if (srv->ssi)
- sdea_len += 2 + 4 + wpabuf_len(srv->ssi);
- len += NAN_ATTR_HDR_LEN + sdea_len;
-
- /* Element Container attribute */
- if (srv->elems)
- len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
-
- buf = nan_de_alloc_sdf(len);
- if (!buf)
- return;
+ if (de->offload)
+ goto offload;
- /* Service Descriptor attribute */
- wpabuf_put_u8(buf, NAN_ATTR_SDA);
- wpabuf_put_le16(buf, sda_len);
- wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN);
- wpabuf_put_u8(buf, srv->id); /* Instance ID */
- wpabuf_put_u8(buf, instance_id); /* Requestor Instance ID */
- ctrl |= NAN_SRV_CTRL_PUBLISH;
- wpabuf_put_u8(buf, ctrl);
+ /* Reply with a solicited Publish message */
- /* Service Descriptor Extension attribute */
- if (srv->type == NAN_DE_PUBLISH || srv->ssi) {
- wpabuf_put_u8(buf, NAN_ATTR_SDEA);
- wpabuf_put_le16(buf, sdea_len);
- wpabuf_put_u8(buf, srv->id); /* Instance ID */
- if (srv->type == NAN_DE_PUBLISH) {
- if (srv->publish.fsd)
- sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ;
- if (srv->publish.fsd_gas)
- sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS;
- }
- wpabuf_put_le16(buf, sdea_ctrl);
- if (srv->ssi) {
- wpabuf_put_le16(buf, 4 + wpabuf_len(srv->ssi));
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, srv->srv_proto_type);
- wpabuf_put_buf(buf, srv->ssi);
- }
- }
+ if (srv->is_p2p)
+ network_id = p2p_network_id;
+ else
+ network_id = nan_network_id;
- /* Element Container attribute */
- if (srv->elems) {
- wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER);
- wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems));
- wpabuf_put_u8(buf, 0); /* Map ID */
- wpabuf_put_buf(buf, srv->elems);
- }
+ if (srv->sync && de->cluster_id_set)
+ a3 = de->cluster_id;
+ else if (srv->publish.solicited_multicast || !a3)
+ a3 = network_id;
+ else if (srv->is_p2p)
+ a3 = de->nmi;
- /* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
- * but there is no synchronization in USD as as such, no NAN Cluster
- * either. Use Wildcard BSSID instead. */
- nan_de_tx(de, srv->freq, 100,
- srv->publish.solicited_multicast ? nan_network_id : peer_addr,
- de->nmi, wildcard_bssid, buf);
- wpabuf_free(buf);
+ nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_PUBLISH,
+ srv->publish.solicited_multicast ?
+ network_id : peer_addr, a3, instance_id, srv->ssi, NULL,
+ NULL);
- nan_de_pause_state(srv, peer_addr, instance_id);
+ if (!srv->is_p2p && !srv->sync)
+ nan_de_pause_state(srv, peer_addr, instance_id);
+offload:
if (!srv->publish.disable_events && de->cb.replied)
de->cb.replied(de->cb.ctx, srv->id, peer_addr, instance_id,
srv_proto_type, ssi, ssi_len);
+
+ return true;
}
-static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
- const u8 *peer_addr, u8 instance_id,
- const u8 *ssi, size_t ssi_len)
+static bool nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
+ const u8 *peer_addr, const u8 *a3,
+ u8 instance_id, const u8 *ssi, size_t ssi_len,
+ const u8 *buf, size_t len)
{
/* Follow-up function processing of a receive Follow-up message for a
* Subscribe or Publish instance */
@@ -959,23 +1830,84 @@ static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
!ssi)) {
wpa_printf(MSG_DEBUG,
"NAN: In pauseState - ignore Follow-up message from another subscriber or without ssi");
- return;
+ return false;
}
- os_get_reltime(&srv->last_followup);
-
- if (srv->type == NAN_DE_PUBLISH && !ssi)
+ if (srv->type == NAN_DE_PUBLISH && !ssi && !srv->sync)
nan_de_pause_state(srv, peer_addr, instance_id);
+ os_memcpy(srv->a3, a3, ETH_ALEN);
+ srv->a3_set = true;
+
if (de->cb.receive)
de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len,
- peer_addr);
+ peer_addr, buf, len);
+
+ return true;
+}
+
+
+static bool nan_check_bloom_filter(const u8 *nmi, const u8 *bf,
+ size_t bf_len, u8 bf_idx)
+{
+ u8 a_j_x[1 + ETH_ALEN];
+ int j;
+ u32 crc;
+
+ for (j = 4 * bf_idx; j < 4 * (bf_idx + 1); j++) {
+ a_j_x[0] = j;
+ os_memcpy(&a_j_x[1], nmi, ETH_ALEN);
+ crc = (~ieee80211_crc32(a_j_x, 1 + ETH_ALEN)) & 0xFFFF;
+ crc %= bf_len * 8;
+ if (!(bf[crc / 8] & BIT(crc % 8)))
+ return false;
+ }
+
+ return true;
}
-static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
+static bool nan_srf_match(struct nan_de *de, const u8 *srf, size_t srf_len)
+{
+ u8 srf_ctrl;
+ bool srf_type_bf;
+ bool include;
+ u8 srf_bf_idx;
+
+ if (srf_len < 1)
+ return false;
+
+ srf_ctrl = *srf++;
+ srf_len--;
+
+ srf_type_bf = !!(srf_ctrl & NAN_SRF_CTRL_BF);
+ include = !!(srf_ctrl & NAN_SRF_CTRL_INCLUDE);
+ srf_bf_idx = (srf_ctrl >> NAN_SRF_CTRL_BF_IDX_POS) &
+ NAN_SRF_CTRL_BF_IDX_MSK;
+
+ if (srf_type_bf) {
+ if (srf_len == 0)
+ return false;
+ if (nan_check_bloom_filter(de->nmi, srf, srf_len, srf_bf_idx))
+ return include;
+ } else {
+ /* MAC Address filter */
+ while (srf_len >= ETH_ALEN) {
+ if (ether_addr_equal(srf, de->nmi))
+ return include;
+
+ srf += ETH_ALEN;
+ srf_len -= ETH_ALEN;
+ }
+ }
+
+ return !include;
+}
+
+
+static bool nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
unsigned int freq, const u8 *buf, size_t len,
- const u8 *sda, size_t sda_len)
+ const u8 *sda, size_t sda_len, int rssi)
{
const u8 *service_id;
u8 instance_id, req_instance_id, ctrl;
@@ -989,16 +1921,17 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
const u8 *end;
const u8 *matching_filter = NULL;
size_t matching_filter_len = 0;
+ bool ret = false;
if (sda_len < NAN_SERVICE_ID_LEN + 1 + 1 + 1)
- return;
+ return false;
end = sda + sda_len;
service_id = sda;
sda += NAN_SERVICE_ID_LEN;
instance_id = *sda++;
req_instance_id = *sda++;
- ctrl = *sda;
+ ctrl = *sda++;
type = ctrl & NAN_SRV_CTRL_TYPE_MASK;
wpa_printf(MSG_DEBUG,
"NAN: SDA - Service ID %02x%02x%02x%02x%02x%02x Instance ID %u Requestor Instance ID %u Service Control 0x%x (Service Control Type %u)",
@@ -1010,12 +1943,12 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
wpa_printf(MSG_DEBUG,
"NAN: Discard SDF with unknown Service Control Type %u",
type);
- return;
+ return false;
}
if (ctrl & NAN_SRV_CTRL_BINDING_BITMAP) {
if (end - sda < 2)
- return;
+ return false;
sda += 2;
}
@@ -1023,10 +1956,10 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
u8 flen;
if (end - sda < 1)
- return;
+ return false;
flen = *sda++;
if (end - sda < flen)
- return;
+ return false;
matching_filter = sda;
matching_filter_len = flen;
sda += flen;
@@ -1036,10 +1969,17 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
u8 flen;
if (end - sda < 1)
- return;
+ return false;
flen = *sda++;
if (end - sda < flen)
- return;
+ return false;
+
+ if (!nan_srf_match(de, sda, flen)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Discard SDA with non-matching SRF");
+ return false;
+ }
+
sda += flen;
}
@@ -1047,16 +1987,19 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
u8 flen;
if (end - sda < 1)
- return;
+ return false;
flen = *sda++;
if (end - sda < flen)
- return;
- if (flen >= 4 && WPA_GET_BE24(sda) == OUI_WFA) {
- srv_proto_type = sda[3];
- ssi = sda + 4;
- ssi_len = flen - 4;
- wpa_printf(MSG_DEBUG, "NAN: Service Protocol Type %d",
- srv_proto_type);
+ return false;
+
+ if (flen) {
+ /* This case of SSI in SDA does not have an explicit
+ * indication of a service protocol type unlike the
+ * SDEA case. For now, leave srv_proto_type to 0 for
+ * this SDA case since that is a reserved value for the
+ * SDEA cases. */
+ ssi = sda;
+ ssi_len = flen;
wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len);
}
sda += flen;
@@ -1094,43 +2037,55 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
wpa_hexdump(MSG_MSGDUMP, "NAN: ssi",
ssi, ssi_len);
}
+ nan_de_process_elem_container(de, buf, len, peer_addr,
+ freq, srv->is_p2p,
+ srv->is_pr);
}
switch (type) {
case NAN_SRV_CTRL_PUBLISH:
- nan_de_rx_publish(de, srv, peer_addr, instance_id,
- req_instance_id,
- sdea_control, srv_proto_type,
- ssi, ssi_len);
+ ret |= nan_de_rx_publish(
+ de, srv, peer_addr, a3, instance_id,
+ matching_filter, matching_filter_len,
+ req_instance_id, sdea_control, srv_proto_type,
+ ssi, ssi_len,
+ ctrl & NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED,
+ rssi, buf, len);
break;
case NAN_SRV_CTRL_SUBSCRIBE:
- nan_de_rx_subscribe(de, srv, peer_addr, instance_id,
- matching_filter,
- matching_filter_len,
- srv_proto_type,
- ssi, ssi_len);
+ ret |= nan_de_rx_subscribe(
+ de, srv, peer_addr, a3, instance_id,
+ matching_filter, matching_filter_len,
+ srv_proto_type, ssi, ssi_len,
+ ctrl & NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED,
+ rssi);
break;
case NAN_SRV_CTRL_FOLLOW_UP:
- nan_de_rx_follow_up(de, srv, peer_addr, instance_id,
- ssi, ssi_len);
+ ret |= nan_de_rx_follow_up(de, srv, peer_addr, a3,
+ instance_id, ssi, ssi_len,
+ buf, len);
break;
}
}
+
+ return ret;
}
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
- const u8 *buf, size_t len)
+bool nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+ unsigned int freq, const u8 *buf, size_t len, int rssi)
{
const u8 *sda;
u16 sda_len;
unsigned int skip;
+ bool ret = false;
if (!de->num_service)
- return;
+ return false;
- wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR " freq=%u len=%zu",
- MAC2STR(peer_addr), freq, len);
+ wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR
+ " freq=%u len=%zu rssi=%d",
+ MAC2STR(peer_addr), freq, len, rssi);
wpa_hexdump(MSG_MSGDUMP, "NAN: SDF payload", buf, len);
@@ -1142,8 +2097,11 @@ void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
sda++;
sda_len = WPA_GET_LE16(sda);
sda += 2;
- nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len);
+ ret |= nan_de_rx_sda(de, peer_addr, a3, freq, buf, len,
+ sda, sda_len, rssi);
}
+
+ return ret;
}
@@ -1196,15 +2154,29 @@ static int nan_de_derive_service_id(struct nan_de_service *srv)
}
+const u8 * nan_de_get_service_id(struct nan_de *de, int id)
+{
+ struct nan_de_service *srv;
+
+ if (id < 1 || id > NAN_DE_MAX_SERVICE)
+ return NULL;
+ srv = de->service[id - 1];
+ if (!srv)
+ return NULL;
+ return srv->service_id;
+}
+
+
int nan_de_publish(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
- struct nan_publish_params *params)
+ struct nan_publish_params *params, bool p2p,
+ const u8 *addr)
{
int publish_id;
struct nan_de_service *srv;
- if (!service_name) {
+ if (!service_name && !params->proximity_ranging) {
wpa_printf(MSG_DEBUG, "NAN: Publish() - no service_name");
return -1;
}
@@ -1215,6 +2187,24 @@ int nan_de_publish(struct nan_de *de, const char *service_name,
return -1;
}
+ if (params->proximity_ranging && params->solicited && !elems) {
+ wpa_printf(MSG_INFO,
+ "NAN: Unable to fetch proximity ranging params");
+ return -1;
+ }
+
+ if (params->sync && !de->cluster_id_set) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publish() - can't publish sync, cluster id is not set");
+ return -1;
+ }
+
+ if (p2p && params->sync) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publish() - P2P is not supported with sync");
+ return -1;
+ }
+
publish_id = nan_de_get_handle(de);
if (publish_id < 1)
return -1;
@@ -1224,11 +2214,18 @@ int nan_de_publish(struct nan_de *de, const char *service_name,
return -1;
srv->type = NAN_DE_PUBLISH;
srv->freq = srv->default_freq = params->freq;
- srv->service_name = os_strdup(service_name);
- if (!srv->service_name)
- goto fail;
- if (nan_de_derive_service_id(srv) < 0)
+
+ if (service_name) {
+ srv->service_name = os_strdup(service_name);
+ if (!srv->service_name)
+ goto fail;
+ }
+
+ if (params->proximity_ranging && !service_name)
+ os_memset(srv->service_id, 0, NAN_SERVICE_ID_LEN);
+ else if (nan_de_derive_service_id(srv) < 0)
goto fail;
+
os_memcpy(&srv->publish, params, sizeof(*params));
if (params->freq_list) {
@@ -1253,14 +2250,82 @@ int nan_de_publish(struct nan_de *de, const char *service_name,
goto fail;
}
+ if (params->match_filter_rx) {
+ srv->matching_filter_rx =
+ wpabuf_parse_bin(params->match_filter_rx);
+ if (!srv->matching_filter_rx ||
+ wpabuf_len(srv->matching_filter_rx) > 255) {
+ wpa_printf(MSG_INFO,
+ "NAN: Failed to parse RX matching filter");
+ goto fail;
+ }
+ }
+
+ if (params->match_filter_tx) {
+ srv->matching_filter_tx =
+ wpabuf_parse_bin(params->match_filter_tx);
+ if (!srv->matching_filter_tx ||
+ wpabuf_len(srv->matching_filter_tx) > 255) {
+ wpa_printf(MSG_INFO,
+ "NAN: Failed to parse TX matching filter");
+ goto fail;
+ }
+ }
+
+ srv->sync = params->sync;
+
+ if (addr && params->forced_addr) {
+ os_memcpy(srv->forced_addr, addr, ETH_ALEN);
+ srv->forced_addr_set = true;
+ wpa_printf(MSG_DEBUG, "NAN: Using source address " MACSTR
+ " for publish service", MAC2STR(srv->forced_addr));
+ }
+
+ srv->security_capab = params->security_capab;
+
+ if (params->cipher_suites_list) {
+ int i = 0;
+
+ while (params->cipher_suites_list[i] && i < NAN_CS_MAX) {
+ if (params->cipher_suites_list[i] >= NAN_CS_MAX) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Invalid cipher suite %d in publish",
+ params->cipher_suites_list[i]);
+ goto fail;
+ }
+
+ i++;
+ }
+
+ srv->cipher_suites_list =
+ int_array_dup(params->cipher_suites_list);
+ if (!srv->cipher_suites_list)
+ goto fail;
+ }
+
+ dl_list_init(&srv->pmkid_list);
+#ifdef CONFIG_NAN
+ if (nan_crypto_pmkid_list(&srv->pmkid_list, de->nmi, srv->service_id,
+ srv->cipher_suites_list, params->nd_pmk) < 0)
+ goto fail;
+#endif /* CONFIG_NAN */
+
/* Prepare for single and multi-channel states; starting with
* single channel */
srv->first_multi_chan = true;
- nan_de_start_new_publish_state(srv, true);
+ nan_de_start_new_publish_state(de, srv, true);
wpa_printf(MSG_DEBUG, "NAN: Assigned new publish handle %d for %s",
- publish_id, service_name);
+ publish_id, service_name ? service_name : "Ranging");
srv->id = publish_id;
+ srv->is_p2p = p2p;
+ srv->is_pr = params->proximity_ranging && params->solicited;
+ srv->close_proximity = params->close_proximity;
+ srv->pbm = params->pbm;
+ srv->gtk_required = params->gtk_required;
+ srv->data_path = params->data_path;
+ srv->security_required = params->security_required;
+
nan_de_add_srv(de, srv);
nan_de_run_timer(de);
return publish_id;
@@ -1309,19 +2374,125 @@ int nan_de_update_publish(struct nan_de *de, int publish_id,
}
+int nan_de_unpause_publish(struct nan_de *de, int publish_id,
+ u8 peer_instance_id, const u8 *peer_addr)
+{
+ struct nan_de_service *srv;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: UnpausePublish(publish_id=%d, peer_instance_id=%d peer_addr="
+ MACSTR ")",
+ publish_id, peer_instance_id, MAC2STR(peer_addr));
+
+ if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE)
+ return -1;
+ srv = de->service[publish_id - 1];
+ if (!srv || srv->type != NAN_DE_PUBLISH)
+ return -1;
+
+ if (srv->sel_peer_id != peer_instance_id ||
+ !ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
+ !os_reltime_initialized(&srv->pause_state_end))
+ return -1;
+
+ nan_de_unpause_state(srv);
+ return 0;
+}
+
+
+static void bloom_filter_add(u8 *bf, u8 bf_idx, u8 bf_len, const u8 *mac)
+{
+ u8 a_j_x[1 + ETH_ALEN];
+ int j;
+ u32 crc;
+
+ for (j = 4 * bf_idx; j < 4 * (bf_idx + 1); j++) {
+ a_j_x[0] = j;
+ os_memcpy(&a_j_x[1], mac, ETH_ALEN);
+ crc = (~ieee80211_crc32(a_j_x, 1 + ETH_ALEN)) & 0xFFFF;
+ crc %= bf_len * 8;
+ bf[crc / 8] |= 1 << (crc % 8);
+ }
+}
+
+
+static struct wpabuf * nan_build_bloom_filter(const char *srf_mac_list,
+ u8 srf_bf_len, u8 srf_bf_idx)
+{
+ struct wpabuf *srf;
+ int i, n;
+ u8 mac[ETH_ALEN];
+ u8 *bf;
+
+ if (srf_bf_idx > 3)
+ return NULL;
+
+ if (os_strlen(srf_mac_list) % (ETH_ALEN * 2)) {
+ wpa_printf(MSG_INFO,
+ "NAN: Invalid SRF MAC list length %zu",
+ os_strlen(srf_mac_list));
+ return NULL;
+ }
+
+ n = os_strlen(srf_mac_list) / (ETH_ALEN * 2);
+
+ srf = wpabuf_alloc(srf_bf_len);
+ if (!srf)
+ return NULL;
+
+ bf = wpabuf_put(srf, srf_bf_len);
+
+ for (i = 0; i < n; i++) {
+ if (hexstr2bin(srf_mac_list + i * 2 * ETH_ALEN, mac, ETH_ALEN))
+ {
+ wpa_printf(MSG_INFO,
+ "NAN: Invalid SRF MAC address %s",
+ srf_mac_list + i * 2 * ETH_ALEN);
+ goto out;
+ }
+
+ bloom_filter_add(bf, srf_bf_idx, srf_bf_len, mac);
+ }
+
+ return srf;
+out:
+ wpabuf_free(srf);
+ return NULL;
+}
+
+
int nan_de_subscribe(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
- struct nan_subscribe_params *params)
+ struct nan_subscribe_params *params, bool p2p,
+ const u8 *addr)
{
int subscribe_id;
struct nan_de_service *srv;
- if (!service_name) {
+ if (!service_name && !params->proximity_ranging) {
wpa_printf(MSG_DEBUG, "NAN: Subscribe() - no service_name");
return -1;
}
+ if (params->proximity_ranging && params->active && !elems) {
+ wpa_printf(MSG_INFO,
+ "NAN: Unable to fetch proximity ranging params");
+ return -1;
+ }
+
+ if (params->sync && !de->cluster_id_set) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Subscribe() - can't publish sync, cluster id is not set");
+ return -1;
+ }
+
+ if (p2p && params->sync) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Subscribe() - P2P is not supported with sync");
+ return -1;
+ }
+
subscribe_id = nan_de_get_handle(de);
if (subscribe_id < 1)
return -1;
@@ -1331,12 +2502,30 @@ int nan_de_subscribe(struct nan_de *de, const char *service_name,
return -1;
srv->type = NAN_DE_SUBSCRIBE;
srv->freq = params->freq;
- srv->service_name = os_strdup(service_name);
- if (!srv->service_name)
- goto fail;
- if (nan_de_derive_service_id(srv) < 0)
+
+ if (service_name) {
+ srv->service_name = os_strdup(service_name);
+ if (!srv->service_name)
+ goto fail;
+ }
+
+ if (params->proximity_ranging && !service_name)
+ os_memset(srv->service_id, 0, NAN_SERVICE_ID_LEN);
+ else if (nan_de_derive_service_id(srv) < 0)
goto fail;
+
os_memcpy(&srv->subscribe, params, sizeof(*params));
+
+ if (params->freq_list) {
+ size_t len;
+
+ len = (int_array_len(params->freq_list) + 1) * sizeof(int);
+ srv->freq_list = os_memdup(params->freq_list, len);
+ if (!srv->freq_list)
+ goto fail;
+ }
+ srv->subscribe.freq_list = NULL;
+
srv->srv_proto_type = srv_proto_type;
if (ssi) {
srv->ssi = wpabuf_dup(ssi);
@@ -1349,9 +2538,72 @@ int nan_de_subscribe(struct nan_de *de, const char *service_name,
goto fail;
}
+ if (params->match_filter_rx) {
+ srv->matching_filter_rx =
+ wpabuf_parse_bin(params->match_filter_rx);
+ if (!srv->matching_filter_rx ||
+ wpabuf_len(srv->matching_filter_rx) > 255) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Failed to parse RX matching filter");
+ goto fail;
+ }
+ }
+
+ if (params->match_filter_tx) {
+ srv->matching_filter_tx =
+ wpabuf_parse_bin(params->match_filter_tx);
+ if (!srv->matching_filter_tx ||
+ wpabuf_len(srv->matching_filter_tx) > 255) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Failed to parse TX matching filter");
+ goto fail;
+ }
+ }
+
+ if (params->srf_mac_list) {
+ if (params->srf_bf_len) {
+ srv->srf = nan_build_bloom_filter(params->srf_mac_list,
+ params->srf_bf_len,
+ params->srf_bf_idx);
+ srv->srf_type_bloom_filter = true;
+ srv->srf_bf_idx = params->srf_bf_idx;
+ } else {
+ srv->srf = wpabuf_parse_bin(params->srf_mac_list);
+ if (wpabuf_len(srv->srf) % ETH_ALEN) {
+ wpa_printf(MSG_INFO,
+ "NAN: Invalid SRF MAC list length");
+ goto fail;
+ }
+ }
+
+ if (!srv->srf || wpabuf_len(srv->srf) > 254) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Failed to parse SRF MAC list");
+ goto fail;
+ }
+
+ srv->srf_include = params->srf_include;
+ }
+
+ if (addr && params->forced_addr) {
+ os_memcpy(srv->forced_addr, addr, ETH_ALEN);
+ srv->forced_addr_set = true;
+ wpa_printf(MSG_DEBUG, "NAN: Using source address " MACSTR
+ " for subscribe service", MAC2STR(srv->forced_addr));
+ }
+
+ dl_list_init(&srv->pmkid_list);
+
wpa_printf(MSG_DEBUG, "NAN: Assigned new subscribe handle %d for %s",
- subscribe_id, service_name);
+ subscribe_id, service_name ? service_name : "Ranging");
srv->id = subscribe_id;
+ srv->is_p2p = p2p;
+ srv->is_pr = params->proximity_ranging && params->active;
+ srv->sync = params->sync;
+ srv->close_proximity = params->close_proximity;
+ srv->pbm = params->pbm;
+ srv->gtk_required = params->gtk_required;
+
nan_de_add_srv(de, srv);
nan_de_run_timer(de);
return subscribe_id;
@@ -1376,9 +2628,12 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id)
int nan_de_transmit(struct nan_de *de, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
- const u8 *peer_addr, u8 req_instance_id)
+ const u8 *peer_addr, u8 req_instance_id,
+ const struct wpabuf *nan_attrs, u32 *cookie)
{
struct nan_de_service *srv;
+ const u8 *a3;
+ const u8 *network_id;
if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
return -1;
@@ -1387,9 +2642,254 @@ int nan_de_transmit(struct nan_de *de, int handle,
if (!srv)
return -1;
+ if (srv->sync && !de->cluster_id_set) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Cannot transmit Follow-up, cluster ID not set");
+ return -1;
+ }
+
+ if (srv->is_p2p)
+ network_id = p2p_network_id;
+ else if (srv->sync)
+ network_id = de->cluster_id;
+ else
+ network_id = nan_network_id;
+
+ if (srv->a3_set)
+ a3 = srv->a3;
+ else
+ a3 = network_id;
nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
- peer_addr, req_instance_id, ssi);
+ peer_addr, a3, req_instance_id, ssi, nan_attrs,
+ cookie);
- os_get_reltime(&srv->last_followup);
+ srv->listen_stopped = false;
return 0;
}
+
+
+int nan_de_stop_listen(struct nan_de *de, int handle)
+{
+ struct nan_de_service *srv;
+
+ if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
+ return -1;
+
+ srv = de->service[handle - 1];
+ if (!srv)
+ return -1;
+ srv->listen_stopped = true;
+ return 0;
+}
+
+
+int nan_de_config(struct nan_de *de, struct nan_de_cfg *cfg)
+{
+ if (!de || !cfg)
+ return -1;
+
+ /* No change in configuration */
+ if (de->cfg.n_min == cfg->n_min && de->cfg.n_max == cfg->n_max &&
+ de->cfg.cycle == cfg->cycle && de->cfg.suspend == cfg->suspend)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Configuring NAN DE: n=(%u, %u), suspend=%u, cycle=%u",
+ cfg->n_min, cfg->n_max, cfg->suspend, cfg->cycle);
+
+ if (!cfg->n_min && !cfg->n_max) {
+ cfg->n_min = NAN_DE_N_MIN;
+ cfg->n_max = NAN_DE_N_MAX;
+ } else if (cfg->n_min < 1 || cfg->n_max < cfg->n_min) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Invalid configuration parameters: N");
+ return -1;
+ }
+
+ if (((!!cfg->suspend) ^ (!!cfg->cycle)) ||
+ (cfg->cycle && cfg->suspend >= cfg->cycle)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Invalid configuration parameters: cycle");
+ return -1;
+ }
+
+ de->cfg = *cfg;
+
+ os_memset(&de->suspend_cycle_start, 0, sizeof(de->suspend_cycle_start));
+
+ if (!de->listen_freq)
+ nan_de_run_timer(de);
+
+ return 0;
+}
+
+
+void nan_de_dw_trigger(struct nan_de *de, int freq)
+{
+ int i;
+ struct os_reltime now;
+
+ de->dw_freq = freq;
+
+ if (!de->cluster_id_set) {
+ wpa_printf(MSG_DEBUG, "NAN: Skip DW, cluster ID not set");
+ return;
+ }
+
+ os_get_reltime(&now);
+ for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
+ struct nan_de_service *srv = de->service[i];
+
+ if (!srv || !srv->sync)
+ continue;
+
+ if (nan_de_srv_expired(srv, &now)) {
+ nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT);
+ continue;
+ }
+
+ if ((srv->type == NAN_DE_PUBLISH &&
+ srv->publish.unsolicited) ||
+ (srv->type == NAN_DE_SUBSCRIBE && srv->subscribe.active)) {
+ nan_de_tx_multicast(de, srv, 0);
+ }
+ }
+}
+
+
+void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id)
+{
+ if (cluster_id) {
+ os_memcpy(de->cluster_id, cluster_id, ETH_ALEN);
+ de->cluster_id_set = true;
+ } else {
+ de->cluster_id_set = false;
+ }
+}
+
+
+bool nan_de_is_valid_instance_id(struct nan_de *de, int handle,
+ bool publish, u8 *service_id)
+{
+ struct nan_de_service *srv;
+
+ if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
+ return false;
+
+ srv = de->service[handle - 1];
+ if (!srv)
+ return false;
+
+ if (publish && srv->type != NAN_DE_PUBLISH)
+ return false;
+ if (!publish && srv->type != NAN_DE_SUBSCRIBE)
+ return false;
+
+ os_memcpy(service_id, srv->service_id, NAN_SERVICE_ID_LEN);
+ return true;
+}
+
+
+u16 nan_de_get_service_bootstrap_methods(struct nan_de *de, int handle)
+{
+ struct nan_de_service *srv;
+
+ if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
+ return 0;
+
+ srv = de->service[handle - 1];
+ if (!srv)
+ return 0;
+
+ return srv->pbm;
+}
+
+
+bool nan_de_service_supports_csid(struct nan_de *de, int handle, int csid)
+{
+ struct nan_de_service *srv;
+
+ if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
+ return false;
+
+ srv = de->service[handle - 1];
+ if (!srv)
+ return false;
+
+ /* If cipher_suites_list is not set, all CSIDs are allowed */
+ if (!srv->cipher_suites_list)
+ return true;
+
+ /* Open is allowed only if security is not required */
+ if (csid == NAN_CS_NONE)
+ return !srv->security_required;
+
+ /* Check if the CSID is in the service's cipher suite list */
+ return int_array_includes(srv->cipher_suites_list, csid);
+}
+
+
+static const char * nan_de_service_type2str(enum nan_de_service_type type)
+{
+ switch (type) {
+ case NAN_DE_PUBLISH:
+ return "publish";
+ case NAN_DE_SUBSCRIBE:
+ return "subscribe";
+ }
+
+ return "unknown";
+}
+
+
+int nan_de_get_status(struct nan_de *de, char *buf, size_t buflen)
+{
+ char *pos, *end;
+ unsigned int i;
+ int ret;
+
+ if (!de)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ ret = os_snprintf(pos, end - pos, "num_services=%u\n",
+ de->num_service);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
+ struct nan_de_service *srv = de->service[i];
+
+ if (!srv)
+ continue;
+
+ ret = os_snprintf(pos, end - pos,
+ "service=%u type=%s name=%s sync=%d\n",
+ srv->id,
+ nan_de_service_type2str(srv->type),
+ srv->service_name ? srv->service_name : "",
+ srv->sync);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+void nan_de_set_tx_mcast_follow_up_prot(struct nan_de *de, bool prot)
+{
+ wpa_printf(MSG_DEBUG,
+ "NAN: Set tx_mcast_follow_up_dual_prot: %u->%u",
+ de->tx_mcast_follow_up_prot, prot);
+
+ de->tx_mcast_follow_up_prot = prot;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/contrib/wpa/src/common/nan_de.h b/contrib/wpa/src/common/nan_de.h
index 62235064b075..0599a808ef45 100644
--- a/contrib/wpa/src/common/nan_de.h
+++ b/contrib/wpa/src/common/nan_de.h
@@ -1,6 +1,6 @@
/*
* NAN Discovery Engine
- * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,7 +9,7 @@
#ifndef NAN_DE_H
#define NAN_DE_H
-#include "nan.h"
+#include "nan_defs.h"
/* Maximum number of active local publish and subscribe instances */
#ifndef NAN_DE_MAX_SERVICE
@@ -24,20 +24,37 @@ enum nan_de_reason {
NAN_DE_REASON_FAILURE,
};
+struct nan_discovery_result {
+ int subscribe_id;
+ enum nan_service_protocol_type srv_proto_type;
+ const u8 *ssi;
+ size_t ssi_len;
+ int peer_publish_id;
+ const u8 *peer_addr;
+ bool fsd;
+ bool fsd_gas;
+ bool data_path;
+ bool security_required;
+ const u8 *pmkid_list;
+ unsigned int pmkid_count;
+ const u8 *cipher_suites;
+ unsigned int n_cipher_suites;
+ bool pairing_setup_supp;
+ bool npk_nik_caching_supp;
+ u16 pbm;
+};
+
struct nan_callbacks {
void *ctx;
int (*tx)(void *ctx, unsigned int freq, unsigned int wait_time,
const u8 *dst, const u8 *src, const u8 *bssid,
const struct wpabuf *buf);
- int (*listen)(void *ctx, unsigned int freq, unsigned int duration);
+ int (*listen)(void *ctx, unsigned int freq, unsigned int duration,
+ const u8 *forced_addr);
/* NAN DE Events */
- void (*discovery_result)(void *ctx, int subscribe_id,
- enum nan_service_protocol_type srv_proto_type,
- const u8 *ssi, size_t ssi_len,
- int peer_publish_id,
- const u8 *peer_addr, bool fsd, bool fsd_gas);
+ void (*discovery_result)(void *ctx, struct nan_discovery_result *res);
void (*replied)(void *ctx, int publish_id, const u8 *peer_addr,
int peer_subscribe_id,
@@ -50,12 +67,33 @@ struct nan_callbacks {
void (*subscribe_terminated)(void *ctx, int subscribe_id,
enum nan_de_reason reason);
+ void (*offload_cancel_publish)(void *ctx, int publish_id);
+ void (*offload_cancel_subscribe)(void *ctx, int subscribe_id);
+
void (*receive)(void *ctx, int id, int peer_instance_id,
const u8 *ssi, size_t ssi_len,
- const u8 *peer_addr);
+ const u8 *peer_addr,
+ const u8 *buf, size_t len);
+
+ void (*process_p2p_usd_elems)(void *ctx, const u8 *buf,
+ u16 buf_len, const u8 *peer_addr,
+ unsigned int freq);
+
+ void (*process_pr_usd_elems)(void *ctx, const u8 *buf,
+ u16 buf_len, const u8 *peer_addr,
+ unsigned int freq);
+ void (*add_extra_attrs)(void *ctx, struct wpabuf *buf);
+ bool (*is_peer_paired)(void *ctx, const u8 *addr);
+ void (*transmit_req_status)(void *ctx, u32 cookie, bool ack);
};
-struct nan_de * nan_de_init(const u8 *nmi, bool ap,
+extern const u8 nan_network_id[ETH_ALEN];
+extern const u8 p2p_network_id[ETH_ALEN];
+
+bool nan_de_is_nan_network_id(const u8 *addr);
+bool nan_de_is_p2p_network_id(const u8 *addr);
+struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+ unsigned int max_listen,
const struct nan_callbacks *cb);
void nan_de_flush(struct nan_de *de);
void nan_de_deinit(struct nan_de *de);
@@ -63,11 +101,14 @@ void nan_de_deinit(struct nan_de *de);
void nan_de_listen_started(struct nan_de *de, unsigned int freq,
unsigned int duration);
void nan_de_listen_ended(struct nan_de *de, unsigned int freq);
-void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst);
+void nan_de_update_nmi(struct nan_de *de, const u8 *nmi);
+void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst,
+ const u8 *data, size_t data_len, bool ack);
void nan_de_tx_wait_ended(struct nan_de *de);
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
- const u8 *buf, size_t len);
+bool nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+ unsigned int freq, const u8 *buf, size_t len, int rssi);
+const u8 * nan_de_get_service_id(struct nan_de *de, int id);
struct nan_publish_params {
/* configuration_parameters */
@@ -99,19 +140,68 @@ struct nan_publish_params {
/* Announcement period in ms; 0 = use default */
unsigned int announcement_period;
+
+ /* Proximity ranging flag */
+ bool proximity_ranging;
+
+ /* Synchronized discovery */
+ bool sync;
+
+ /*
+ * Null-terminated string containing the hex-encoded
+ * representation of the matching filters.
+ */
+ const char *match_filter_tx;
+ const char *match_filter_rx;
+
+ /* RSSI range limit */
+ bool close_proximity;
+
+ /* Source MAC address for this service (optional) */
+ const u8 *forced_addr;
+
+ /*
+ * Pairing Bootstrapping Methods as defined in Wi-Fi Aware spec v4.0,
+ * Table 128
+ */
+ u16 pbm;
+
+ /* int_array of cipher suites */
+ const int *cipher_suites_list;
+
+ /* Bitmap of NAN_CS_INFO_CAPA_* */
+ u8 security_capab;
+
+ /* ND-PMK to use for creating a list of PMKIDs for the service */
+ const u8 *nd_pmk;
+
+ /*
+ * GTK protection required for group-addressed Data frames transmitted
+ * and received for the service
+ */
+ bool gtk_required;
+
+ /* Request NAN Data Path */
+ bool data_path;
+
+ bool security_required;
};
/* Returns -1 on failure or >0 publish_id */
int nan_de_publish(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
- struct nan_publish_params *params);
+ struct nan_publish_params *params, bool p2p,
+ const u8 *addr);
void nan_de_cancel_publish(struct nan_de *de, int publish_id);
int nan_de_update_publish(struct nan_de *de, int publish_id,
const struct wpabuf *ssi);
+int nan_de_unpause_publish(struct nan_de *de, int publish_id,
+ u8 peer_instance_id, const u8 *peer_addr);
+
struct nan_subscribe_params {
/* configuration_parameters */
@@ -124,15 +214,62 @@ struct nan_subscribe_params {
/* Selected frequency */
unsigned int freq;
+ /* Multi-channel frequencies (publishChannelList) */
+ const int *freq_list;
+
/* Query period in ms; 0 = use default */
unsigned int query_period;
+
+ /* Proximity ranging flag */
+ bool proximity_ranging;
+
+ /* Synchronized discovery */
+ bool sync;
+
+ /*
+ * Null-terminated string containing the hex-encoded
+ * representation of the matching filters.
+ */
+ const char *match_filter_tx;
+ const char *match_filter_rx;
+
+ /* Service response filter include flag */
+ bool srf_include;
+
+ /* Service response filter MAC list */
+ const char *srf_mac_list;
+
+ /* Bloom filter length in octets. If 0, MAC list is used instead */
+ u8 srf_bf_len;
+
+ /* Bloom filter index (0-3) */
+ u8 srf_bf_idx;
+
+ /* RSSI range limit */
+ bool close_proximity;
+
+ /* Source MAC address for this service (optional) */
+ const u8 *forced_addr;
+
+ /*
+ * Pairing Bootstrapping Methods as defined in Wi-Fi Aware spec v4.0,
+ * Table 128
+ */
+ u16 pbm;
+
+ /*
+ * GTK protection required for group-addressed Data frames transmitted
+ * and received for the service
+ */
+ bool gtk_required;
};
/* Returns -1 on failure or >0 subscribe_id */
int nan_de_subscribe(struct nan_de *de, const char *service_name,
enum nan_service_protocol_type srv_proto_type,
const struct wpabuf *ssi, const struct wpabuf *elems,
- struct nan_subscribe_params *params);
+ struct nan_subscribe_params *params, bool p2p,
+ const u8 *addr);
void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id);
@@ -140,6 +277,30 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id);
* req_instance_id = peer publish_id or subscribe_id */
int nan_de_transmit(struct nan_de *de, int handle,
const struct wpabuf *ssi, const struct wpabuf *elems,
- const u8 *peer_addr, u8 req_instance_id);
+ const u8 *peer_addr, u8 req_instance_id,
+ const struct wpabuf *nan_attrs, u32 *cookie);
+
+void nan_de_dw_trigger(struct nan_de *de, int freq);
+void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id);
+bool nan_de_is_valid_instance_id(struct nan_de *de, int handle,
+ bool publish, u8 *service_id);
+u16 nan_de_get_service_bootstrap_methods(struct nan_de *de, int handle);
+bool nan_de_service_supports_csid(struct nan_de *de, int handle, int csid);
+void nan_de_set_tx_mcast_follow_up_prot(struct nan_de *de, bool prot);
+int nan_de_get_status(struct nan_de *de, char *buf, size_t buflen);
+
+int nan_de_stop_listen(struct nan_de *de, int handle);
+
+struct nan_de_cfg {
+ /* N and M minimal and maximal values */
+ u32 n_min, n_max;
+
+ /* When not in pause state, stop the DE radio usage for 'suspend' ms
+ * every 'cycle' ms.
+ */
+ u32 suspend, cycle;
+};
+
+int nan_de_config(struct nan_de *de, struct nan_de_cfg *cfg);
#endif /* NAN_DE_H */
diff --git a/contrib/wpa/src/common/nan_defs.h b/contrib/wpa/src/common/nan_defs.h
new file mode 100644
index 000000000000..88b46eaf3ce0
--- /dev/null
+++ b/contrib/wpa/src/common/nan_defs.h
@@ -0,0 +1,663 @@
+/*
+ * NAN (Wi-Fi Aware) definitions
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * Copyright (c) 2026, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NAN_DEFS_H
+#define NAN_DEFS_H
+
+/* Wi-Fi Aware spec v4.0, Table 35 (NAI Action frame subtypes) */
+enum nan_subtype {
+ NAN_SUBTYPE_INVALID = 0,
+ NAN_SUBTYPE_RANGING_REQUEST = 1,
+ NAN_SUBTYPE_RANGING_RESPONSE = 2,
+ NAN_SUBTYPE_RANGING_TERMINATION = 3,
+ NAN_SUBTYPE_RANGING_REPORT = 4,
+ NAN_SUBTYPE_DATA_PATH_REQUEST = 5,
+ NAN_SUBTYPE_DATA_PATH_RESPONSE = 6,
+ NAN_SUBTYPE_DATA_PATH_CONFIRM = 7,
+ NAN_SUBTYPE_DATA_PATH_KEY_INSTALL = 8,
+ NAN_SUBTYPE_DATA_PATH_TERMINATION = 9,
+ NAN_SUBTYPE_SCHEDULE_REQUEST = 10,
+ NAN_SUBTYPE_SCHEDULE_RESPONSE = 11,
+ NAN_SUBTYPE_SCHEDULE_CONFIRM = 12,
+ NAN_SUBTYPE_SCHEDULE_UPDATE_NOTIF = 13,
+};
+
+enum nan_attr_id {
+ NAN_ATTR_MASTER_INDICATION = 0x00,
+ NAN_ATTR_CLUSTER = 0x01,
+ NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST = 0x02,
+ NAN_ATTR_SDA = 0x03, /* Service Descriptor attribute */
+ NAN_ATTR_CONN_CAPA = 0x04,
+ NAN_ATTR_WLAN_INFRA = 0x05,
+ NAN_ATTR_P2P_OPER = 0x06,
+ NAN_ATTR_IBSS = 0x07,
+ NAN_ATTR_MESH = 0x08,
+ NAN_ATTR_FURTHER_NAN_SD = 0x09,
+ NAN_ATTR_FURTHER_AVAIL_MAP = 0x0A,
+ NAN_ATTR_COUNTRY_CODE = 0x0B,
+ NAN_ATTR_RANGING = 0x0C,
+ NAN_ATTR_CLUSTER_DISCOVERY = 0x0D,
+ NAN_ATTR_SDEA = 0x0E, /* Service Descriptor Extension attribute */
+ NAN_ATTR_DEVICE_CAPABILITY = 0x0F,
+ NAN_ATTR_NDP = 0x10,
+ NAN_ATTR_NAN_AVAILABILITY = 0x12,
+ NAN_ATTR_NDC = 0x13,
+ NAN_ATTR_NDL = 0x14,
+ NAN_ATTR_NDL_QOS = 0x15,
+ NAN_ATTR_UNALIGNED_SCHEDULE = 0x17,
+ NAN_ATTR_RANGING_INFO = 0x1A,
+ NAN_ATTR_RANGING_SETUP = 0x1B,
+ NAN_ATTR_FTM_RANGING_REPORT = 0x1C,
+ NAN_ATTR_ELEM_CONTAINER = 0x1D,
+ NAN_ATTR_EXT_WLAN_INFRA = 0x1E,
+ NAN_ATTR_EXT_P2P_OPER = 0x1F,
+ NAN_ATTR_EXT_IBSS = 0x20,
+ NAN_ATTR_EXT_MESH = 0x21,
+ NAN_ATTR_CSIA = 0x22, /* Cipher Suite Info attribute */
+ NAN_ATTR_SCIA = 0x23, /* Security Context Info attribute */
+ NAN_ATTR_SHARED_KEY_DESCR = 0x24,
+ NAN_ATTR_PUBLIC_AVAILABILITY = 0x27,
+ NAN_ATTR_SUBSC_SERVICE_ID_LIST = 0x28,
+ NAN_ATTR_NDP_EXT = 0x29,
+ NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */
+ NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */
+ NAN_ATTR_NPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
+ NAN_ATTR_S3 = 0x2D,
+ NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
+ NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
+};
+
+/* Wi-Fi Aware spec v4.0, Table 43 (Reason Code field) */
+enum nan_reason {
+ NAN_REASON_RESERVED = 0,
+ NAN_REASON_UNSPECIFIED_REASON = 1,
+ NAN_REASON_RESOURCE_LIMITATION = 2,
+ NAN_REASON_INVALID_PARAMETERS = 3,
+ NAN_REASON_FTM_PARAMETERS_INCAPABLE = 4,
+ NAN_REASON_NO_MOVEMENT = 5,
+ NAN_REASON_INVALID_AVAILABILITY = 6,
+ NAN_REASON_IMMUTABLE_UNACCEPTABLE = 7,
+ NAN_REASON_SECURITY_POLICY = 8,
+ NAN_REASON_QOS_UNACCEPTABLE = 9,
+ NAN_REASON_NDP_REJECTED = 10,
+ NAN_REASON_NDL_UNACCEPTABLE = 11,
+ NAN_REASON_RANGING_SCHED_NOT_ACCEPT = 12,
+ NAN_REASON_PAIR_BOOTSTRAP_REJECTED = 13,
+};
+
+/* Service Descriptor attribute (SDA) */
+
+/* Service Control field */
+#define NAN_SRV_CTRL_TYPE_MASK (BIT(0) | BIT(1))
+#define NAN_SRV_CTRL_MATCHING_FILTER BIT(2)
+#define NAN_SRV_CTRL_RESP_FILTER BIT(3)
+#define NAN_SRV_CTRL_SRV_INFO BIT(4)
+#define NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED BIT(5)
+#define NAN_SRV_CTRL_BINDING_BITMAP BIT(6)
+
+enum nan_service_control_type {
+ NAN_SRV_CTRL_PUBLISH = 0,
+ NAN_SRV_CTRL_SUBSCRIBE = 1,
+ NAN_SRV_CTRL_FOLLOW_UP = 2,
+};
+
+/* Service Descriptor Extension attribute (SDEA) */
+
+/* SDEA Control field */
+#define NAN_SDEA_CTRL_FSD_REQ BIT(0)
+#define NAN_SDEA_CTRL_FSD_GAS BIT(1)
+#define NAN_SDEA_CTRL_DATA_PATH_REQ BIT(2)
+#define NAN_SDEA_CTRL_DATA_PATH_TYPE BIT(3)
+#define NAN_SDEA_CTRL_QOS_REQ BIT(5)
+#define NAN_SDEA_CTRL_SECURITY_REQ BIT(6)
+#define NAN_SDEA_CTRL_RANGING_REQ BIT(7)
+#define NAN_SDEA_CTRL_RANGE_LIMIT BIT(8)
+#define NAN_SDEA_CTRL_SRV_UPD_INDIC BIT(9)
+#define NAN_SDEA_CTRL_GTK_REQ BIT(10)
+
+enum nan_service_protocol_type {
+ NAN_SRV_PROTO_BONJOUR = 1,
+ NAN_SRV_PROTO_GENERIC = 2,
+ NAN_SRV_PROTO_CSA_MATTER = 3,
+};
+
+/* SRF Control field */
+/* bit 0 = 0: Address Set is a sequence of MAC Addresses
+ * bit 0 = 1: Address Set is a Bloom filter */
+#define NAN_SRF_CTRL_BF BIT(0)
+#define NAN_SRF_CTRL_INCLUDE BIT(1)
+
+/* bit 2-3: Bloom Filter Index */
+#define NAN_SRF_CTRL_BF_IDX_MSK (BIT(0) | BIT(1))
+#define NAN_SRF_CTRL_BF_IDX_POS 2
+
+/* Wi-Fi Aware spec v4.0, Table 128 (NPBA format) */
+#define NAN_PBA_TYPE_MASK 0xf
+#define NAN_PBA_STATUS_MASK 0xf
+#define NAN_PBA_STATUS_POS 4
+
+enum nan_pba_type {
+ NAN_PBA_TYPE_ADVERTISE = 0,
+ NAN_PBA_TYPE_REQUEST = 1,
+ NAN_PBA_TYPE_RESPONSE = 2,
+};
+
+enum nan_pba_status {
+ NAN_PBA_STATUS_ACCEPTED = 0,
+ NAN_PBA_STATUS_REJECTED = 1,
+ NAN_PBA_STATUS_COMEBACK = 2,
+};
+
+enum nan_pairing_bootstrapping_method {
+ NAN_PBA_METHOD_OPPORTUNISTIC = BIT(0),
+ NAN_PBA_METHOD_PIN_DISPLAY = BIT(1),
+ NAN_PBA_METHOD_PASSPHRASE_DISPLAY = BIT(2),
+ NAN_PBA_METHOD_QR_DISPLAY = BIT(3),
+ NAN_PBA_METHOD_NFC_TAG = BIT(4),
+ NAN_PBA_METHOD_PIN_KEYPAD = BIT(5),
+ NAN_PBA_METHOD_PASSPHRASE_KEYPAD = BIT(6),
+ NAN_PBA_METHOD_QR_SCAN = BIT(7),
+ NAN_PBA_METHOD_NFC_READER = BIT(8),
+ NAN_PBA_METHOD_SERVICE_MANAGED = BIT(14),
+ NAN_PBA_METHOD_HANDSHAKE_SKIPPED = BIT(15),
+};
+
+#define NAN_ATTR_HDR_LEN 3
+#define NAN_SERVICE_ID_LEN 6
+
+#define NAN_USD_DEFAULT_FREQ 2437
+
+/* Map ID
+ * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */
+#define NAN_DEV_CAPA_MAP_ID_DONT_APPLY_ALL BIT(0)
+#define NAN_DEV_CAPA_MAP_ID_POS 1
+#define NAN_DEV_CAPA_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) \
+ BIT(4))
+
+/* Supported Bands:
+ * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */
+#define NAN_DEV_CAPA_SBAND_SUB_1G BIT(1)
+#define NAN_DEV_CAPA_SBAND_2G BIT(2)
+#define NAN_DEV_CAPA_SBAND_5G BIT(4)
+#define NAN_DEV_CAPA_SBAND_6G BIT(7)
+
+/* Wi-Fi Aware spec v4.0, Table 80 (Committed DW Information field format) */
+#define NAN_CDW_INFO_2G_POS 0
+#define NAN_CDW_INFO_2G_MASK (BIT(0) | BIT(1) | BIT(2))
+#define NAN_CDW_INFO_5G_POS 3
+#define NAN_CDW_INFO_5G_MASK (BIT(3) | BIT(4) | BIT(5))
+#define NAN_CDW_INFO_2G_OVERRIDE_POS 6
+#define NAN_CDW_INFO_2G_OVERRIDE_MASK (BIT(6) | BIT(7) | BIT(8) | BIT(9))
+#define NAN_CDW_INFO_5G_OVERRIDE_POS 10
+#define NAN_CDW_INFO_5G_OVERRIDE_MASK (BIT(10) | BIT(11) | BIT(12) | BIT(13))
+
+/* Wi-Fi Aware spec v4.0, Table 81 (Operation Mode field format) */
+#define NAN_DEV_CAPA_OP_MODE_PHY_MODE (BIT(0) | BIT(4))
+#define NAN_DEV_CAPA_OP_MODE_PHY_MODE_VHT BIT(0)
+#define NAN_DEV_CAPA_OP_MODE_HE_VHT_80P80 BIT(1)
+#define NAN_DEV_CAPA_OP_MODE_HE_VHT_160 BIT(2)
+#define NAN_DEV_CAPA_OP_MODE_PHY_MODE_HE BIT(4)
+
+/* Number of Antennas
+ * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */
+#define NAN_DEV_CAPA_TX_ANT_POS 0
+#define NAN_DEV_CAPA_TX_ANT_MASK 0x0f
+#define NAN_DEV_CAPA_RX_ANT_POS 4
+#define NAN_DEV_CAPA_RX_ANT_MASK 0xf0
+
+/* Capabilities
+ * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */
+#define NAN_DEV_CAPA_DFS_MASTER BIT(0)
+#define NAN_DEV_CAPA_EXT_KEY_ID BIT(1)
+#define NAN_DEV_CAPA_SIM_NDP_RX BIT(2)
+#define NAN_DEV_CAPA_NDPE_ATTR_SUPP BIT(3)
+#define NAN_DEV_CAPA_S3 BIT(4)
+
+/* Device Capability attribute
+ * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */
+struct nan_device_capa {
+ u8 map_id; /* Map ID */
+ le16 cdw_info; /* Committed DW Info */
+ u8 supported_bands; /* Supported Bands */
+ u8 op_mode; /* Operation Mode */
+ u8 ant; /* Number of Antennas */
+ le16 channel_switch_time; /* Max Channel Switch Time */
+ u8 capa; /* Capabilities */
+} STRUCT_PACKED;
+
+/* NAN Data Path (NDP) attribute
+ * Wi-Fi Aware spec v4.0, Table 82 (NDP attribute format) */
+#define NAN_NDP_TYPE_POS 0
+#define NAN_NDP_TYPE_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+#define NAN_NDP_STATUS_POS 4
+#define NAN_NDP_STATUS_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
+
+/* NDP type (in Type and Status field) */
+enum nan_ndp_type {
+ NAN_NDP_TYPE_REQUEST = 0,
+ NAN_NDP_TYPE_RESPONSE = 1,
+ NAN_NDP_TYPE_CONFIRM = 2,
+ NAN_NDP_TYPE_SECURITY_INSTALL = 3,
+ NAN_NDP_TYPE_TERMINATE = 4,
+};
+
+/* NDP status (in Type and Status field) */
+enum nan_ndp_status {
+ NAN_NDP_STATUS_CONTINUED = 0,
+ NAN_NDP_STATUS_ACCEPTED = 1,
+ NAN_NDP_STATUS_REJECTED = 2,
+};
+
+/* Wi-Fi Aware spec v4.0, Table 84 (NDP Control field) and Table 87 (Subfields
+ * of NDPE Control field format).
+ * Bits 0-4 are identical for NDP and NDPE. The only differences are in
+ * Bit 5.
+ */
+#define NAN_NDP_CTRL_CONFIRM_REQUIRED BIT(0)
+#define NAN_NDP_CTRL_SECURITY_PRESENT BIT(2)
+#define NAN_NDP_CTRL_PUBLISH_ID_PRESENT BIT(3)
+#define NAN_NDP_CTRL_RESPONDER_NDI_PRESENT BIT(4)
+
+#define NAN_NDP_CTRL_SPEC_INFO_PRESENT BIT(5)
+#define NAN_NDPE_CTRL_GTK_REQUIRED BIT(5)
+
+/* Wi-Fi Aware spec v4.0, Table 82 (NDP attribute format) and
+ * Table 86 (NAN Data Path Extension attribute format)
+ * Note: The structure does not include the Attribute ID and Length fields. */
+struct ieee80211_ndp {
+ u8 dialog_token; /* Dialog Token */
+ u8 type_and_status; /* Type and Status */
+ u8 reason_code; /* Reason Code */
+ u8 initiator_ndi[ETH_ALEN]; /* Initiator NDI */
+ u8 ndp_id; /* NDP ID */
+ u8 ndp_ctrl; /* NDP Control */
+
+ /* followed by optional fields based on ndp_ctrl */
+ u8 optional[0];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 89 (List of TLV types for the NDPE attribute) */
+enum nan_ndpe_tlv_type {
+ NAN_NDPE_TLV_IPV6_LINK_LOCAL = 0,
+ NAN_NDPE_TLV_SRV_INFO = 1,
+};
+
+#define NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN 8
+
+/* Wi-Fi Aware spec v4.0, Table 97 (Time Bitmap Control field format for the
+ * NAN Availability attribute) */
+#define NAN_TIME_BM_CTRL_BIT_DURATION_POS 0
+#define NAN_TIME_BM_CTRL_BIT_DURATION_MASK (BIT(0) | BIT(1) | BIT(2))
+#define NAN_TIME_BM_CTRL_BIT_DURATION_16_TU 0
+#define NAN_TIME_BM_CTRL_BIT_DURATION_32_TU 1
+#define NAN_TIME_BM_CTRL_BIT_DURATION_64_TU 2
+#define NAN_TIME_BM_CTRL_BIT_DURATION_128_TU 3
+
+#define NAN_TIME_BM_CTRL_PERIOD_POS 3
+#define NAN_TIME_BM_CTRL_PERIOD_MASK (BIT(3) | BIT(4) | BIT(5))
+#define NAN_TIME_BM_CTRL_PERIOD_NONE 0
+
+#define NAN_TIME_BM_CTRL_START_OFFSET_POS 6
+#define NAN_TIME_BM_CTRL_START_OFFSET_MASK (BIT(6) | BIT(7) | BIT(8) | \
+ BIT(9) | BIT(10) | BIT(11) | \
+ BIT(12) | BIT(13) | BIT(14))
+
+/* Wi-Fi Aware spec v4.0, Table 96 (Entry Control field format for the
+ * NAN Availability attribute) */
+#define NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED BIT(0)
+#define NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL BIT(1)
+#define NAN_AVAIL_ENTRY_CTRL_TYPE_COND BIT(2)
+#define NAN_AVAIL_ENTRY_CTRL_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+
+#define NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS 3
+#define NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_MASK (BIT(3) | BIT(4))
+#define NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN 7
+#define NAN_AVAIL_ENTRY_CTRL_UTIL_MAX 5
+#define NAN_AVAIL_ENTRY_CTRL_UTIL_POS 5
+#define NAN_AVAIL_ENTRY_CTRL_UTIL_MASK (BIT(5) | BIT(6) | BIT(7))
+#define NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS 8
+#define NAN_AVAIL_ENTRY_CTRL_RX_NSS_MASK (BIT(8) | BIT(9) | BIT(10) | \
+ BIT(11))
+#define NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT BIT(12)
+
+/* Wi-Fi Aware spec v4.0, Table 99 (List of Band Entries) */
+enum nan_band_entry {
+ NAN_BAND_ENTRY_SUB_1G = 1,
+ NAN_BAND_ENTRY_2G = 2,
+ NAN_BAND_ENTRY_5G = 4,
+ NAN_BAND_ENTRY_6G = 7,
+};
+
+/* Wi-Fi Aware spec v4.0, Table 100 (Channel Entry format for the
+ * NAN Availability attribute) */
+struct nan_chan_entry {
+ u8 op_class; /* Operating Class */
+ le16 chan_bitmap; /* Channel Bitmap */
+ u8 pri_chan_bitmap; /* Primary Channel Bitmap */
+
+ /* This field is optional. It is present only if
+ * NAN_BAND_CHAN_CTRL_NON_CONT_BW is set
+ */
+ le16 aux_chan_bitmap; /* Auxiliary Channel Bitmap */
+} STRUCT_PACKED;
+
+/* Channel entry only contains the aux_chan_bitmap field for 80+80 MHz operating
+ * class. See Table 100. */
+#define NAN_CHAN_ENTRY_MIN_LEN 4
+#define NAN_CHAN_ENTRY_80P80_LEN 6
+
+/* Wi-Fi Aware spec v4.0, Table 98 (Band/Channel Entries List field format for
+ * the NAN Availability attribute) */
+#define NAN_BAND_CHAN_CTRL_TYPE BIT(0)
+#define NAN_BAND_CHAN_CTRL_NON_CONT_BW BIT(1)
+#define NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS 4
+#define NAN_BAND_CHAN_CTRL_NUM_ENTRIES_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
+
+struct nan_band_chan_list {
+ u8 ctrl;
+ u8 entries[0];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 95 (Availability Entry field format for the
+ * NAN Availability attribute)
+ */
+struct nan_avail_ent {
+ le16 len;
+ le16 ctrl; /* Entry Control */
+
+ /* followed by optional fields based on ctrl. Note that this also
+ * includes the inclusion of Time Bitmap Control and Length fields
+ */
+ u8 optional[0];
+} STRUCT_PACKED;
+
+#define MIN_AVAIL_ENTRY_LEN 2
+
+/* This structure represents a time bitmap related fields in the NAN
+ * Availability entry. */
+struct nan_tbm {
+ le16 ctrl;
+ u8 len;
+ u8 bitmap[0];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 94 (Attribute Control field format for the
+ * NAN Availability attribute) */
+#define NAN_AVAIL_CTRL_MAP_ID_POS 0
+#define NAN_AVAIL_CTRL_MAP_ID_MASK 0xf
+#define NAN_AVAIL_CTRL_COMMITTED_CHANGED BIT(4)
+#define NAN_AVAIL_CTRL_POTENTIAL_CHANGED BIT(5)
+#define NAN_AVAIL_CTRL_PUB_AVAIL_ATTR_CHANGED BIT(6)
+#define NAN_AVAIL_CTRL_NDC_ATTR_CHANGED BIT(7)
+
+/* Wi-Fi Aware spec v4.0, Table 93 (NAN Availability attribute format).
+ * Attribute ID and Length fields not included. */
+struct nan_avail {
+ u8 seq_id; /* Sequence ID */
+ le16 ctrl; /* Attribute Control */
+
+ /* followed by Availability Entry List */
+ u8 optional[0];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 110 (Attribute Control field format for the
+ * Unaligned Schedule attribute)
+ */
+#define NAN_UNALIGNED_SCHED_CTRL_SCHED_ID_MASK (BIT(0) | BIT(1) | BIT(2) | \
+ BIT(3))
+
+/* Wi-Fi Aware spec v4.0, Table 111 (ULW Overwrite field format) */
+#define NAN_ULW_OVERWRITE_ALL BIT(0)
+#define NAN_ULW_OVERWRITE_MAP_ID_POS 1
+#define NAN_ULW_OVERWRITE_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) | BIT(4))
+
+/* Wi-Fi Aware spec v4.0, Table 112 (ULW Control field format) */
+#define NAN_ULW_CTRL_TYPE_POS 0
+#define NAN_ULW_CTRL_TYPE_MASK (BIT(0) | BIT(1))
+#define NAN_ULW_CTRL_TYPE_BAND_ID 0
+#define NAN_ULW_CTRL_TYPE_CHAN_ENTRY 1
+#define NAN_ULW_CTRL_TYPE_CHAN_ENTRY_WITH_AUX 2
+#define NAN_ULW_CTRL_CHAN_AVAIL BIT(2)
+#define NAN_ULW_CTRL_RX_NSS_POS 3
+#define NAN_ULW_CTRL_RX_NSS_MASK (BIT(3) | BIT(4) | BIT(5) | BIT(6))
+
+/* Wi-Fi Aware spec v4.0, Table 109 (Unaligned Schedule attribute format).
+ * ID and length are not included.
+ */
+struct nan_unaligned_sched {
+ u8 sched_id;
+ u8 seq_id;
+ le32 start_time;
+ le32 duration;
+ le32 period;
+ u8 count_down;
+ u8 ulw_overwrite;
+
+ /* followed by optional fields (ULW Control and Band ID or Channel
+ * Entry) */
+ u8 optional[0];
+} STRUCT_PACKED;
+
+#define NAN_SCHED_ENTRY_MAP_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
+/* Wi-Fi Aware spec v4.0, Table 104 (Schedule Entry format for the NDC
+ * attribute) */
+struct nan_sched_entry {
+ u8 map_id; /* Map ID */
+ le16 control; /* Time Bitmap Control */
+ u8 len; /* Time Bitmap Length */
+ u8 bm[0]; /* Time Bitmap */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 103 (Attribute Control field format for the NDC
+ * attribute) */
+#define NAN_NDC_CTRL_SELECTED BIT(0)
+
+/* Wi-Fi Aware spec v4.0, Table 102 (NDC attribute format).
+ * Attribute ID and Length fields not included */
+struct ieee80211_ndc {
+ u8 ndc_id[ETH_ALEN]; /* NDC ID */
+ u8 ctrl; /* Attribute Control */
+ u8 sched_entries[0]; /* Schedule Entry List */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 105 (NDL attribute format) */
+#define NAN_NDL_TYPE_POS 0
+#define NAN_NDL_TYPE_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+#define NAN_NDL_STATUS_POS 4
+#define NAN_NDL_STATUS_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
+
+enum nan_ndl_type {
+ NAN_NDL_TYPE_REQUEST = 0,
+ NAN_NDL_TYPE_RESPONSE = 1,
+ NAN_NDL_TYPE_CONFIRM = 2,
+};
+
+enum nan_ndl_status {
+ NAN_NDL_STATUS_CONTINUED = 0,
+ NAN_NDL_STATUS_ACCEPTED = 1,
+ NAN_NDL_STATUS_REJECTED = 2,
+};
+
+/* Wi-Fi Aware spec v4.0, Table 107 (NDL Control field format) */
+#define NAN_NDL_CTRL_PEER_ID_PRESENT BIT(0)
+#define NAN_NDL_CTRL_IMMUT_SCHED_PRESENT BIT(1)
+#define NAN_NDL_CTRL_NDC_ATTR_PRESENT BIT(2)
+#define NAN_NDL_CTRL_NDL_QOS_ATTR_PRESENT BIT(3)
+#define NAN_NDL_CTRL_MAX_IDLE_PERIOD_PRESENT BIT(4)
+#define NAN_NDL_CTRL_NDL_TYPE BIT(5)
+#define NAN_NDL_CTRL_NDL_SETUP_REASON_POS 6
+#define NAN_NDL_CTRL_NDL_SETUP_REASON_MASK (BIT(6) | BIT(7))
+
+#define NAN_NDL_CTRL_NDL_SETUP_REASON_NDP 0x0
+#define NAN_NDL_CTRL_NDL_SETUP_REASON_FSD_GAS 0x1
+
+/* Wi-Fi Aware spec v4.0, Table 105 (NDL attribute format) */
+struct ieee80211_ndl {
+ u8 dialog_token; /* Dialog Token */
+ u8 type_and_status; /* Type and Status */
+ u8 reason_code; /* Reason Code */
+ u8 ctrl; /* NDL Control */
+
+ /* followed by optional fields based on ndl_ctrl */
+ u8 optional[0];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 130 (Element Container attribute format) */
+#define NAN_ELEMENT_CONTAINER_MAP_ID_VALID_POS 0
+#define NAN_ELEMENT_CONTAINER_MAP_ID_VALID_MASK BIT(0)
+#define NAN_ELEMENT_CONTAINER_MAP_ID_POS 1
+#define NAN_ELEMENT_CONTAINER_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) |\
+ BIT(4))
+
+struct ieee80211_elemc {
+ u8 map_id; /* Map ID */
+ u8 variable[0]; /* Elements */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 108 (NDL QoS attribute format) */
+struct ieee80211_nan_qos {
+ u8 min_slots; /* Minimum time slots */
+ le16 max_latency; /* Maximum latency */
+} STRUCT_PACKED;
+
+#define NAN_QOS_MIN_SLOTS_NO_PREF 0
+#define NAN_QOS_MAX_LATENCY_NO_PREF 0xffff
+
+#define NAN_AUTH_TOKEN_LEN 16
+#define NAN_KEY_MIC_LEN 16
+#define NAN_KEY_MIC_24_LEN 24
+
+/* Wi-Fi Aware spec v4.0, Table 121 (Cipher Suite attribute field format) */
+enum nan_cipher_suite_id {
+ NAN_CS_NONE = 0,
+ NAN_CS_SK_CCM_128 = 1,
+ NAN_CS_SK_GCM_256 = 2,
+ NAN_CS_PK_2WDH_128 = 3,
+ NAN_CS_PK_2WDH_256 = 4,
+ NAN_CS_GTK_CCMP_128 = 5,
+ NAN_CS_GTK_GCMP_256 = 6,
+ NAN_CS_PK_PASN_128 = 7,
+ NAN_CS_PK_PASN_256 = 8,
+ /* Keep last */
+ NAN_CS_MAX
+};
+
+/* Helper macros to check CSID properties */
+#define NAN_CS_IS_128(csid) \
+ ((csid) == NAN_CS_SK_CCM_128 || (csid) == NAN_CS_PK_PASN_128)
+#define NAN_CS_IS_256(csid) \
+ ((csid) == NAN_CS_SK_GCM_256 || (csid) == NAN_CS_PK_PASN_256)
+#define NAN_CS_IS_VALID_NDP(csid) \
+ (NAN_CS_IS_128(csid) || NAN_CS_IS_256(csid))
+#define NAN_CS_IS_PASN(csid) \
+ ((csid) == NAN_CS_PK_PASN_128 || (csid) == NAN_CS_PK_PASN_256)
+
+struct nan_cipher_suite {
+ u8 csid; /* Cipher Suite ID */
+ u8 instance_id; /* Publish ID */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 122 (Cipher Suite Information attribute (CSIA)
+ * field format) */
+#define NAN_CS_INFO_CAPA_16_ND_TKSA_REPLAY_COUNTERS BIT(0)
+#define NAN_CS_INFO_CAPA_GTK_SUPP_POS 1
+#define NAN_CS_INFO_CAPA_GTK_SUPP_MASK (BIT(1) | BIT(2))
+#define NAN_CS_INFO_CAPA_GTK_SUPP_NONE 0
+#define NAN_CS_INFO_CAPA_GTK_SUPP_NO_BIGTK 1
+#define NAN_CS_INFO_CAPA_GTK_SUPP_ALL 2
+#define NAN_CS_INFO_CAPA_16_REPLAY_COUNTERS BIT(3)
+#define NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256 BIT(4)
+
+struct nan_cipher_suite_info {
+ u8 capab; /* Capabilities */
+ u8 cs[0]; /* Cipher Suite List */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 123 (Security Context Identifier (SCID) field
+ * format) */
+enum nan_sec_ctx_type {
+ NAN_SEC_CTX_TYPE_ND_PMKID = 1,
+};
+
+struct nan_sec_ctxt {
+ le16 len; /* Security Context Identifier Type Length */
+ u8 scid; /* Security Context Identifier Type */
+ u8 instance_id; /* Publish ID */
+ u8 ctxt[0]; /* Security Context Identifier */
+} STRUCT_PACKED;
+
+/* Only key descriptor type 2 is supported */
+#define NAN_KEY_DESC 2
+
+/* Wi-Fi Aware spec v4.0, Table 125 (NAN Shared Key Descriptor attribute field
+ * format) */
+struct nan_shared_key {
+ u8 publish_id; /* Publish ID */
+
+ /*
+ * The format of the key is as defined in the IEEE 802.11 standard,
+ * starting with the Descriptor Type field. See struct wpa_eapol_key.
+ */
+ u8 key[0]; /* IEEE 802.11 RSNA Key Descriptor */
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Table 141 (Capability Info field) */
+#define NAN_DEV_CAPA_EXT_INFO_0_REG_VALID BIT(0)
+#define NAN_DEV_CAPA_EXT_INFO_0_AP_TYPE_POS 1
+#define NAN_DEV_CAPA_EXT_INFO_0_AP_TYPE_MASK (BIT(1) | BIT(2) | BIT(3))
+#define NAN_DEV_CAPA_EXT_INFO_INDOOR_AP 0
+#define NAN_DEV_CAPA_EXT_INFO_SP_AP 1
+#define NAN_DEV_CAPA_EXT_INFO_VLP_AP 2
+#define NAN_DEV_CAPA_EXT_INFO_INDOOR_ENABLED_AP 3
+#define NAN_DEV_CAPA_EXT_INFO_INDOOR_SP_AP 4
+
+#define NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP BIT(0)
+#define NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING BIT(1)
+
+/* Wi-Fi Aware spec v4.0, Table 22 (NIRA Cryptographic Parameters and Methods)
+ */
+#define NAN_NIRA_NONCE_LEN 8
+#define NAN_NIRA_TAG_LEN 8
+#define NAN_NIRA_STR "NIR"
+#define NAN_NIRA_STR_LEN 3
+#define NAN_NIRA_CIPHER_VER_128 0
+#define NAN_NIK_LEN 16
+
+#define NAN_PASN_SSID "516F9A010000"
+
+/* Wi-Fi Aware spec v4.0, Table 126 (NAN KDE field format) */
+#define NAN_KEY_DATA_NIK RSN_SELECTOR(0x50, 0x6f, 0x9a, 36)
+#define NAN_KEY_DATA_LIFETIME RSN_SELECTOR(0x50, 0x6f, 0x9a, 37)
+
+/* Wi-Fi Aware spec v4.0, Figure 61 (NIK KDE format)
+ * Note: The NIK field is defined with a variable length in Figure 61, but
+ * since only one cipher version is defined for now (which determines the
+ * length), use a fixed length here.
+ */
+struct nan_nik_kde {
+ u8 cipher_ver;
+ u8 nik[NAN_NIK_LEN];
+} STRUCT_PACKED;
+
+/* Wi-Fi Aware spec v4.0, Figure 63 (Key Bitmap format) */
+#define NAN_KEY_LIFETIME_GTK BIT(0)
+#define NAN_KEY_LIFETIME_IGTK BIT(1)
+#define NAN_KEY_LIFETIME_BIGTK BIT(2)
+#define NAN_KEY_LIFETIME_NIK BIT(3)
+#define NAN_KEY_LIFETIME_ND_TK BIT(4)
+#define NAN_KEY_LIFETIME_NM_TK BIT(5)
+
+/* Wi-Fi Aware spec v4.0, Figure 62 (NAN Key Lifetime KDE format) */
+struct nan_key_lifetime_kde {
+ le16 key_bitmap;
+ be32 lifetime_sec;
+} STRUCT_PACKED;
+
+#endif /* NAN_DEFS_H */
diff --git a/contrib/wpa/src/common/proc_coord.c b/contrib/wpa/src/common/proc_coord.c
new file mode 100644
index 000000000000..ac9157878820
--- /dev/null
+++ b/contrib/wpa/src/common/proc_coord.c
@@ -0,0 +1,817 @@
+/*
+ * Coordination of operations between processes
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/un.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "proc_coord.h"
+
+
+struct proc_coord_msg_header {
+ u32 msg_type; /* enum proc_coord_message_types */
+ u32 cmd; /* proc_coord_commands */
+ u32 seq;
+};
+
+enum proc_coord_peer_state {
+ PROC_COORD_PEER_WAITING,
+ PROC_COORD_PEER_ACTIVE,
+ PROC_COORD_PEER_TIMED_OUT,
+};
+
+struct proc_coord_peer {
+ struct dl_list list;
+ pid_t pid;
+ enum proc_coord_peer_state state;
+ struct os_reltime last_rx;
+};
+
+struct proc_coord_pending_request {
+ struct dl_list list;
+ struct proc_coord_peer *peer;
+ enum proc_coord_commands cmd;
+ u32 seq;
+ proc_coord_response_cb cb;
+ void *cb_ctx;
+ struct os_reltime timeout;
+};
+
+struct proc_coord_handler {
+ struct dl_list list;
+ proc_coord_cb cb;
+ void *cb_ctx;
+};
+
+struct proc_coord {
+ pid_t pid;
+ char *dir;
+ char *own_sock;
+ int sock;
+ u32 next_seq;
+ struct dl_list handlers; /* struct proc_coord_handler::list */
+ struct dl_list peers; /* struct proc_coord_peer::list */
+ struct dl_list requests; /* struct proc_coord_pending_request::list */
+};
+
+
+static void proc_coord_set_req_expire_timer(struct proc_coord *pc);
+
+
+static struct proc_coord_peer * proc_coord_get_peer(struct proc_coord *pc,
+ pid_t pid)
+{
+ struct proc_coord_peer *peer;
+
+ dl_list_for_each(peer, &pc->peers, struct proc_coord_peer, list) {
+ if (peer->pid == pid)
+ return peer;
+ }
+
+ return NULL;
+}
+
+
+static struct proc_coord_peer * proc_coord_add_peer(struct proc_coord *pc,
+ pid_t pid)
+{
+ struct proc_coord_peer *peer;
+
+ peer = os_zalloc(sizeof(*peer));
+ if (!peer)
+ return NULL;
+
+ peer->pid = pid;
+ peer->state = PROC_COORD_PEER_WAITING;
+ dl_list_add(&pc->peers, &peer->list);
+
+ return peer;
+}
+
+
+static void proc_coord_remove_req(struct proc_coord_pending_request *req)
+{
+ dl_list_del(&req->list);
+ if (req->cb)
+ req->cb(req->cb_ctx, req->peer->pid, NULL);
+ os_free(req);
+}
+
+
+static void proc_coord_remove_peer(struct proc_coord *pc,
+ struct proc_coord_peer *peer)
+{
+ struct proc_coord_pending_request *req, *tmp;
+
+ dl_list_for_each_safe(req, tmp, &pc->requests,
+ struct proc_coord_pending_request, list) {
+ if (req->peer == peer)
+ proc_coord_remove_req(req);
+ }
+
+ dl_list_del(&peer->list);
+ os_free(peer);
+}
+
+
+static void proc_coord_expire_requests(void *eloop_ctx, void *timeout_ctx)
+{
+ struct proc_coord *pc = eloop_ctx;
+ struct proc_coord_pending_request *req, *tmp;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ dl_list_for_each_safe(req, tmp, &pc->requests,
+ struct proc_coord_pending_request, list) {
+ if (os_reltime_before(&req->timeout, &now)) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Pending request peer=%u cmd=%d seq=%u timed out",
+ req->peer->pid, req->cmd, req->seq);
+ proc_coord_remove_req(req);
+ }
+ }
+
+ proc_coord_set_req_expire_timer(pc);
+}
+
+
+void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb,
+ void *cb_ctx)
+{
+ struct proc_coord_pending_request *req, *tmp;
+
+ dl_list_for_each_safe(req, tmp, &pc->requests,
+ struct proc_coord_pending_request, list) {
+ if (req->cb == cb && req->cb_ctx == cb_ctx) {
+ req->cb = NULL;
+ req->cb_ctx = NULL;
+ proc_coord_remove_req(req);
+ }
+ }
+
+ proc_coord_set_req_expire_timer(pc);
+}
+
+
+static void proc_coord_set_req_expire_timer(struct proc_coord *pc)
+{
+ struct proc_coord_pending_request *req;
+ struct os_reltime *first = NULL;
+
+ eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL);
+ dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request,
+ list) {
+ if (!first || os_reltime_before(&req->timeout, first))
+ first = &req->timeout;
+ }
+
+ if (first) {
+ struct os_reltime now, res;
+ unsigned int ms;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(first, &now)) {
+ ms = 0;
+ } else {
+ os_reltime_sub(first, &now, &res);
+ ms = os_reltime_in_ms(&res);
+ }
+ eloop_register_timeout(ms / 1000, (ms % 1000) * 1000,
+ proc_coord_expire_requests, pc, NULL);
+ }
+}
+
+
+static struct proc_coord_pending_request *
+proc_coord_get_request(struct proc_coord *pc, struct proc_coord_peer *peer,
+ enum proc_coord_commands cmd, u32 seq)
+{
+ struct proc_coord_pending_request *req;
+
+ dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request,
+ list) {
+ if (req->peer == peer && req->cmd == cmd && req->seq == seq)
+ return req;
+ }
+
+ return NULL;
+}
+
+
+static int proc_coord_send_msg(struct proc_coord *pc,
+ struct proc_coord_peer *peer,
+ enum proc_coord_message_types msg_type,
+ enum proc_coord_commands cmd,
+ u32 seq, const struct wpabuf *msg)
+{
+ struct proc_coord_msg_header hdr;
+ ssize_t res;
+ struct sockaddr_un addr;
+ struct msghdr mh;
+ struct iovec io[2];
+
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Send message to %d (msg_type=%u cmd=%u seq=%u)",
+ peer->pid, msg_type, cmd, seq);
+
+ os_memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_type = msg_type;
+ hdr.cmd = cmd;
+ hdr.seq = seq;
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%u",
+ pc->dir, peer->pid);
+
+ io[0].iov_base = &hdr;
+ io[0].iov_len = sizeof(hdr);
+ if (msg) {
+ io[1].iov_base = (void *) wpabuf_head(msg);
+ io[1].iov_len = wpabuf_len(msg);
+ }
+
+ os_memset(&mh, 0, sizeof(mh));
+ mh.msg_iov = io;
+ mh.msg_iovlen = msg ? 2 : 1;
+ mh.msg_name = (void *) &addr;
+ mh.msg_namelen = sizeof(addr);
+
+ res = sendmsg(pc->sock, &mh, MSG_DONTWAIT);
+ if (res < 0) {
+ int err = errno;
+
+ wpa_printf(MSG_INFO, "proc_coord: sendmsg: %s",
+ strerror(errno));
+ if (err == ENOENT || err == ECONNREFUSED) {
+ wpa_printf(MSG_INFO,
+ "proc_coord: Remove peer %u due to connection being refused",
+ peer->pid);
+ proc_coord_remove_peer(pc, peer);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+static void proc_coord_rx_starting(struct proc_coord *pc, pid_t pid)
+{
+ wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STARTING", pid);
+}
+
+
+static void proc_coord_rx_stopping(struct proc_coord *pc, pid_t pid)
+{
+ struct proc_coord_peer *peer;
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STOPPING", pid);
+
+ peer = proc_coord_get_peer(pc, pid);
+ if (peer) {
+ wpa_printf(MSG_INFO,
+ "proc_coord: Remove peer %u due to STOPPING event",
+ pid);
+ proc_coord_remove_peer(pc, peer);
+ }
+}
+
+
+static void proc_coord_rx_ping(struct proc_coord *pc, pid_t pid,
+ enum proc_coord_message_types msg_type, u32 seq)
+{
+ if (msg_type != PROC_COORD_MSG_REQUEST)
+ return;
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Reply to peer %u PING", pid);
+ proc_coord_send_response(pc, pid, PROC_COORD_CMD_PING, seq, NULL);
+}
+
+
+static void proc_coord_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct proc_coord *pc = eloop_ctx;
+ struct sockaddr_un from;
+ struct wpabuf *msg;
+ size_t msg_len;
+ ssize_t res;
+ struct proc_coord_msg_header hdr;
+ pid_t pid;
+ char tmp[20], *pos, *end;
+ struct proc_coord_peer *peer;
+ struct msghdr mh;
+ struct iovec io[2];
+ struct proc_coord_handler *handler, *h_tmp;
+
+ res = recv(sock, tmp, 0, MSG_PEEK | MSG_TRUNC);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "proc_coord: recv: %s",
+ strerror(errno));
+ return;
+ }
+ msg_len = res;
+ if (msg_len < sizeof(hdr))
+ return;
+ msg_len -= sizeof(hdr);
+
+ msg = wpabuf_alloc(msg_len);
+ if (!msg)
+ return;
+
+ io[0].iov_base = &hdr;
+ io[0].iov_len = sizeof(hdr);
+ io[1].iov_base = wpabuf_mhead(msg);
+ io[1].iov_len = msg_len;
+
+ os_memset(&mh, 0, sizeof(mh));
+ mh.msg_iov = io;
+ mh.msg_iovlen = 2;
+ mh.msg_name = (void *) &from;
+ mh.msg_namelen = sizeof(from);
+
+ res = recvmsg(sock, &mh, MSG_DONTWAIT);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "proc_coord: recvmsg: %s",
+ strerror(errno));
+ goto out;
+ }
+ if ((size_t) res < sizeof(hdr))
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Received message from %s",
+ from.sun_path);
+ if ((size_t) res > sizeof(hdr)) {
+ wpabuf_put(msg, res - sizeof(hdr));
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "proc_coord: Received message payload", msg);
+ }
+
+ end = ((char *) &from) + mh.msg_namelen;
+
+ /* Require same directory for client socket */
+ if ((size_t) (end - from.sun_path) < os_strlen(pc->dir) ||
+ os_strncmp(pc->dir, from.sun_path, os_strlen(pc->dir)) != 0)
+ goto out;
+
+ /* Find the peer PID from the socket name */
+ pos = end - 1;
+ while (pos > from.sun_path) {
+ if (*pos == '/')
+ break;
+ pos--;
+ }
+ if (pos == from.sun_path)
+ goto out;
+ pos++;
+ os_memcpy(tmp, pos, end - pos);
+ pid = atoi(tmp);
+
+ wpa_printf(MSG_DEBUG, "proc_coord: pid=%u msg_type=%u cmd=%u seq=%u",
+ pid, hdr.msg_type, hdr.cmd, hdr.seq);
+
+ peer = proc_coord_get_peer(pc, pid);
+
+ switch (hdr.msg_type) {
+ case PROC_COORD_MSG_REQUEST:
+ break;
+ case PROC_COORD_MSG_RESPONSE: {
+ struct proc_coord_pending_request *req;
+
+ if (!peer) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Discard msg_type=RESPONSE from %u since there is no peer entry for it",
+ pid);
+ goto out;
+ }
+
+ req = proc_coord_get_request(pc, peer, hdr.cmd, hdr.seq);
+ if (!req) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Discard msg_type=RESPONSE from %u since there is no pending request for it",
+ pid);
+ goto out;
+ }
+ if (req->cb)
+ req->cb(req->cb_ctx, peer->pid, msg);
+ dl_list_del(&req->list);
+ os_free(req);
+ break;
+ }
+ case PROC_COORD_MSG_EVENT:
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Discard unknown msg_type=%u from %u",
+ hdr.msg_type, pid);
+ goto out;
+ }
+
+ if (!peer) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Add new peer entry for %d based on received message",
+ pid);
+ peer = proc_coord_add_peer(pc, pid);
+ if (!peer) {
+ wpa_printf(MSG_ERROR,
+ "proc_coord: Could not add peer entry for %u",
+ pid);
+ goto out;
+ }
+ }
+
+ if (peer->state != PROC_COORD_PEER_ACTIVE) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Mark peer %d active due to received message",
+ pid);
+ peer->state = PROC_COORD_PEER_ACTIVE;
+ }
+
+ os_get_reltime(&peer->last_rx);
+
+ switch (hdr.cmd) {
+ case PROC_COORD_CMD_STARTING :
+ proc_coord_rx_starting(pc, pid);
+ break;
+ case PROC_COORD_CMD_STOPPING:
+ proc_coord_rx_stopping(pc, pid);
+ break;
+ case PROC_COORD_CMD_PING:
+ proc_coord_rx_ping(pc, pid, hdr.msg_type, hdr.seq);
+ break;
+ default:
+ if (hdr.msg_type == PROC_COORD_MSG_RESPONSE)
+ break;
+
+ dl_list_for_each_safe(handler, h_tmp, &pc->handlers,
+ struct proc_coord_handler, list) {
+ if (handler->cb(handler->cb_ctx, pid, hdr.msg_type,
+ hdr.cmd, hdr.seq, msg))
+ break;
+ }
+ break;
+ }
+
+out:
+ wpabuf_free(msg);
+}
+
+
+static void proc_coord_send_starting(struct proc_coord *pc)
+{
+ struct proc_coord_peer *peer, *tmp;
+ int count = 0;
+
+ if (dl_list_empty(&pc->peers))
+ return;
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Send STARTING event to all peers");
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list) {
+ if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT,
+ PROC_COORD_CMD_STARTING, 0, NULL) == 0)
+ count++;
+ }
+ wpa_printf(MSG_DEBUG, "proc_coord: STARTING sent to %d peer(s)", count);
+}
+
+
+static void proc_coord_send_stopping(struct proc_coord *pc)
+{
+ if (dl_list_empty(&pc->peers))
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Send STOPPING event to all active peers");
+ proc_coord_send_event(pc, 0, PROC_COORD_CMD_STOPPING, NULL);
+}
+
+
+static void proc_coord_cb_ping(void *ctx, int pid, const struct wpabuf *msg)
+{
+ struct proc_coord *pc = ctx;
+ struct proc_coord_peer *peer;
+
+ peer = proc_coord_get_peer(pc, pid);
+ if (!peer)
+ return;
+ if (msg) {
+ if (peer->state != PROC_COORD_PEER_ACTIVE) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Mark peer %d active due to response to PING",
+ pid);
+ peer->state = PROC_COORD_PEER_ACTIVE;
+ }
+ } else {
+ if (peer->state != PROC_COORD_PEER_TIMED_OUT) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Mark peer %d timed out due to no response to PING",
+ pid);
+ peer->state = PROC_COORD_PEER_TIMED_OUT;
+ }
+ }
+}
+
+
+static void proc_coord_update_peers_from_dir(struct proc_coord *pc)
+{
+ DIR *dir;
+ struct dirent *de;
+ struct proc_coord_peer *peer, *tmp;
+
+ /* Remove peers that do not have a socket file */
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list) {
+ char fname[256];
+
+ os_snprintf(fname, sizeof(fname), "%s/%d", pc->dir, peer->pid);
+ if (!os_file_exists(fname)) {
+ wpa_printf(MSG_DEBUG,
+ "proc_coord: Remove peer %d due to socket file not present",
+ peer->pid);
+ proc_coord_remove_peer(pc, peer);
+ }
+ }
+
+ /* Add peer entries for all new sockets in the directory */
+ dir = opendir(pc->dir);
+ if (!dir) {
+ wpa_printf(MSG_ERROR, "proc_coord: opendir: %s",
+ strerror(errno));
+ return;
+ }
+
+ while ((de = readdir(dir))) {
+ int pid = atoi(de->d_name);
+
+ if (pid <= 0 || pid == pc->pid)
+ continue;
+
+ peer = proc_coord_get_peer(pc, pid);
+ if (peer)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Add new peer entry for %u",
+ pid);
+ peer = proc_coord_add_peer(pc, pid);
+ if (!peer) {
+ wpa_printf(MSG_ERROR,
+ "proc_coord: Could not add peer entry for %u",
+ pid);
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG, "proc_coord: Ping new peer %u", pid);
+ proc_coord_send_request(pc, pid, PROC_COORD_CMD_PING, NULL,
+ 10000, proc_coord_cb_ping, pc);
+ }
+
+ closedir(dir);
+}
+
+
+static void proc_coord_update_peers(void *eloop_ctx, void *timeout_ctx)
+{
+ struct proc_coord *pc = eloop_ctx;
+ struct proc_coord_peer *peer, *tmp;
+ struct os_reltime now;
+
+ proc_coord_update_peers_from_dir(pc);
+
+ os_get_reltime(&now);
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list) {
+ if (!os_reltime_expired(&now, &peer->last_rx, 60))
+ continue;
+ proc_coord_send_request(pc, peer->pid, PROC_COORD_CMD_PING,
+ NULL, 10000, NULL, NULL);
+ }
+
+ eloop_register_timeout(10, 0, proc_coord_update_peers, pc, NULL);
+}
+
+
+struct proc_coord * proc_coord_init(const char *dir)
+{
+ struct proc_coord *pc;
+ struct sockaddr_un addr;
+ size_t len;
+ int flags;
+
+ pc = os_zalloc(sizeof(*pc));
+ if (!pc)
+ return NULL;
+
+ dl_list_init(&pc->peers);
+ dl_list_init(&pc->requests);
+ dl_list_init(&pc->handlers);
+ pc->sock = -1;
+
+ pc->dir = os_strdup(dir);
+ if (!pc->dir)
+ goto fail;
+
+ len = os_strlen(dir) + 20;
+ pc->own_sock = os_zalloc(len);
+ if (!pc->own_sock)
+ goto fail;
+ pc->pid = getpid();
+ os_snprintf(pc->own_sock, len, "%s/%d", dir, pc->pid);
+ wpa_printf(MSG_DEBUG, "proc_coord: Own socket at %s", pc->own_sock);
+ unlink(pc->own_sock);
+
+ pc->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (pc->sock < 0) {
+ wpa_printf(MSG_ERROR, "proc_coord: socket(PF_UNIX): %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, pc->own_sock, sizeof(addr.sun_path));
+ if (bind(pc->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "proc_coord: bind(PF_UNIX) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ flags = fcntl(pc->sock, F_GETFL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(pc->sock, F_SETFL, flags) < 0) {
+ wpa_printf(MSG_INFO,
+ "proc_coord: fcntl(O_NONBLOCK): %s",
+ strerror(errno));
+ /* Not fatal, continue on.*/
+ }
+ }
+
+ eloop_register_read_sock(pc->sock, proc_coord_receive, pc, NULL);
+
+ proc_coord_update_peers_from_dir(pc);
+
+ /* Start periodic updates quickly to recover from potential race
+ * conditions if the processes are started at the same time. */
+ eloop_register_timeout(1, 0, proc_coord_update_peers, pc, NULL);
+
+ proc_coord_send_starting(pc);
+
+ return pc;
+
+fail:
+ proc_coord_deinit(pc);
+ return NULL;
+}
+
+
+void proc_coord_deinit(struct proc_coord *pc)
+{
+ struct proc_coord_peer *peer, *tmp;
+ struct proc_coord_pending_request *req, *tmp2;
+
+ if (!pc)
+ return;
+
+ eloop_cancel_timeout(proc_coord_update_peers, pc, NULL);
+ eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL);
+
+ if (pc->sock >= 0) {
+ proc_coord_send_stopping(pc);
+ eloop_unregister_read_sock(pc->sock);
+ close(pc->sock);
+ unlink(pc->own_sock);
+ }
+
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list)
+ proc_coord_remove_peer(pc, peer);
+
+ dl_list_for_each_safe(req, tmp2, &pc->requests,
+ struct proc_coord_pending_request, list) {
+ dl_list_del(&req->list);
+ os_free(req);
+ }
+
+ os_free(pc->dir);
+ os_free(pc->own_sock);
+ os_free(pc);
+}
+
+
+int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb,
+ void *cb_ctx)
+{
+ struct proc_coord_handler *handler;
+
+ handler = os_zalloc(sizeof(*handler));
+ if (!handler)
+ return -1;
+
+ handler->cb = cb;
+ handler->cb_ctx = cb_ctx;
+ dl_list_add(&pc->handlers, &handler->list);
+ return 0;
+}
+
+
+void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb,
+ void *cb_ctx)
+{
+ struct proc_coord_handler *handler;
+
+ dl_list_for_each(handler, &pc->handlers, struct proc_coord_handler,
+ list) {
+ if (handler->cb == cb && handler->cb_ctx == cb_ctx) {
+ dl_list_del(&handler->list);
+ os_free(handler);
+ break;
+ }
+ }
+}
+
+
+int proc_coord_send_event(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd,
+ const struct wpabuf *msg)
+{
+ struct proc_coord_peer *peer, *tmp;
+ int count = 0;
+ u32 seq = 0;
+
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list) {
+ if (dst && peer->pid != dst)
+ continue;
+ if (!dst && peer->state != PROC_COORD_PEER_ACTIVE)
+ continue;
+ if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, cmd,
+ seq, msg) == 0)
+ count++;
+ }
+
+ return count;
+}
+
+
+int proc_coord_send_request(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd,
+ const struct wpabuf *msg,
+ unsigned int timeout_ms,
+ proc_coord_response_cb cb,
+ void *cb_ctx)
+{
+ struct proc_coord_peer *peer, *tmp;
+ int count = 0;
+ struct proc_coord_pending_request *req;
+
+ pc->next_seq++;
+
+ dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
+ list) {
+ if (dst && peer->pid != dst)
+ continue;
+ if (!dst && peer->state != PROC_COORD_PEER_ACTIVE)
+ continue;
+ if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_REQUEST, cmd,
+ pc->next_seq, msg) < 0)
+ continue;
+ count++;
+
+ req = os_zalloc(sizeof(*req));
+ if (!req)
+ break;
+ req->peer = peer;
+ req->cmd = cmd;
+ req->seq = pc->next_seq;
+ req->cb = cb;
+ req->cb_ctx = cb_ctx;
+ os_get_reltime(&req->timeout);
+ os_reltime_add_ms(&req->timeout, timeout_ms);
+ dl_list_add(&pc->requests, &req->list);
+ }
+ proc_coord_set_req_expire_timer(pc);
+
+ return count;
+}
+
+
+int proc_coord_send_response(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd, u32 seq,
+ const struct wpabuf *msg)
+{
+ struct proc_coord_peer *peer = proc_coord_get_peer(pc, dst);
+
+ if (!peer)
+ return -1;
+ return proc_coord_send_msg(pc, peer, PROC_COORD_MSG_RESPONSE, cmd,
+ seq, msg);
+}
diff --git a/contrib/wpa/src/common/proc_coord.h b/contrib/wpa/src/common/proc_coord.h
new file mode 100644
index 000000000000..8eae443444f2
--- /dev/null
+++ b/contrib/wpa/src/common/proc_coord.h
@@ -0,0 +1,139 @@
+/*
+ * Coordination of operations between processes
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PROC_COORD_H
+#define PROC_COORD_H
+
+struct proc_coord;
+
+enum proc_coord_message_types {
+ PROC_COORD_MSG_REQUEST = 0,
+ PROC_COORD_MSG_RESPONSE = 1,
+ PROC_COORD_MSG_EVENT = 2,
+};
+
+enum proc_coord_commands {
+ PROC_COORD_CMD_STARTING = 0,
+ PROC_COORD_CMD_STOPPING = 1,
+ PROC_COORD_CMD_PING = 2,
+ PROC_COORD_CMD_TEST = 3,
+};
+
+/**
+ * proc_coord_init - Initialize process coordinations
+ * @dir: Access controlled directory for process coordination
+ * Returns: Context pointer on success or %NULL on failure
+ *
+ * The returned context must be released with a call to proc_coord_deinit().
+ */
+struct proc_coord * proc_coord_init(const char *dir);
+
+/**
+ * proc_coord_deinit - Deinitialize process coordinations
+ * @pc: Process coordination context from proc_coord_init()
+ */
+void proc_coord_deinit(struct proc_coord *pc);
+
+typedef bool (*proc_coord_cb)(void *ctx, int src,
+ enum proc_coord_message_types msg_type,
+ enum proc_coord_commands cmd,
+ u32 seq, const struct wpabuf *msg);
+
+/**
+ * proc_coord_register_handler - Register a handler for process coordination
+ * @pc: Process coordination context from proc_coord_init()
+ * @cb: Callback function
+ * @cb_ctx: Context for the callback function
+ * Returns: 0 on success or -1 on failure
+ *
+ * The registered handler will be called for received request and event
+ * messages. Received request messages are delivered to the separate handler
+ * registered with proc_coord_send_request().
+ *
+ * The handler function can return true to stop iteration of handler functions
+ * or false to allow the iteration to continue reporting the message to other
+ * registered handler functions, if any.
+ */
+int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb,
+ void *cb_ctx);
+
+/**
+ * proc_coord_unregister_handler - Unregister a handler for process coordination
+ * @pc: Process coordination context from proc_coord_init()
+ * @cb: Callback function
+ * @cb_ctx: Context for the callback function
+ */
+void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb,
+ void *cb_ctx);
+
+/**
+ * proc_coord_send_event - Send an event message
+ * @pc: Process coordination context from proc_coord_init()
+ * @dst: Destination peer (PID) or 0 for all active peers
+ * @cmd: The command ID for the message
+ * @msg: Payload of the message
+ * Returns: The number of peers the message was sent to
+ */
+int proc_coord_send_event(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd,
+ const struct wpabuf *msg);
+
+typedef void (*proc_coord_response_cb)(void *ctx, int pid,
+ const struct wpabuf *msg);
+
+/**
+ * proc_coord_send_request - Send a request message
+ * @pc: Process coordination context from proc_coord_init()
+ * @dst: Destination peer (PID) or 0 for all active peers
+ * @cmd: The command ID for the message
+ * @msg: Payload of the message
+ * @timeout_ms: Timeout for receiving a response
+ * @cb: Callback function to report the responses or %NULL for no callback
+ * @cb_ctx: Context for the callback function
+ * Returns: The number of peers the message was sent to
+ *
+ * If a response is received from a peer, the response is reported to the
+ * callback function. If no response is received within the specified timeout,
+ * the callback function is called with msg == NULL. The specified @cb_ctx has
+ * to remain valid until all the pending responses have been reported or until
+ * proc_coord_cancel_wait() has been used to cancel any pending wait.
+ */
+int proc_coord_send_request(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd,
+ const struct wpabuf *msg,
+ unsigned int timeout_ms,
+ proc_coord_response_cb cb,
+ void *cb_ctx);
+
+/**
+ * proc_coord_cancel_wait - Cancel wait for a pending response message
+ * @pc: Process coordination context from proc_coord_init()
+ * @cb: Callback function registered with proc_coord_send_request()
+ * @cb_ctx: Context for the callback function
+ */
+void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb,
+ void *cb_ctx);
+
+/**
+ * proc_coord_send_event - Send a response message
+ * @pc: Process coordination context from proc_coord_init()
+ * @dst: Destination peer (PID)
+ * @cmd: The command ID for the message
+ * @seq: The sequence number from the received request message
+ * @msg: Payload of the message
+ * Returns: 0 on success or -1 on failure
+ *
+ * This is used to send a response to a request message that was reported
+ * through a call to the handler function that was registered with
+ * proc_coord_register_handler().
+ */
+int proc_coord_send_response(struct proc_coord *pc, int dst,
+ enum proc_coord_commands cmd, u32 seq,
+ const struct wpabuf *msg);
+
+#endif /* PROC_COORD_H */
diff --git a/contrib/wpa/src/common/proximity_ranging.c b/contrib/wpa/src/common/proximity_ranging.c
new file mode 100644
index 000000000000..a230043f56e8
--- /dev/null
+++ b/contrib/wpa/src/common/proximity_ranging.c
@@ -0,0 +1,2707 @@
+/*
+ * Proxmity Ranging
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
+#include "pasn/pasn_common.h"
+#include "proximity_ranging.h"
+
+
+static bool valid_country_ch(char c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+
+static void pr_device_free(struct pr_data *pr, struct pr_device *dev)
+{
+#ifdef CONFIG_PASN
+ wpabuf_free(dev->ranging_wrapper);
+ if (dev->pasn) {
+ wpa_pasn_reset(dev->pasn);
+ pasn_data_deinit(dev->pasn);
+ }
+#endif /* CONFIG_PASN */
+ os_free(dev);
+}
+
+
+static struct pr_device * pr_get_device(struct pr_data *pr, const u8 *addr)
+{
+ struct pr_device *dev;
+
+ dl_list_for_each(dev, &pr->devices, struct pr_device, list) {
+ if (ether_addr_equal(dev->pr_device_addr, addr))
+ return dev;
+ }
+ return NULL;
+}
+
+
+static struct pr_device * pr_create_device(struct pr_data *pr, const u8 *addr)
+{
+ struct pr_device *dev, *oldest = NULL;
+ size_t count = 0;
+
+ dev = pr_get_device(pr, addr);
+ if (dev)
+ return dev;
+
+ dl_list_for_each(dev, &pr->devices, struct pr_device, list) {
+ count++;
+ if (!oldest ||
+ os_reltime_before(&dev->last_seen, &oldest->last_seen))
+ oldest = dev;
+ }
+ if (count + 1 > PR_MAX_PEER && oldest) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Remove oldest peer entry to make room for a new peer "
+ MACSTR, MAC2STR(oldest->pr_device_addr));
+ dl_list_del(&oldest->list);
+ pr_device_free(pr, oldest);
+ }
+
+ dev = os_zalloc(sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ dl_list_add(&pr->devices, &dev->list);
+ os_memcpy(dev->pr_device_addr, addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "PR: New Proximity Ranging device " MACSTR
+ " added to list", MAC2STR(addr));
+
+ return dev;
+}
+
+
+struct pr_data * pr_init(const struct pr_config *cfg)
+{
+ struct pr_data *pr;
+
+ pr = os_zalloc(sizeof(*pr) + sizeof(*cfg));
+ if (!pr)
+ return NULL;
+
+ pr->cfg = (struct pr_config *) (pr + 1);
+ os_memcpy(pr->cfg, cfg, sizeof(*cfg));
+ if (cfg->dev_name)
+ pr->cfg->dev_name = os_strdup(cfg->dev_name);
+ else
+ pr->cfg->dev_name = NULL;
+
+ dl_list_init(&pr->devices);
+ dl_list_init(&pr->dev_iks);
+
+#ifdef CONFIG_PASN
+ pr->initiator_pmksa = pasn_initiator_pmksa_cache_init();
+ pr->responder_pmksa = pasn_responder_pmksa_cache_init();
+#endif /* CONFIG_PASN */
+
+ return pr;
+}
+
+
+static void pr_deinit_dev_iks(struct pr_data *pr)
+{
+ struct pr_dev_ik *dev_ik, *prev_dev_ik;
+
+ dl_list_for_each_safe(dev_ik, prev_dev_ik, &pr->dev_iks,
+ struct pr_dev_ik, list) {
+ dl_list_del(&dev_ik->list);
+ os_free(dev_ik);
+ }
+}
+
+
+void pr_flush(struct pr_data *pr)
+{
+#ifdef CONFIG_PASN
+ if (pr->initiator_pmksa)
+ pasn_initiator_pmksa_cache_flush(pr->initiator_pmksa);
+ if (pr->responder_pmksa)
+ pasn_responder_pmksa_cache_flush(pr->responder_pmksa);
+#endif /* CONFIG_PASN */
+}
+
+void pr_deinit(struct pr_data *pr)
+{
+ struct pr_device *dev, *prev;
+
+ if (!pr)
+ return;
+
+ os_free(pr->cfg->dev_name);
+
+ dl_list_for_each_safe(dev, prev, &pr->devices, struct pr_device, list) {
+ dl_list_del(&dev->list);
+ pr_device_free(pr, dev);
+ }
+
+ pr_deinit_dev_iks(pr);
+
+#ifdef CONFIG_PASN
+ os_free(pr->pr_pasn_params);
+ pr->pr_pasn_params = NULL;
+ pr->ranging_final_received = false;
+
+ pasn_initiator_pmksa_cache_deinit(pr->initiator_pmksa);
+ pasn_responder_pmksa_cache_deinit(pr->responder_pmksa);
+#endif /* CONFIG_PASN */
+
+ os_free(pr);
+ wpa_printf(MSG_DEBUG, "PR: Deinit done");
+}
+
+
+void pr_set_dev_addr(struct pr_data *pr, const u8 *addr)
+{
+ if (pr && addr)
+ os_memcpy(pr->cfg->dev_addr, addr, ETH_ALEN);
+}
+
+
+void pr_clear_dev_iks(struct pr_data *pr)
+{
+ struct pr_device *dev;
+
+ pr->cfg->dik_len = 0;
+ os_memset(pr->cfg->dik_data, 0, DEVICE_IDENTITY_KEY_LEN);
+ pr->cfg->global_password_valid = false;
+ os_memset(pr->cfg->global_password, 0,
+ sizeof(pr->cfg->global_password));
+
+ dl_list_for_each(dev, &pr->devices, struct pr_device, list) {
+ dev->password_valid = false;
+ os_memset(dev->password, 0, sizeof(dev->password));
+ dev->dik_valid = false;
+ os_memset(dev->dik, 0, DEVICE_IDENTITY_KEY_LEN);
+ }
+
+ pr_deinit_dev_iks(pr);
+}
+
+
+void pr_add_dev_ik(struct pr_data *pr, const u8 *dik, const char *password,
+ const u8 *pmk, size_t pmk_len, bool own)
+{
+ struct pr_dev_ik *dev_ik;
+
+ if (own) {
+ os_memcpy(pr->cfg->dik_data, dik, DEVICE_IDENTITY_KEY_LEN);
+ pr->cfg->dik_len = DEVICE_IDENTITY_KEY_LEN;
+ if (password) {
+ os_strlcpy(pr->cfg->global_password, password,
+ sizeof(pr->cfg->global_password));
+ pr->cfg->global_password_valid = true;
+ }
+ return;
+ }
+
+ if (pmk && (pmk_len != 32 && pmk_len != 48 && pmk_len != 64)) {
+ wpa_printf(MSG_INFO, "PR: Unexpected PMK length %zu", pmk_len);
+ return;
+ }
+
+ dl_list_for_each(dev_ik, &pr->dev_iks, struct pr_dev_ik, list) {
+ if (os_memcmp(dik, dev_ik->dik, DEVICE_IDENTITY_KEY_LEN) == 0) {
+ dl_list_del(&dev_ik->list);
+ os_free(dev_ik);
+ break;
+ }
+ }
+
+ dev_ik = os_zalloc(sizeof(*dev_ik));
+ if (!dev_ik)
+ return;
+
+ dl_list_add(&pr->dev_iks, &dev_ik->list);
+ os_memcpy(dev_ik->dik, dik, DEVICE_IDENTITY_KEY_LEN);
+ if (password) {
+ os_strlcpy(dev_ik->password, password,
+ sizeof(dev_ik->password));
+ dev_ik->password_valid = true;
+ }
+ if (pmk) {
+ os_memcpy(dev_ik->pmk, pmk, pmk_len);
+ dev_ik->pmk_len = pmk_len;
+ dev_ik->pmk_valid = true;
+ }
+
+ wpa_printf(MSG_DEBUG, "PR: New Device Identity added to list");
+}
+
+
+int pr_set_peer_credentials(struct pr_data *pr, const u8 *addr,
+ const u8 *pmk, size_t pmk_len,
+ const char *password)
+{
+ struct pr_device *dev;
+
+ if (!pr || !addr)
+ return -1;
+
+ dev = pr_get_device(pr, addr);
+ if (!dev) {
+ wpa_printf(MSG_DEBUG, "PR: set_peer_credentials: " MACSTR
+ " not found", MAC2STR(addr));
+ return -1;
+ }
+
+ if (pmk && pmk_len) {
+ if (pmk_len > PMK_LEN_MAX)
+ return -1;
+ os_memcpy(dev->pmk, pmk, pmk_len);
+ dev->pmk_len = pmk_len;
+ dev->pmk_valid = true;
+ wpa_printf(MSG_DEBUG, "PR: PMK set for " MACSTR, MAC2STR(addr));
+ }
+
+ if (password) {
+ if (os_strlen(password) >= sizeof(dev->password))
+ return -1;
+ os_strlcpy(dev->password, password, sizeof(dev->password));
+ dev->password_valid = true;
+ wpa_printf(MSG_DEBUG, "PR: password set for " MACSTR,
+ MAC2STR(addr));
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * pr_encaps_elem(const struct wpabuf *subelems,
+ u32 ie_type)
+{
+ struct wpabuf *ie = NULL;
+ const u8 *pos, *end;
+ size_t len = 0;
+
+ if (!subelems)
+ return NULL;
+
+ len = wpabuf_len(subelems) + 1000;
+ ie = wpabuf_alloc(len);
+ if (!ie)
+ return NULL;
+
+ pos = wpabuf_head(subelems);
+ end = pos + wpabuf_len(subelems);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, ie_type);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+ return ie;
+}
+
+
+static void pr_get_ranging_capabilities(struct pr_data *pr,
+ struct pr_capabilities *capab)
+{
+ os_memset(capab, 0, sizeof(struct pr_capabilities));
+
+ if (pr->cfg->dev_name)
+ os_strlcpy(capab->device_name, pr->cfg->dev_name,
+ sizeof(capab->device_name));
+
+ if (pr->cfg->edca_ista_support || pr->cfg->edca_rsta_support)
+ capab->edca_support = true;
+
+ if (pr->cfg->ntb_ista_support || pr->cfg->ntb_rsta_support)
+ capab->ntb_support = true;
+
+ capab->secure_he_ltf = pr->cfg->secure_he_ltf;
+ capab->pasn_type = pr->cfg->pasn_type;
+ capab->support_6ghz = pr->cfg->support_6ghz;
+}
+
+
+static void pr_get_edca_capabilities(struct pr_data *pr,
+ struct edca_capabilities *capab)
+{
+ u16 edca_hw_caps = 0;
+
+ os_memset(capab, 0, sizeof(struct edca_capabilities));
+ capab->ista_support = pr->cfg->edca_ista_support;
+ capab->rsta_support = pr->cfg->edca_rsta_support;
+ os_memcpy(capab->country, pr->cfg->country, 3);
+
+ edca_hw_caps |= (pr->cfg->edca_format_and_bw & EDCA_FORMAT_AND_BW_MASK)
+ << EDCA_FORMAT_AND_BW;
+ edca_hw_caps |= (pr->cfg->max_tx_antenna & EDCA_MAX_TX_ANTENNA_MASK) <<
+ EDCA_MAX_TX_ANTENNA;
+ edca_hw_caps |= (pr->cfg->max_rx_antenna & EDCA_MAX_RX_ANTENNA_MASK) <<
+ EDCA_MAX_RX_ANTENNA;
+
+ capab->edca_hw_caps = edca_hw_caps;
+ os_memcpy(&capab->channels, &pr->cfg->edca_channels,
+ sizeof(struct pr_channels));
+}
+
+
+static void pr_get_ntb_capabilities(struct pr_data *pr,
+ struct ntb_capabilities *capab)
+{
+ u32 ntb_hw_caps = 0;
+
+ os_memset(capab, 0, sizeof(struct ntb_capabilities));
+ capab->ista_support = pr->cfg->ntb_ista_support;
+ capab->rsta_support = pr->cfg->ntb_rsta_support;
+ os_memcpy(capab->country, pr->cfg->country, 3);
+ capab->secure_he_ltf = pr->cfg->secure_he_ltf;
+
+ ntb_hw_caps |= (pr->cfg->ntb_format_and_bw & NTB_FORMAT_AND_BW_MASK) <<
+ NTB_FORMAT_AND_BW;
+ ntb_hw_caps |= (pr->cfg->max_tx_ltf_repetations &
+ MAX_TX_LTF_REPETATIONS_MASK) << MAX_TX_LTF_REPETATIONS;
+ ntb_hw_caps |= (pr->cfg->max_rx_ltf_repetations &
+ MAX_RX_LTF_REPETATIONS_MASK) << MAX_RX_LTF_REPETATIONS;
+
+ ntb_hw_caps |= (pr->cfg->max_rx_ltf_total & MAX_RX_LTF_TOTAL_MASK) <<
+ MAX_RX_LTF_TOTAL;
+ ntb_hw_caps |= (pr->cfg->max_tx_ltf_total & MAX_TX_LTF_TOTAL_MASK) <<
+ MAX_TX_LTF_TOTAL;
+
+ ntb_hw_caps |= (pr->cfg->max_rx_sts_le_80 & MAX_RX_STS_LE_80_MASK) <<
+ MAX_RX_STS_LE_80;
+ ntb_hw_caps |= (pr->cfg->max_rx_sts_gt_80 & MAX_RX_STS_GT_80_MASK) <<
+ MAX_RX_STS_GT_80;
+
+ ntb_hw_caps |= (pr->cfg->max_tx_sts_le_80 & MAX_TX_STS_LE_80_MASK) <<
+ MAX_TX_STS_LE_80;
+ ntb_hw_caps |= (pr->cfg->max_tx_sts_gt_80 & MAX_TX_STS_GT_80_MASK) <<
+ MAX_TX_STS_GT_80;
+
+ capab->ntb_hw_caps = ntb_hw_caps;
+ os_memcpy(&capab->channels, &pr->cfg->ntb_channels,
+ sizeof(struct pr_channels));
+}
+
+
+static int pr_derive_dira(struct pr_data *pr, const u8 *src_addr,
+ struct pr_dira *dira)
+{
+ u8 nonce[DEVICE_IDENTITY_NONCE_LEN];
+ u8 tag[DEVICE_MAX_HASH_LEN];
+ u8 data[DIR_STR_LEN + ETH_ALEN + DEVICE_IDENTITY_NONCE_LEN];
+
+ if (pr->cfg->dik_cipher != DIRA_CIPHER_VERSION_128) {
+ wpa_printf(MSG_INFO, "PR: Unsupported DIRA Cipher version %d",
+ pr->cfg->dik_cipher);
+ return -1;
+ }
+
+ if (pr->cfg->dik_len != DEVICE_IDENTITY_KEY_LEN) {
+ wpa_printf(MSG_INFO, "PR: Invalid DIK length %zu",
+ pr->cfg->dik_len);
+ return -1;
+ }
+
+ os_memset(data, 0, sizeof(data));
+
+ if (os_get_random(nonce, DEVICE_IDENTITY_NONCE_LEN) < 0) {
+ wpa_printf(MSG_INFO, "PR: Failed to generate DIRA nonce");
+ return -1;
+ }
+
+ /* Tag = Truncate-64(HMAC-SHA-256(DevIK, "DIR" || PR Device Address ||
+ * Nonce))
+ */
+ os_memcpy(data, "DIR", DIR_STR_LEN);
+ os_memcpy(&data[DIR_STR_LEN], src_addr, ETH_ALEN);
+ os_memcpy(&data[DIR_STR_LEN + ETH_ALEN], nonce,
+ DEVICE_IDENTITY_NONCE_LEN);
+
+ if (hmac_sha256(pr->cfg->dik_data, pr->cfg->dik_len, data, sizeof(data),
+ tag) < 0) {
+ wpa_printf(MSG_ERROR, "PR: Could not derive DIRA tag");
+ return -1;
+ }
+
+ os_memset(dira, 0, sizeof(struct pr_dira));
+ dira->cipher_version = pr->cfg->dik_cipher;
+ dira->nonce_len = DEVICE_IDENTITY_NONCE_LEN;
+ os_memcpy(dira->nonce, nonce, DEVICE_IDENTITY_NONCE_LEN);
+ dira->tag_len = DEVICE_IDENTITY_TAG_LEN;
+ os_memcpy(dira->tag, tag, DEVICE_IDENTITY_TAG_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "PR: DIK", pr->cfg->dik_data,
+ pr->cfg->dik_len);
+ wpa_hexdump(MSG_DEBUG, "PR: DIRA-NONCE", dira->nonce, dira->nonce_len);
+ wpa_hexdump(MSG_DEBUG, "PR: DIRA-TAG", dira->tag, dira->tag_len);
+
+ return 0;
+}
+
+
+static int pr_validate_dira(struct pr_data *pr, struct pr_device *dev,
+ const u8 *dira, u16 dira_len)
+{
+ int ret;
+ size_t len[3];
+ const u8 *addr[3];
+ struct pr_dev_ik *dev_ik;
+ u8 tag[DEVICE_MAX_HASH_LEN];
+ const char *label = "DIR";
+ const u8 *dira_nonce, *dira_tag;
+
+ /* Reset DevIK state - set only if DIRA verification succeeds */
+ os_memset(dev->dik, 0, DEVICE_IDENTITY_KEY_LEN);
+ dev->dik_valid = false;
+
+ if (dira_len < 1 + DEVICE_IDENTITY_NONCE_LEN + DEVICE_IDENTITY_TAG_LEN)
+ {
+ wpa_printf(MSG_DEBUG, "PR: Truncated DIRA (length %u)",
+ dira_len);
+ return -1;
+ }
+
+ /* Cipher Version */
+ if (dira[0] != DIRA_CIPHER_VERSION_128) {
+ wpa_printf(MSG_DEBUG, "PR: Unsupported DIRA cipher version %d",
+ dira[0]);
+ return -1;
+ }
+
+ /* Nonce */
+ dira_nonce = &dira[1];
+
+ /* Tag */
+ dira_tag = &dira[1 + DEVICE_IDENTITY_NONCE_LEN];
+
+ /* Tag = Truncate-64(HMAC-SHA-256(DevIK, "DIR" || Device Address ||
+ * Nonce)) */
+ addr[0] = (const u8 *) label;
+ len[0] = DIR_STR_LEN;
+ addr[1] = dev->pr_device_addr;
+ len[1] = ETH_ALEN;
+ addr[2] = dira_nonce;
+ len[2] = DEVICE_IDENTITY_NONCE_LEN;
+
+ dl_list_for_each(dev_ik, &pr->dev_iks, struct pr_dev_ik, list) {
+ ret = hmac_sha256_vector(dev_ik->dik, DEVICE_IDENTITY_KEY_LEN,
+ 3, addr, len, tag);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO,
+ "PR: Failed to derive DIRA Tag");
+ return -1;
+ }
+
+ if (os_memcmp(tag, dira_tag, DEVICE_IDENTITY_TAG_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "PR: DIRA Tag matched");
+ if (dev_ik->password_valid) {
+ os_strlcpy(dev->password, dev_ik->password,
+ sizeof(dev->password));
+ dev->password_valid = true;
+ }
+ if (dev_ik->pmk_valid) {
+ os_memcpy(dev->pmk, dev_ik->pmk,
+ dev_ik->pmk_len);
+ dev->pmk_len = dev_ik->pmk_len;
+ dev->pmk_valid = true;
+ }
+ os_memcpy(dev->dik, dev_ik->dik,
+ DEVICE_IDENTITY_KEY_LEN);
+ dev->dik_valid = true;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+#ifdef CONFIG_PASN
+
+static void pr_copy_channels(struct pr_channels *dst,
+ const struct pr_channels *src, bool allow_6ghz)
+{
+ size_t i, j;
+
+ if (allow_6ghz) {
+ os_memcpy(dst, src, sizeof(struct pr_channels));
+ return;
+ }
+
+ for (i = 0, j = 0; i < src->op_classes; i++) {
+ if (is_6ghz_op_class(src->op_class[i].op_class))
+ continue;
+ os_memcpy(&dst->op_class[j], &src->op_class[i],
+ sizeof(struct pr_op_class));
+ j++;
+ }
+ dst->op_classes = j;
+}
+
+
+static void pr_op_class_intersect(const struct pr_op_class *a,
+ const struct pr_op_class *b,
+ struct pr_op_class *res)
+{
+ size_t i, j;
+
+ res->op_class = a->op_class;
+ for (i = 0; i < a->channels; i++) {
+ for (j = 0; j < b->channels; j++) {
+ if (a->channel[i] != b->channel[j])
+ continue;
+ res->channel[res->channels] = a->channel[i];
+ res->channels++;
+ if (res->channels == PR_MAX_OP_CLASS_CHANNELS)
+ return;
+ }
+ }
+}
+
+
+/**
+ * pr_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of supported channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channel sets are assumed to use the global operating classes or the
+ * same country code. If different country codes are used without using the
+ * global operating classes, the operating class numbers may not be matched
+ * correctly and results are undefined.
+ */
+static void pr_channels_intersect(const struct pr_channels *a,
+ const struct pr_channels *b,
+ struct pr_channels *res)
+{
+ size_t i, j;
+ const struct pr_op_class *a_op;
+ const struct pr_op_class *b_op;
+
+ os_memset(res, 0, sizeof(*res));
+
+ for (i = 0; i < a->op_classes; i++) {
+ a_op = &a->op_class[i];
+ for (j = 0; j < b->op_classes; j++) {
+ b_op = &b->op_class[j];
+ if (a_op->op_class != b_op->op_class)
+ continue;
+ pr_op_class_intersect(a_op, b_op,
+ &res->op_class[res->op_classes]);
+ if (res->op_class[res->op_classes].channels) {
+ res->op_classes++;
+ if (res->op_classes == PR_MAX_OP_CLASSES)
+ return;
+ }
+ }
+ }
+}
+
+#endif /* CONFIG_PASN */
+
+
+static void pr_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ const struct pr_channels *chan)
+{
+ size_t i;
+
+ wpabuf_put_data(buf, country, 3); /* Country String */
+ wpabuf_put_u8(buf, chan->op_classes); /* Number of Channel Entries */
+
+ /* Channel Entry List */
+ for (i = 0; i < chan->op_classes; i++) {
+ const struct pr_op_class *c = &chan->op_class[i];
+
+ wpabuf_put_u8(buf, c->op_class);
+ wpabuf_put_u8(buf, c->channels);
+ wpabuf_put_data(buf, c->channel, c->channels);
+ }
+}
+
+
+static void pr_buf_add_ranging_capa_info(struct wpabuf *buf,
+ const struct pr_capabilities *capab)
+{
+ u8 *len;
+ u8 capa_6g = 0;
+ u8 protocol_type = 0;
+ size_t _len;
+
+ /* Proximity Ranging Capability Attribute */
+ wpabuf_put_u8(buf, PR_ATTR_RANGING_CAPABILITY);
+ len = wpabuf_put(buf, 2); /* Attribute length to be filled */
+
+ /* Ranging Protocol Type */
+ if (capab->edca_support)
+ protocol_type |= PR_EDCA_BASED_RANGING;
+ if (capab->ntb_support && capab->secure_he_ltf)
+ protocol_type |= PR_NTB_SECURE_LTF_BASED_RANGING;
+ if (capab->ntb_support)
+ protocol_type |= PR_NTB_OPEN_BASED_RANGING;
+ wpabuf_put_u8(buf, protocol_type);
+
+ /* PASN Type */
+ wpabuf_put_u8(buf, capab->pasn_type);
+
+ /* 6GHz band */
+ if (capab->support_6ghz)
+ capa_6g |= BIT(0);
+
+ wpabuf_put_u8(buf, capa_6g);
+
+ /* Device Name */
+ wpabuf_put_data(buf, capab->device_name, WPS_DEV_NAME_MAX_LEN);
+ wpa_printf(MSG_DEBUG, "PR: Device name: %s", capab->device_name);
+
+ _len = (u8 *) wpabuf_put(buf, 0) - len - 2;
+ WPA_PUT_LE16(len, _len);
+ wpa_hexdump(MSG_DEBUG, "PR: * Capability Attribute", len + 2, _len);
+}
+
+
+static void pr_buf_add_edca_capa_info(struct wpabuf *buf,
+ const struct edca_capabilities *edca_data)
+{
+ u8 *len;
+ u8 ranging_role = 0;
+ size_t _len;
+
+ /* Proximity Ranging EDCA Capability Attribute */
+ wpabuf_put_u8(buf, PR_ATTR_EDCA_CAPABILITY);
+ len = wpabuf_put(buf, 2); /* Attribute length to be filled */
+
+ /* Ranging Role */
+ if (edca_data->ista_support)
+ ranging_role |= PR_ISTA_SUPPORT;
+ if (edca_data->rsta_support)
+ ranging_role |= PR_RSTA_SUPPORT;
+ wpabuf_put_u8(buf, ranging_role);
+
+ /* Ranging Parameters */
+ wpabuf_put_le16(buf, edca_data->edca_hw_caps);
+
+ pr_buf_add_channel_list(buf, edca_data->country, &edca_data->channels);
+
+ _len = (u8 *) wpabuf_put(buf, 0) - len - 2;
+ WPA_PUT_LE16(len, _len);
+ wpa_hexdump(MSG_DEBUG, "PR: * EDCA Capability Attribute",
+ len + 2, _len);
+}
+
+
+static void pr_buf_add_ntb_capa_info(struct wpabuf *buf,
+ const struct ntb_capabilities *ntb_data)
+{
+ u8 *len;
+ u8 ranging_role = 0;
+ size_t _len;
+
+ /* Proximity Ranging 11az NTB Capability Attribute */
+ wpabuf_put_u8(buf, PR_ATTR_NTB_CAPABILITY);
+ len = wpabuf_put(buf, 2);
+
+ /* Ranging Role */
+ if (ntb_data->ista_support)
+ ranging_role |= PR_ISTA_SUPPORT;
+ if (ntb_data->rsta_support)
+ ranging_role |= PR_RSTA_SUPPORT;
+ wpabuf_put_u8(buf, ranging_role);
+
+ /* Ranging Parameter */
+ wpabuf_put_le32(buf, ntb_data->ntb_hw_caps);
+
+ pr_buf_add_channel_list(buf, ntb_data->country, &ntb_data->channels);
+
+ _len = (u8 *) wpabuf_put(buf, 0) - len - 2;
+ WPA_PUT_LE16(len, _len);
+ wpa_hexdump(MSG_DEBUG, "PR: * NTB Capability Attribute", len + 2, _len);
+}
+
+
+static void pr_buf_add_dira(struct wpabuf *buf, const struct pr_dira *dira)
+{
+ u8 *len;
+ size_t _len;
+
+ /* Proximity Ranging Device Identity Resolution attribute */
+ wpabuf_put_u8(buf, PR_ATTR_DEVICE_IDENTITY_RESOLUTION);
+
+ /* Length to be filled */
+ len = wpabuf_put(buf, 2);
+
+ wpabuf_put_u8(buf, dira->cipher_version);
+ wpabuf_put_data(buf, dira->nonce, dira->nonce_len);
+ wpabuf_put_data(buf, dira->tag, dira->tag_len);
+
+ /* Update attribute length */
+ _len = (u8 *) wpabuf_put(buf, 0) - len - 2;
+ WPA_PUT_LE16(len, _len);
+
+ wpa_printf(MSG_DEBUG, "PR: * DIRA");
+}
+
+
+struct wpabuf * pr_prepare_usd_elems(struct pr_data *pr, const u8 *src_addr)
+{
+ u32 ie_type;
+ struct wpabuf *buf, *buf2;
+ struct pr_capabilities pr_caps;
+ struct pr_dira dira;
+
+ buf = wpabuf_alloc(1000);
+ if (!buf)
+ return NULL;
+
+ pr_get_ranging_capabilities(pr, &pr_caps);
+ pr_buf_add_ranging_capa_info(buf, &pr_caps);
+
+ if (pr->cfg->edca_ista_support || pr->cfg->edca_rsta_support) {
+ struct edca_capabilities edca_caps;
+
+ pr_get_edca_capabilities(pr, &edca_caps);
+ pr_buf_add_edca_capa_info(buf, &edca_caps);
+ }
+
+ if (pr->cfg->ntb_ista_support || pr->cfg->ntb_rsta_support) {
+ struct ntb_capabilities ntb_caps;
+
+ pr_get_ntb_capabilities(pr, &ntb_caps);
+ pr_buf_add_ntb_capa_info(buf, &ntb_caps);
+ }
+
+ if (!pr_derive_dira(pr, src_addr, &dira))
+ pr_buf_add_dira(buf, &dira);
+
+ ie_type = (OUI_WFA << 8) | PR_OUI_TYPE;
+ buf2 = pr_encaps_elem(buf, ie_type);
+ wpabuf_free(buf);
+
+ return buf2;
+}
+
+
+static int pr_parse_attribute(u8 id, const u8 *data, u16 len,
+ struct pr_message *msg)
+{
+ switch (id) {
+ case PR_ATTR_STATUS:
+ if (len < 1) {
+ wpa_printf(MSG_INFO,
+ "PR: Invalid Proximity Ranging Status Attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->status_ie = data;
+ msg->status_ie_len = len;
+ wpa_printf(MSG_DEBUG, "PR: Status Code %u", data[0]);
+ break;
+ case PR_ATTR_RANGING_CAPABILITY:
+ if (len < 35) {
+ wpa_printf(MSG_INFO,
+ "PR: Too short Proximity Ranging Capability Attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->pr_capability = data;
+ msg->pr_capability_len = len;
+ wpa_printf(MSG_DEBUG,
+ "PR: Ranging Protocol Type: %02x PASN Type: %02x",
+ data[0], data[1]);
+ break;
+ case PR_ATTR_EDCA_CAPABILITY:
+ if (len < 10) {
+ wpa_printf(MSG_INFO,
+ "PR: Too short Proximity Ranging EDCA Capability Attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->edca_capability = data;
+ msg->edca_capability_len = len;
+ wpa_printf(MSG_DEBUG,
+ "PR: EDCA Ranging Role %02x, Ranging Parameters %04x",
+ data[0], WPA_GET_LE16(data + 1));
+ break;
+ case PR_ATTR_NTB_CAPABILITY:
+ if (len < 12) {
+ wpa_printf(MSG_INFO,
+ "PR: Too short Proximity Ranging 11az NTB Capability Attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->ntb_capability = data;
+ msg->ntb_capability_len = len;
+ wpa_printf(MSG_DEBUG,
+ "PR: NTB Ranging Role %02x, Ranging Parameter %04x",
+ data[0], WPA_GET_LE32(data + 1));
+ break;
+ case PR_ATTR_OPERATION_MODE:
+ if (len < 9) {
+ wpa_printf(MSG_INFO,
+ "PR: Invalid Proximity Ranging Operation Mode Attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->op_mode = data;
+ msg->op_mode_len = len;
+ break;
+ case PR_ATTR_DEVICE_IDENTITY_RESOLUTION:
+ if (len < 1 + DEVICE_IDENTITY_NONCE_LEN +
+ DEVICE_IDENTITY_TAG_LEN) {
+ wpa_printf(MSG_INFO, "PR: Too short DIRA (length %d)",
+ len);
+ return -1;
+ }
+ msg->dira = data;
+ msg->dira_len = len;
+ wpa_printf(MSG_DEBUG, "PR: DIRA cipher version %u", data[0]);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "PR: Skipped unknown attribute %d (length %d)",
+ id, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * pr_parse_proximity_ranging_element - Parse Proximity Ranging element
+ * @buf: Concatenated PR element(s) payload
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The caller is responsible for clearing the msg data structure before
+ * calling this function.
+ */
+static int pr_parse_proximity_ranging_element(const struct wpabuf *buf,
+ struct pr_message *msg)
+{
+ const u8 *pos = wpabuf_head_u8(buf);
+ const u8 *end = pos + wpabuf_len(buf);
+
+ wpa_printf(MSG_DEBUG, "PR: Parsing Proximity Ranging element");
+
+ while (pos < end) {
+ u16 attr_len;
+ u8 id;
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "PR: Invalid PR attribute");
+ return -1;
+ }
+ id = *pos++;
+ attr_len = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_printf(MSG_DEBUG, "PR: Attribute %d length %u",
+ id, attr_len);
+ if (attr_len > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Attribute underflow (len=%u left=%d)",
+ attr_len, (int) (end - pos));
+ wpa_hexdump(MSG_MSGDUMP, "PR: Data", pos, end - pos);
+ return -1;
+ }
+ if (pr_parse_attribute(id, pos, attr_len, msg))
+ return -1;
+ pos += attr_len;
+ }
+
+ return 0;
+}
+
+
+static void pr_parse_free(struct pr_message *msg)
+{
+ wpabuf_free(msg->pr_attributes);
+ msg->pr_attributes = NULL;
+}
+
+
+/**
+ * pr_parse_elements - Parse Proximity Ranging element(s)
+ * @data: Elements from the message
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The caller is responsible for clearing the msg data structure before
+ * calling this function.
+ *
+ * Note: The caller must free temporary memory allocations by calling
+ * pr_parse_free() when the parsed data is not needed anymore.
+ */
+static int pr_parse_elements(const u8 *data, size_t len, struct pr_message *msg)
+{
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(data, len, &elems, true) == ParseFailed)
+ return -1;
+
+ msg->pr_attributes = ieee802_11_vendor_ie_concat(data, len,
+ PR_IE_VENDOR_TYPE);
+ if (msg->pr_attributes &&
+ pr_parse_proximity_ranging_element(msg->pr_attributes, msg)) {
+ wpa_printf(MSG_INFO,
+ "PR: Failed to parse Proximity Ranging element data");
+ if (msg->pr_attributes)
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "PR: Proximity Ranging element payload",
+ msg->pr_attributes);
+ pr_parse_free(msg);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int pr_process_channels(const u8 *channel_list, size_t channel_list_len,
+ struct pr_channels *ch)
+{
+ u8 channels;
+ const u8 *pos, *end;
+ u8 op_class_count;
+
+ if (channel_list_len < 1)
+ return -1;
+
+ pos = channel_list;
+ end = channel_list + channel_list_len;
+
+ /* Number of Channel Entries */
+ /* Get total count of the operational classes */
+ op_class_count = pos[0];
+ wpa_printf(MSG_DEBUG, "PR: Total operational classes: %u",
+ op_class_count);
+ pos++;
+
+ /* Channel Entry List */
+ ch->op_classes = 0;
+ while (end - pos > 2 && (ch->op_classes <= op_class_count)) {
+ struct pr_op_class *cl = &ch->op_class[ch->op_classes];
+
+ cl->op_class = *pos++; /* Operating Class */
+ channels = *pos++; /* Number of Channels */
+
+ /* Channel List */
+ if (channels > end - pos) {
+ wpa_printf(MSG_INFO,
+ "PR: Invalid channel list channel %d, size: %ld",
+ channels, end - pos);
+ return -1;
+ }
+ cl->channels = channels > PR_MAX_OP_CLASS_CHANNELS ?
+ PR_MAX_OP_CLASS_CHANNELS : channels;
+ os_memcpy(cl->channel, pos, cl->channels);
+ pos += channels;
+ ch->op_classes++;
+ if (ch->op_classes == PR_MAX_OP_CLASSES)
+ break;
+ }
+
+ if (ch->op_classes != op_class_count &&
+ ch->op_classes < PR_MAX_OP_CLASSES) {
+ wpa_printf(MSG_INFO,
+ "PR: Channel list count mismatch %lu != %d",
+ ch->op_classes, op_class_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void pr_process_ranging_capabilities(const u8 *caps, size_t caps_len,
+ struct pr_capabilities *pr_caps)
+{
+ const u8 *pos;
+
+ if (!caps || caps_len < 3 + WPS_DEV_NAME_MAX_LEN) {
+ wpa_printf(MSG_INFO,
+ "PR: Invalid Proximity Ranging Capability Attribute");
+ return;
+ }
+
+ pos = caps;
+
+ /* Ranging Protocol Type */
+ if (*pos & PR_EDCA_BASED_RANGING)
+ pr_caps->edca_support = true;
+ if (*pos & PR_NTB_SECURE_LTF_BASED_RANGING) {
+ pr_caps->secure_he_ltf = true;
+ pr_caps->ntb_support = true;
+ }
+ if (*pos & PR_NTB_OPEN_BASED_RANGING)
+ pr_caps->ntb_support = true;
+
+ pos++;
+ /* PASN Type */
+ pr_caps->pasn_type = *pos;
+
+ pos++;
+ /* 6GHz band */
+ pr_caps->support_6ghz = *pos & BIT(0);
+
+ pos++;
+ /* Device Name */
+ os_memset(pr_caps->device_name, 0, WPS_DEV_NAME_MAX_LEN + 1);
+ os_memcpy(pr_caps->device_name, pos, WPS_DEV_NAME_MAX_LEN);
+
+ wpa_printf(MSG_DEBUG,
+ "PR: Device name=%s, edca capability=%x, ntb capability=%x, secure LTF capability=%u, 6GHz=%u",
+ pr_caps->device_name, pr_caps->edca_support,
+ pr_caps->ntb_support, pr_caps->secure_he_ltf,
+ pr_caps->support_6ghz);
+}
+
+
+static void pr_process_edca_capabilities(const u8 *caps, size_t caps_len,
+ struct edca_capabilities *edca_caps)
+{
+ const u8 *pos, *end;
+
+ if (caps_len < 7)
+ return;
+
+ pos = caps;
+ end = caps + caps_len;
+
+ /* Ranging Role */
+ if (*pos & PR_ISTA_SUPPORT)
+ edca_caps->ista_support = true;
+ if (*pos & PR_RSTA_SUPPORT)
+ edca_caps->rsta_support = true;
+ pos++;
+
+ /* Ranging Parameters */
+ edca_caps->edca_hw_caps = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* Country String */
+ os_memcpy(edca_caps->country, pos, 3);
+ pos += 3;
+
+ pr_process_channels(pos, end - pos, &edca_caps->channels);
+
+ wpa_printf(MSG_DEBUG,
+ "PR: EDCA ISTA support=%u, EDCA RSTA support=%u, op classes count=%lu, country=%c%c",
+ edca_caps->ista_support, edca_caps->rsta_support,
+ edca_caps->channels.op_classes,
+ valid_country_ch(edca_caps->country[0]) ?
+ edca_caps->country[0] : '_',
+ valid_country_ch(edca_caps->country[1]) ?
+ edca_caps->country[1] : '_');
+}
+
+
+static void pr_process_ntb_capabilities(const u8 *caps, size_t caps_len,
+ struct ntb_capabilities *ntb_caps,
+ bool secure_ltf)
+{
+ const u8 *pos, *end;
+
+ if (caps_len < 9)
+ return;
+
+ pos = caps;
+ end = caps + caps_len;
+
+ /* Ranging Role */
+ if (*pos & PR_ISTA_SUPPORT)
+ ntb_caps->ista_support = true;
+ if (*pos & PR_RSTA_SUPPORT)
+ ntb_caps->rsta_support = true;
+ if (secure_ltf)
+ ntb_caps->secure_he_ltf = true;
+ pos++;
+
+ /* Ranging Parameter */
+ ntb_caps->ntb_hw_caps = WPA_GET_LE32(pos);
+ pos += 4;
+
+ /* Country String */
+ os_memcpy(ntb_caps->country, pos, 3);
+ pos += 3;
+
+ pr_process_channels(pos, end - pos, &ntb_caps->channels);
+
+ wpa_printf(MSG_DEBUG,
+ "PR: NTB ISTA support=%u, NTB RSTA support=%u, op classes count=%lu, secure HE-LTF=%u, country=%c%c",
+ ntb_caps->ista_support, ntb_caps->rsta_support,
+ ntb_caps->channels.op_classes,
+ ntb_caps->secure_he_ltf,
+ valid_country_ch(ntb_caps->country[0]) ?
+ ntb_caps->country[0] : '_',
+ valid_country_ch(ntb_caps->country[1]) ?
+ ntb_caps->country[1] : '_');
+}
+
+
+void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len,
+ const u8 *peer_addr, unsigned int freq)
+{
+ struct pr_device *dev;
+ struct pr_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ os_memcpy(msg.pr_device_addr, peer_addr, ETH_ALEN);
+
+ if (pr_parse_elements(ies, ies_len, &msg)) {
+ wpa_printf(MSG_INFO,
+ "PR: Failed to parse Proximity Ranging element(s)");
+ pr_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.pr_capability) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Ranging caps not present, ignoring proximity device "
+ MACSTR, MAC2STR(peer_addr));
+ pr_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.edca_capability && !msg.ntb_capability) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Neither EDCA nor NTB capabilities are present, ignoring proximity device "
+ MACSTR, MAC2STR(peer_addr));
+ pr_parse_free(&msg);
+ return;
+ }
+
+ dev = pr_create_device(pr, peer_addr);
+ if (!dev) {
+ pr_parse_free(&msg);
+ wpa_printf(MSG_INFO, "PR: Failed to create a device");
+ return;
+ }
+
+ os_get_reltime(&dev->last_seen);
+ dev->listen_freq = freq;
+ dev->discovery_type = PR_DISCOVERY_TYPE_USD;
+
+ pr_process_ranging_capabilities(msg.pr_capability,
+ msg.pr_capability_len, &dev->pr_caps);
+
+ if (dev->pr_caps.edca_support && msg.edca_capability)
+ pr_process_edca_capabilities(msg.edca_capability,
+ msg.edca_capability_len,
+ &dev->edca_caps);
+
+ if (dev->pr_caps.ntb_support && msg.ntb_capability)
+ pr_process_ntb_capabilities(msg.ntb_capability,
+ msg.ntb_capability_len,
+ &dev->ntb_caps,
+ dev->pr_caps.secure_he_ltf);
+
+ if (msg.dira && msg.dira_len)
+ pr_validate_dira(pr, dev, msg.dira, msg.dira_len);
+
+ if (pr->cfg->device_found)
+ pr->cfg->device_found(pr->cfg->cb_ctx, dev);
+
+ pr_parse_free(&msg);
+}
+
+
+/**
+ * pr_ensure_oob_peer - Add a minimal OOB peer entry if not already present
+ */
+int pr_ensure_oob_peer(struct pr_data *pr, const u8 *addr, int freq)
+{
+ struct pr_device *dev;
+
+ if (!pr || !addr)
+ return -1;
+
+ dev = pr_get_device(pr, addr);
+ if (dev)
+ return 0;
+
+ dev = pr_create_device(pr, addr);
+ if (!dev) {
+ wpa_printf(MSG_INFO, "PR: Failed to create OOB peer " MACSTR,
+ MAC2STR(addr));
+ return -1;
+ }
+
+ dev->discovery_type = PR_DISCOVERY_TYPE_OOB;
+ if (freq)
+ dev->listen_freq = freq;
+
+ wpa_printf(MSG_DEBUG, "PR: OOB peer " MACSTR " created at PASN_START",
+ MAC2STR(addr));
+ return 0;
+}
+
+
+#ifdef CONFIG_PASN
+
+static bool pr_eq_ranging_capa_params(const struct pr_device *dev,
+ const struct pr_capabilities *caps)
+{
+ if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB)
+ return true;
+
+ return dev->pr_caps.edca_support == caps->edca_support &&
+ dev->pr_caps.ntb_support == caps->ntb_support &&
+ dev->pr_caps.pasn_type == caps->pasn_type &&
+ dev->pr_caps.secure_he_ltf == caps->secure_he_ltf &&
+ dev->pr_caps.support_6ghz == caps->support_6ghz &&
+ os_strcmp(dev->pr_caps.device_name, caps->device_name) == 0;
+}
+
+
+static bool pr_eq_edca_params(const struct pr_device *dev,
+ const struct edca_capabilities *edca_caps)
+{
+ if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB)
+ return true;
+
+ return dev->edca_caps.ista_support == edca_caps->ista_support &&
+ dev->edca_caps.rsta_support == edca_caps->rsta_support &&
+ dev->edca_caps.edca_hw_caps == edca_caps->edca_hw_caps &&
+ os_memcmp(dev->edca_caps.country, edca_caps->country, 3) == 0;
+}
+
+
+static bool pr_eq_ntb_params(const struct pr_device *dev,
+ const struct ntb_capabilities *ntb_caps)
+{
+ if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB)
+ return true;
+
+ return dev->ntb_caps.ista_support == ntb_caps->ista_support &&
+ dev->ntb_caps.rsta_support == ntb_caps->rsta_support &&
+ dev->ntb_caps.ntb_hw_caps == ntb_caps->ntb_hw_caps &&
+ os_memcmp(dev->ntb_caps.country, ntb_caps->country, 3) == 0;
+}
+
+
+static void pr_buf_add_operation_mode(struct wpabuf *buf,
+ struct operation_mode *mode)
+{
+ u8 *len;
+ size_t _len;
+
+ /* Proximity Ranging Operation Mode Attribute */
+ wpabuf_put_u8(buf, PR_ATTR_OPERATION_MODE);
+ /* Length to be filled */
+ len = wpabuf_put(buf, 2);
+
+ /* Ranging Protocol Type */
+ wpabuf_put_u8(buf, mode->protocol_type);
+
+ /* Ranging Role */
+ wpabuf_put_u8(buf, mode->role);
+
+ pr_buf_add_channel_list(buf, mode->country, &mode->channels);
+
+ /* Update attribute length */
+ _len = (u8 *) wpabuf_put(buf, 0) - len - 2;
+ WPA_PUT_LE16(len, _len);
+ wpa_hexdump(MSG_DEBUG, "PR: * Operation Mode", len + 2, _len);
+}
+
+
+static void pr_buf_add_ranging_neg_status(struct wpabuf *buf, u8 status)
+{
+ /* Proximity Ranging Status Attribute */
+ wpabuf_put_u8(buf, PR_ATTR_STATUS);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, status);
+ wpa_printf(MSG_DEBUG, "PR: * Role negotiation status %u", status);
+}
+
+
+static void pr_process_op_mode(const u8 *caps, size_t caps_len,
+ struct operation_mode *op_mode)
+{
+ const u8 *pos, *end;
+
+ if (!caps || caps_len < 6)
+ return;
+
+ pos = caps;
+ end = caps + caps_len;
+
+ /* Ranging Protocol Type */
+ op_mode->protocol_type = *pos;
+ pos++;
+
+ /* Ranging Role */
+ op_mode->role = *pos;
+ pos++;
+
+ /* Country String */
+ os_memcpy(op_mode->country, pos, 3);
+ pos += 3;
+
+ pr_process_channels(pos, end - pos, &op_mode->channels);
+
+ wpa_printf(MSG_DEBUG,
+ "PR: protocol type=%u, role type=%u, country=%c%c, channel size=%lu",
+ op_mode->protocol_type, op_mode->role,
+ valid_country_ch(op_mode->country[0]) ?
+ op_mode->country[0] : '_',
+ valid_country_ch(op_mode->country[1]) ?
+ op_mode->country[1] : '_',
+ op_mode->channels.op_classes);
+}
+
+
+static void pr_choose_best_channel(struct pr_channels *common_channel,
+ u8 *op_class, u8 *op_channel)
+{
+ int bw;
+ int max_bw = 0;
+ const struct oper_class_map *map;
+ size_t i;
+
+ if (!common_channel || !common_channel->op_classes ||
+ !common_channel->op_class[0].channels) {
+ wpa_printf(MSG_INFO,
+ "PR: Empty channel list, cannot get best channel");
+ *op_class = 0;
+ *op_channel = 0;
+ return;
+ }
+
+ for (i = 0; i < common_channel->op_classes; i++) {
+ map = get_oper_class(NULL,
+ common_channel->op_class[i].op_class);
+ if (!map)
+ continue;
+ bw = oper_class_bw_to_int(map);
+ if (bw > max_bw) {
+ *op_class = common_channel->op_class[i].op_class;
+ *op_channel = common_channel->op_class[i].channel[0];
+ max_bw = bw;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "PR: Choose the operating class: %u, operating channel: %u",
+ *op_class, *op_channel);
+}
+
+
+static u8 pr_pasn_get_best_op_mode(struct pr_data *pr, u8 peer_supp_roles,
+ struct operation_mode *op_mode,
+ struct operation_mode *res_op_mode)
+{
+ u8 ranging_type = 0;
+ struct pr_channels common_chan;
+ struct pr_channels *own_channels = NULL;
+ int status = PR_NEGOTIATION_FAIL;
+ u8 op_class = 0, op_channel = 0;
+ bool own_ista_support = false, own_rsta_support = false;
+
+ if (op_mode->protocol_type &
+ (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+ if (op_mode->protocol_type == PR_NTB_SECURE_LTF_BASED_RANGING &&
+ !pr->cfg->secure_he_ltf) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Secure HE-LTF not supported");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if ((op_mode->protocol_type &
+ PR_NTB_SECURE_LTF_BASED_RANGING) && pr->cfg->secure_he_ltf)
+ ranging_type = PR_NTB_SECURE_LTF_BASED_RANGING;
+ else
+ ranging_type = PR_NTB_OPEN_BASED_RANGING;
+
+ own_ista_support = pr->cfg->ntb_ista_support;
+ own_rsta_support = pr->cfg->ntb_rsta_support;
+ own_channels = &pr->cfg->ntb_channels;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose NTB Ranging Protocol");
+ } else if (op_mode->protocol_type & PR_EDCA_BASED_RANGING) {
+ ranging_type = PR_EDCA_BASED_RANGING;
+
+ own_ista_support = pr->cfg->edca_ista_support;
+ own_rsta_support = pr->cfg->edca_rsta_support;
+ own_channels = &pr->cfg->edca_channels;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose EDCA Ranging Protocol");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Invalid Ranging Protocol, proposed type 0x%x",
+ op_mode->protocol_type);
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (!own_ista_support && !own_rsta_support) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Device can't fulfill any requested Ranging Role");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (own_channels == NULL || !own_channels->op_classes) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Invalid or empty channel list to negotiate");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ pr_channels_intersect(own_channels, &op_mode->channels, &common_chan);
+ if (!common_chan.op_classes) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: No common channels to perform ranging");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support &&
+ !(peer_supp_roles & PR_RSTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Device cannot act as RSTA and cannot update role");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_RSTA_SUPPORT && !own_ista_support &&
+ !(peer_supp_roles & PR_ISTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Device cannot act as ISTA and cannot update role");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT && own_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ } else if (op_mode->role == PR_RSTA_SUPPORT && own_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ } else if ((op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support) &&
+ ((peer_supp_roles & PR_RSTA_SUPPORT) && own_ista_support)) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = PR_NEGOTIATION_UPDATE;
+ } else if ((op_mode->role == PR_RSTA_SUPPORT && !own_ista_support) &&
+ ((peer_supp_roles & PR_ISTA_SUPPORT) && own_rsta_support)) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = PR_NEGOTIATION_UPDATE;
+ } else if (op_mode->role == (PR_RSTA_SUPPORT | PR_ISTA_SUPPORT)) {
+ if ((pr->cfg->preferred_ranging_role || !own_ista_support) &&
+ own_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose RSTA role");
+ } else if ((!pr->cfg->preferred_ranging_role ||
+ !own_rsta_support) && own_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose ISTA role");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "PR PASN: Failed to choose device role");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ res_op_mode->protocol_type = ranging_type;
+ os_memcpy(res_op_mode->country, pr->cfg->country, 3);
+
+ if (res_op_mode->role == PR_RSTA_SUPPORT) {
+ pr_copy_channels(&res_op_mode->channels, &common_chan,
+ pr->cfg->support_6ghz);
+ } else {
+ pr_choose_best_channel(&common_chan, &op_class, &op_channel);
+ if (!op_class || !op_channel) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Couldn't choose a common channel for ranging in ISTA role");
+ return PR_NEGOTIATION_FAIL;
+ }
+ res_op_mode->channels.op_classes = 1;
+ res_op_mode->channels.op_class[0].channels = 1;
+ res_op_mode->channels.op_class[0].channel[0] = op_channel;
+ res_op_mode->channels.op_class[0].op_class = op_class;
+ wpa_printf(MSG_DEBUG,
+ "PR: Choose operating class %u, channel %u",
+ op_class, op_channel);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Ranging Protocol=%u, Role=%u, country=%c%c, status=%d, op class size=%lu",
+ res_op_mode->protocol_type, res_op_mode->role,
+ valid_country_ch(res_op_mode->country[0]) ?
+ res_op_mode->country[0] : '_',
+ valid_country_ch(res_op_mode->country[1]) ?
+ res_op_mode->country[1] : '_',
+ status, res_op_mode->channels.op_classes);
+
+ return status;
+}
+
+
+static u8 pr_pasn_get_final_op_mode(struct pr_data *pr, u8 supp_roles,
+ struct operation_mode *op_mode,
+ struct operation_mode *res_op_mode)
+{
+ u8 ranging_type = 0;
+ struct pr_channels common_chan;
+ struct pr_channels *own_channels = NULL;
+ int status = PR_NEGOTIATION_FAIL;
+ u8 op_class = 0, op_channel = 0;
+ bool own_ista_support = false, own_rsta_support = false;
+
+ if (op_mode->protocol_type &
+ (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+ if (op_mode->protocol_type == PR_NTB_SECURE_LTF_BASED_RANGING &&
+ !pr->cfg->secure_he_ltf) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Secure HE-LTF not supported");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if ((op_mode->protocol_type &
+ PR_NTB_SECURE_LTF_BASED_RANGING) && pr->cfg->secure_he_ltf)
+ ranging_type = PR_NTB_SECURE_LTF_BASED_RANGING;
+ else
+ ranging_type = PR_NTB_OPEN_BASED_RANGING;
+
+ own_ista_support = pr->cfg->ntb_ista_support;
+ own_rsta_support = pr->cfg->ntb_rsta_support;
+ own_channels = &pr->cfg->ntb_channels;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose NTB Ranging Protocol");
+ } else if (op_mode->protocol_type & PR_EDCA_BASED_RANGING) {
+ ranging_type = PR_EDCA_BASED_RANGING;
+
+ own_ista_support = pr->cfg->edca_ista_support;
+ own_rsta_support = pr->cfg->edca_rsta_support;
+ own_channels = &pr->cfg->edca_channels;
+ wpa_printf(MSG_DEBUG, "PR PASN: Choose EDCA Ranging Protocol");
+ } else {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Invalid Ranging Protocol, proposed type 0x%x",
+ op_mode->protocol_type);
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support) {
+ wpa_printf(MSG_INFO, "PR: Device cannot act as RSTA");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_RSTA_SUPPORT && !own_ista_support) {
+ wpa_printf(MSG_INFO, "PR: Device cannot act as ISTA");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ } else if (op_mode->role == PR_RSTA_SUPPORT) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = PR_NEGOTIATION_SUCCESS;
+ } else {
+ wpa_printf(MSG_INFO, "PR: Invalid Ranging Role proposed");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ pr_channels_intersect(own_channels, &op_mode->channels, &common_chan);
+ if (!common_chan.op_classes) {
+ wpa_printf(MSG_INFO,
+ "PR: No common channels to perform ranging");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ pr_choose_best_channel(&common_chan, &op_class, &op_channel);
+ if (!op_class || !op_channel) {
+ wpa_printf(MSG_INFO,
+ "PR: Couldn't choose a common channel for ranging");
+ return PR_NEGOTIATION_FAIL;
+ }
+
+ res_op_mode->protocol_type = ranging_type;
+ os_memcpy(res_op_mode->country, pr->cfg->country, 3);
+ res_op_mode->channels.op_classes = 1;
+ res_op_mode->channels.op_class[0].channels = 1;
+ res_op_mode->channels.op_class[0].channel[0] = op_channel;
+ res_op_mode->channels.op_class[0].op_class = op_class;
+ wpa_printf(MSG_DEBUG, "PR: Choose operating class %u, channel %u",
+ op_class, op_channel);
+
+ return status;
+}
+
+
+static int pr_prepare_pasn_pr_elem(struct pr_data *pr, struct wpabuf *extra_ies,
+ bool add_dira, u8 ranging_role,
+ u8 ranging_type, int forced_pr_freq)
+{
+ u32 ie_type;
+ struct wpabuf *buf, *buf2;
+ struct pr_dira dira;
+ struct pr_capabilities pr_caps;
+ struct edca_capabilities edca_caps;
+ struct ntb_capabilities ntb_caps;
+ struct operation_mode op_mode;
+ struct pr_channels op_channels;
+ u8 forced_op_class = 0, forced_op_channel = 0;
+ enum hostapd_hw_mode hw_mode;
+
+ buf = wpabuf_alloc(1000);
+ if (!buf)
+ return -1;
+
+ pr_get_ranging_capabilities(pr, &pr_caps);
+ pr_buf_add_ranging_capa_info(buf, &pr_caps);
+
+ if (ranging_type & PR_EDCA_BASED_RANGING) {
+ pr_get_edca_capabilities(pr, &edca_caps);
+ pr_buf_add_edca_capa_info(buf, &edca_caps);
+ pr_copy_channels(&op_channels, &edca_caps.channels,
+ pr->cfg->support_6ghz);
+ } else if (ranging_type & PR_NTB_OPEN_BASED_RANGING ||
+ ranging_type & PR_NTB_SECURE_LTF_BASED_RANGING) {
+ pr_get_ntb_capabilities(pr, &ntb_caps);
+ pr_buf_add_ntb_capa_info(buf, &ntb_caps);
+ pr_copy_channels(&op_channels, &ntb_caps.channels,
+ pr->cfg->support_6ghz);
+ } else {
+ wpa_printf(MSG_INFO, "PR: Unsupported ranging_type 0x%x",
+ ranging_type);
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ os_memset(&op_mode, 0, sizeof(struct operation_mode));
+ op_mode.role = ranging_role;
+ op_mode.protocol_type = ranging_type;
+ os_memcpy(op_mode.country, pr->cfg->country, 3);
+
+ if (forced_pr_freq) {
+ hw_mode = ieee80211_freq_to_channel_ext(forced_pr_freq, 0, 0,
+ &forced_op_class,
+ &forced_op_channel);
+ if (hw_mode == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO, "PR: Invalid forced_pr_freq");
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ op_mode.channels.op_classes = 1;
+ op_mode.channels.op_class[0].channels = 1;
+ op_mode.channels.op_class[0].channel[0] = forced_op_channel;
+ op_mode.channels.op_class[0].op_class = forced_op_class;
+ } else {
+ pr_copy_channels(&op_mode.channels, &op_channels,
+ pr->cfg->support_6ghz);
+ }
+
+ pr_buf_add_operation_mode(buf, &op_mode);
+
+ /* PR Device Identity Resolution attribute */
+ if (!pr_derive_dira(pr, pr->cfg->dev_addr, &dira))
+ pr_buf_add_dira(buf, &dira);
+
+ ie_type = (OUI_WFA << 8) | PR_OUI_TYPE;
+ buf2 = pr_encaps_elem(buf, ie_type);
+ wpabuf_free(buf);
+
+ if (wpabuf_tailroom(extra_ies) < wpabuf_len(buf2)) {
+ wpa_printf(MSG_INFO,
+ "PR: Not enough room for PR element in PASN Frame");
+ wpabuf_free(buf2);
+ return -1;
+ }
+ wpabuf_put_buf(extra_ies, buf2);
+ wpabuf_free(buf2);
+
+ return 0;
+}
+
+
+static struct wpabuf * pr_pasn_generate_rsnxe(struct pr_data *pr, int akmp)
+{
+ u32 capab;
+ size_t flen = 0;
+ struct wpabuf *buf;
+
+ capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN);
+
+ if (wpa_key_mgmt_sae(akmp))
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ if (pr->cfg->secure_he_ltf)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+
+ while (capab >> flen * 8)
+ flen++;
+
+ buf = wpabuf_alloc(2 + flen);
+ if (!buf)
+ return NULL;
+
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+ wpa_printf(MSG_DEBUG, "PR: RSNXE capabilities: %04x", capab);
+ wpabuf_put_u8(buf, WLAN_EID_RSNX);
+ wpabuf_put_u8(buf, flen);
+ while (flen--) {
+ wpabuf_put_u8(buf, capab & 0xff);
+ capab = capab >> 8;
+ }
+
+ return buf;
+}
+
+
+/* SSID used for deriving SAE pt for PR security */
+#define PR_PASN_SSID "516F9A010000"
+
+static void pr_pasn_set_password(struct pasn_data *pasn, u8 pasn_type,
+ const char *passphrase)
+{
+ int pasn_groups[4] = { 0, 0, 0, 0 };
+ size_t len;
+
+ if (!passphrase)
+ return;
+
+ len = os_strlen(passphrase);
+
+ if ((pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) &&
+ (pasn_type & (PR_PASN_DH19_UNAUTH | PR_PASN_DH19_AUTH))) {
+ pasn_groups[0] = 20;
+ pasn_groups[1] = 19;
+ } else if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) {
+ pasn_groups[0] = 20;
+ } else {
+ pasn_groups[0] = 19;
+ }
+ pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) PR_PASN_SSID,
+ os_strlen(PR_PASN_SSID),
+ (const u8 *) passphrase, len, NULL, 0);
+}
+
+
+static int pr_pasn_initialize(struct pr_data *pr, struct pr_device *dev,
+ const u8 *addr, u8 auth_mode, int freq,
+ u8 ranging_type, const u8 *pmkid)
+{
+ struct wpabuf *rsnxe;
+ struct pasn_data *pasn;
+
+ if (dev->pasn) {
+ wpa_pasn_reset(dev->pasn);
+ } else {
+ dev->pasn = pasn_data_init();
+ if (!dev->pasn)
+ return -1;
+ }
+
+ pasn = dev->pasn;
+ os_memcpy(pasn->own_addr, pr->cfg->dev_addr, ETH_ALEN);
+ os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+
+ if (dev->pasn_role == PR_ROLE_PASN_INITIATOR) {
+ pasn->pmksa = pr->initiator_pmksa;
+ os_memcpy(pasn->bssid, pasn->peer_addr, ETH_ALEN);
+ } else {
+ pasn->pmksa = pr->responder_pmksa;
+ os_memcpy(pasn->bssid, pasn->own_addr, ETH_ALEN);
+ }
+
+ pasn->noauth = 1;
+
+ /* As specified in Proximity Ranging Implementation Considerations for
+ * P2P Operation D1.8, unauthenticated mode PASN with DH group 19
+ * should be supported by all P2P proximity ranging devices. Skip
+ * this check for OOB peers whose capabilities are not known.
+ */
+ if (dev->discovery_type != PR_DISCOVERY_TYPE_OOB &&
+ (!(pr->cfg->pasn_type & BIT(0)) ||
+ !(dev->pr_caps.pasn_type & BIT(0)))) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Unauthenticated DH group 19 NOT supported, PASN type of self 0x%x, peer 0x%x",
+ pr->cfg->pasn_type, dev->pr_caps.pasn_type);
+ return -1;
+ }
+
+ /* As specified in Proximity Ranging Implementation Considerations for
+ * P2P Operation D1.8, EDCA based ranging is only supported with
+ * unauthenticated mode PASN with DH group 19. */
+ if (((pr->cfg->pasn_type & 0xc) && (dev->pr_caps.pasn_type & 0xc)) &&
+ ranging_type != PR_EDCA_BASED_RANGING) {
+ pasn->group = 20;
+ pasn->cipher = WPA_CIPHER_GCMP_256;
+ } else {
+ pasn->group = 19;
+ pasn->cipher = WPA_CIPHER_CCMP;
+ }
+
+ if (pr->cfg->secure_he_ltf &&
+ ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING) {
+ pasn->secure_ltf = true;
+ pasn_enable_kdk_derivation(pasn);
+ } else {
+ pasn_disable_kdk_derivation(pasn);
+ }
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ if (auth_mode == PR_PASN_AUTH_MODE_SAE) {
+ pasn->akmp = WPA_KEY_MGMT_SAE;
+ if (dev->password_valid) {
+ pr_pasn_set_password(pasn, pr->cfg->pasn_type,
+ dev->password);
+ } else if (pr->cfg->global_password_valid) {
+ pr_pasn_set_password(pasn, pr->cfg->pasn_type,
+ pr->cfg->global_password);
+ } else {
+ wpa_printf(MSG_INFO, "PR PASN: Password not available");
+ return -1;
+ }
+ } else if (auth_mode == PR_PASN_AUTH_MODE_PMK && dev->pmk_valid) {
+ if (!dev->pmk_valid) {
+ wpa_printf(MSG_INFO, "PR PASN: PMK not available");
+ return -1;
+ }
+ if (dev->pasn_role == PR_ROLE_PASN_INITIATOR)
+ pasn_initiator_pmksa_cache_add(pr->initiator_pmksa,
+ pasn->own_addr,
+ pasn->peer_addr,
+ dev->pmk,
+ dev->pmk_len,
+ pmkid,
+ WPA_KEY_MGMT_SAE);
+ else
+ pasn_responder_pmksa_cache_add(pr->responder_pmksa,
+ pasn->own_addr,
+ pasn->peer_addr,
+ dev->pmk,
+ dev->pmk_len,
+ pmkid,
+ WPA_KEY_MGMT_SAE);
+ pasn->akmp = WPA_KEY_MGMT_SAE;
+ } else {
+ pasn->akmp = WPA_KEY_MGMT_PASN;
+ }
+
+ pasn->rsn_pairwise = pasn->cipher;
+ pasn->wpa_key_mgmt = pasn->akmp;
+
+ rsnxe = pr_pasn_generate_rsnxe(pr, pasn->akmp);
+ if (rsnxe) {
+ os_free(pasn->rsnxe_ie);
+ pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe),
+ wpabuf_len(rsnxe));
+ wpabuf_free(rsnxe);
+ if (!pasn->rsnxe_ie)
+ return -1;
+ }
+
+ pasn->cb_ctx = pr->cfg->cb_ctx;
+ pasn->send_mgmt = pr->cfg->pasn_send_mgmt;
+ pasn->freq = freq;
+ return 0;
+}
+
+
+static int pr_validate_pasn_request(struct pr_data *pr, struct pr_device *dev,
+ u8 auth_mode, u8 ranging_role,
+ u8 ranging_type)
+{
+ if (!ranging_role || !ranging_type)
+ return -1;
+
+ if (auth_mode == PR_PASN_AUTH_MODE_PASN) {
+ if (!(pr->cfg->pasn_type &
+ (PR_PASN_DH19_UNAUTH | PR_PASN_DH20_UNAUTH)) ||
+ !(dev->pr_caps.pasn_type &
+ (PR_PASN_DH19_UNAUTH | PR_PASN_DH20_UNAUTH))) {
+ wpa_printf(MSG_INFO,
+ "PR: Dev/Peer doesn't support PASN-UNAUTH");
+ return -1;
+ }
+ } else if (auth_mode == PR_PASN_AUTH_MODE_PMK ||
+ auth_mode == PR_PASN_AUTH_MODE_SAE) {
+ if (!(pr->cfg->pasn_type &
+ (PR_PASN_DH19_AUTH | PR_PASN_DH20_AUTH)) ||
+ !(dev->pr_caps.pasn_type &
+ (PR_PASN_DH19_AUTH | PR_PASN_DH20_AUTH))) {
+ wpa_printf(MSG_INFO, "PR: Dev/Peer doesn't support PASN-SAE/PMK");
+ return -1;
+ }
+ }
+
+ if (ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING ||
+ ranging_type == PR_NTB_OPEN_BASED_RANGING) {
+ if (ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING &&
+ (!pr->cfg->secure_he_ltf || !dev->ntb_caps.secure_he_ltf)) {
+ wpa_printf(MSG_INFO,
+ "PR: Dev/Peer doesn't support HE-LTF");
+ return -1;
+ }
+
+ if (ranging_role == PR_ISTA_SUPPORT &&
+ !pr->cfg->ntb_ista_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device doesn't support NTB ISTA role");
+ return -1;
+ }
+
+ if (ranging_role == PR_RSTA_SUPPORT &&
+ !pr->cfg->ntb_rsta_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device doesn't support NTB RSTA role");
+ return -1;
+ }
+
+ if (ranging_role == PR_ISTA_SUPPORT &&
+ !dev->ntb_caps.rsta_support &&
+ !pr->cfg->ntb_rsta_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device and Peer doesn't support NTB RSTA role, no possiblity for negotiation update");
+ return -1;
+ }
+
+ if (ranging_role == PR_RSTA_SUPPORT &&
+ !dev->ntb_caps.ista_support &&
+ !pr->cfg->ntb_ista_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device and Peer doesn't support NTB ISTA role, no possiblity for negotiation update");
+ return -1;
+ }
+ } else if (ranging_type == PR_EDCA_BASED_RANGING) {
+ if (ranging_role == PR_ISTA_SUPPORT &&
+ !pr->cfg->edca_ista_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device doesn't support EDCA ISTA role");
+ return -1;
+ }
+
+ if (ranging_role == PR_RSTA_SUPPORT &&
+ !pr->cfg->edca_rsta_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device doesn't support EDCA RSTA role");
+ return -1;
+ }
+
+ if (ranging_role == PR_ISTA_SUPPORT &&
+ !dev->edca_caps.rsta_support &&
+ !pr->cfg->edca_rsta_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device and Peer doesn't support EDCA RSTA role, no possiblity for negotiation update");
+ return -1;
+ }
+
+ if (ranging_role == PR_RSTA_SUPPORT &&
+ !dev->edca_caps.ista_support &&
+ !pr->cfg->edca_ista_support) {
+ wpa_printf(MSG_INFO,
+ "PR: Device and Peer doesn't support EDCA ISTA role, no possiblity for negotiation update");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq,
+ u8 auth_mode, u8 ranging_role, u8 ranging_type,
+ int forced_pr_freq)
+{
+ int ret = 0;
+ struct pasn_data *pasn;
+ struct pr_device *dev;
+ u8 pmkid[PMKID_LEN];
+ struct wpabuf *extra_ies;
+
+ if (!addr) {
+ wpa_printf(MSG_DEBUG, "PR PASN: Peer address NULL");
+ return -1;
+ }
+
+ dev = pr_get_device(pr, addr);
+ if (!dev) {
+ wpa_printf(MSG_DEBUG, "PR PASN: Peer not known");
+ return -1;
+ }
+
+ if (dev->discovery_type != PR_DISCOVERY_TYPE_OOB &&
+ pr_validate_pasn_request(pr, dev, auth_mode, ranging_role,
+ ranging_type) < 0) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Invalid parameters to initiate authentication");
+ return -1;
+ }
+
+ if (freq == 0)
+ freq = dev->listen_freq;
+
+ dev->pasn_role = PR_ROLE_PASN_INITIATOR;
+
+ if (auth_mode == PR_PASN_AUTH_MODE_PMK && dev->pmk_valid &&
+ os_get_random(pmkid, PMKID_LEN) < 0)
+ return -1;
+
+ if (pr_pasn_initialize(pr, dev, addr, auth_mode, freq, ranging_type,
+ pmkid)) {
+ wpa_printf(MSG_INFO, "PR PASN: Initialization failed");
+ return -1;
+ }
+ pasn = dev->pasn;
+
+ extra_ies = wpabuf_alloc(1500);
+ if (!extra_ies)
+ return -1;
+
+ if (pr_prepare_pasn_pr_elem(pr, extra_ies, false, ranging_role,
+ ranging_type, forced_pr_freq)) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to prepare extra elements");
+ ret = -1;
+ goto out;
+ }
+
+ pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(extra_ies),
+ wpabuf_len(extra_ies));
+
+ if (auth_mode == PR_PASN_AUTH_MODE_PMK) {
+ ret = wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr,
+ pasn->bssid, pasn->akmp, pasn->cipher,
+ pasn->group, pasn->freq, NULL, 0, NULL, 0,
+ NULL);
+ } else {
+ ret = wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr,
+ pasn->bssid, pasn->akmp, pasn->cipher,
+ pasn->group, pasn->freq, NULL, 0, NULL, 0,
+ NULL);
+ }
+ if (ret) {
+ wpa_printf(MSG_INFO, "PR PASN: Failed to start PASN");
+ } else {
+ /* M1 sent successfully - notify that negotiation has started */
+ if (pr->cfg->negotiation_started)
+ pr->cfg->negotiation_started(pr->cfg->cb_ctx, addr,
+ ranging_role,
+ ranging_type);
+ }
+
+out:
+ wpabuf_free(extra_ies);
+ return ret;
+}
+
+
+int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
+ bool acked)
+{
+ int ret = 0;
+ struct pr_device *dev;
+ struct pasn_data *pasn;
+ const struct ieee80211_mgmt *mgmt =
+ (const struct ieee80211_mgmt *) data;
+ u8 self_format_bw, peer_format_bw;
+
+ if (!pr)
+ return -1;
+
+ dev = pr_get_device(pr, mgmt->da);
+ if (!dev || !dev->pasn) {
+ wpa_printf(MSG_INFO, "PR PASN: Peer not found " MACSTR,
+ MAC2STR(mgmt->da));
+ return -1;
+ }
+
+ pasn = dev->pasn;
+ wpa_printf(MSG_DEBUG, "PR PASN: TX status from " MACSTR " ack=%d",
+ MAC2STR(mgmt->da), acked);
+
+ ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+
+ /*
+ * Authentication frame 1 was not acked; return to caller to schedule a
+ * retransmission. Preserve pasn->frame for the retry.
+ */
+ if (ret == 2)
+ return ret;
+
+ if (ret == 1 && acked && pr->cfg->pasn_result)
+ pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role,
+ dev->protocol_type, dev->final_op_class,
+ dev->final_op_channel, pr->cfg->country);
+
+ if (ret == 1 && acked && pr->cfg->get_ranging_params) {
+ if (dev->protocol_type & PR_EDCA_BASED_RANGING) {
+ self_format_bw = pr->cfg->edca_format_and_bw;
+ peer_format_bw = dev->edca_caps.edca_hw_caps &
+ EDCA_FORMAT_AND_BW_MASK;
+ } else if ((dev->protocol_type &
+ PR_NTB_SECURE_LTF_BASED_RANGING) ||
+ (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) {
+ self_format_bw = pr->cfg->ntb_format_and_bw;
+ peer_format_bw = dev->ntb_caps.ntb_hw_caps &
+ NTB_FORMAT_AND_BW_MASK;
+ } else {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Invalid protocol type: %u",
+ dev->protocol_type);
+ goto out;
+ }
+
+ pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr,
+ dev->ranging_role,
+ dev->protocol_type,
+ dev->final_op_class,
+ dev->final_op_channel,
+ self_format_bw,
+ peer_format_bw);
+ pr_flush(pr);
+ }
+
+out:
+ wpabuf_free(pasn->frame);
+ pasn->frame = NULL;
+
+ return ret;
+}
+
+
+int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr)
+{
+ struct pr_device *dev;
+ struct pasn_data *pasn;
+
+ dev = pr_get_device(pr, addr);
+ if (!dev || !dev->pasn || !dev->pasn->frame)
+ return -1;
+
+ pasn = dev->pasn;
+ wpa_printf(MSG_DEBUG, "PR PASN: retransmit Authentication frame 1 to "
+ MACSTR, MAC2STR(addr));
+ return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
+ wpabuf_len(pasn->frame), 0, pasn->freq, 1000);
+}
+
+
+static int pr_process_pasn_ranging_wrapper(struct pr_data *pr,
+ struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int trans_seq)
+{
+ u32 ie_type;
+ const u8 *ies;
+ size_t ies_len;
+ u8 status = PR_NEGOTIATION_FAIL;
+ bool success = false;
+ struct wpabuf *buf, *buf2;
+ struct pr_message msg;
+ struct pr_capabilities caps;
+ struct edca_capabilities edca;
+ struct ntb_capabilities ntb;
+ struct operation_mode op_mode;
+ u8 supp_ranging_role = 0;
+ struct operation_mode res_op_mode;
+ bool ntb_caps_valid = false, edca_caps_valid = false;
+
+ buf = wpabuf_alloc(1000);
+ if (!buf) {
+ wpa_printf(MSG_INFO, "PR: Memory allocation failed");
+ return -1;
+ }
+
+ os_memset(&msg, 0, sizeof(msg));
+ ies = mgmt->u.auth.variable;
+ if (offsetof(struct ieee80211_mgmt, u.auth.variable) > len)
+ return -1;
+ ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ if (pr_parse_elements(ies, ies_len, &msg) || !msg.op_mode) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to parse PR element in Auth1");
+ goto end;
+ }
+
+ if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
+ if (!msg.status_ie || !msg.status_ie_len) {
+ wpa_printf(MSG_DEBUG, "PR INFO: * No status attribute");
+ wpabuf_free(buf);
+ pr_parse_free(&msg);
+ return -1;
+ }
+ if (*msg.status_ie == PR_NEGOTIATION_FAIL) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: * Ranging Negotiation status fail");
+ wpabuf_free(buf);
+ pr_parse_free(&msg);
+ return -1;
+ }
+ }
+
+ if (!msg.op_mode || !msg.op_mode_len ||
+ !msg.pr_capability || !msg.pr_capability_len ||
+ ((!msg.edca_capability || !msg.edca_capability_len) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len)))
+ goto end;
+
+ if (msg.dira && msg.dira_len)
+ pr_validate_dira(pr, dev, msg.dira, msg.dira_len);
+
+ pr_process_op_mode(msg.op_mode, msg.op_mode_len, &op_mode);
+ if (!op_mode.channels.op_classes) {
+ wpa_printf(MSG_INFO, "PR: Received empty channel list");
+ goto end;
+ }
+
+ os_memset(&caps, 0, sizeof(struct pr_capabilities));
+ pr_process_ranging_capabilities(msg.pr_capability,
+ msg.pr_capability_len, &caps);
+ if (!pr_eq_ranging_capa_params(dev, &caps)) {
+ wpa_printf(MSG_INFO, "PR: Ranging capabilities not matching");
+ goto end;
+ }
+
+ pr_get_ranging_capabilities(pr, &caps);
+
+ if ((op_mode.protocol_type & PR_EDCA_BASED_RANGING) &&
+ (!msg.edca_capability || !msg.edca_capability_len))
+ goto end;
+ if ((op_mode.protocol_type & PR_NTB_OPEN_BASED_RANGING) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len))
+ goto end;
+ if ((op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len))
+ goto end;
+
+ if (op_mode.protocol_type &
+ (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+ pr_process_ntb_capabilities(msg.ntb_capability,
+ msg.ntb_capability_len, &ntb,
+ caps.secure_he_ltf);
+
+ if (!pr_eq_ntb_params(dev, &ntb)) {
+ wpa_printf(MSG_INFO,
+ "PR: NTB capabilities not matching");
+ goto end;
+ }
+
+ if (dev->ntb_caps.ista_support)
+ supp_ranging_role |= PR_ISTA_SUPPORT;
+ if (dev->ntb_caps.rsta_support)
+ supp_ranging_role |= PR_RSTA_SUPPORT;
+
+ pr_get_ntb_capabilities(pr, &ntb);
+ ntb_caps_valid = true;
+ } else if (op_mode.protocol_type & PR_EDCA_BASED_RANGING) {
+ pr_process_edca_capabilities(msg.edca_capability,
+ msg.edca_capability_len, &edca);
+ if (!pr_eq_edca_params(dev, &edca)) {
+ wpa_printf(MSG_INFO,
+ "PR: EDCA capabilities not matching");
+ goto end;
+ }
+
+ if (dev->edca_caps.ista_support)
+ supp_ranging_role |= PR_ISTA_SUPPORT;
+ if (dev->edca_caps.rsta_support)
+ supp_ranging_role |= PR_RSTA_SUPPORT;
+
+ pr_get_edca_capabilities(pr, &edca);
+ edca_caps_valid = true;
+ }
+
+ if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1)
+ status = pr_pasn_get_best_op_mode(pr, supp_ranging_role,
+ &op_mode, &res_op_mode);
+ else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2)
+ status = pr_pasn_get_final_op_mode(pr, supp_ranging_role,
+ &op_mode, &res_op_mode);
+
+ if (status != PR_NEGOTIATION_SUCCESS &&
+ status != PR_NEGOTIATION_UPDATE) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Couldn't derive suitable operation mode");
+ goto end;
+ }
+
+ if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
+ pr_buf_add_ranging_capa_info(buf, &caps);
+ if (edca_caps_valid)
+ pr_buf_add_edca_capa_info(buf, &edca);
+ if (ntb_caps_valid)
+ pr_buf_add_ntb_capa_info(buf, &ntb);
+ }
+
+ pr_buf_add_ranging_neg_status(buf, status);
+ pr_buf_add_operation_mode(buf, &res_op_mode);
+
+ dev->ranging_role = res_op_mode.role;
+ dev->protocol_type = res_op_mode.protocol_type;
+
+ if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
+ dev->final_op_channel =
+ res_op_mode.channels.op_class[0].channel[0];
+ dev->final_op_class = res_op_mode.channels.op_class[0].op_class;
+ }
+
+ success = true;
+end:
+ if (!success)
+ pr_buf_add_ranging_neg_status(buf, PR_NEGOTIATION_FAIL);
+
+ ie_type = (OUI_WFA << 8) | PR_OUI_TYPE;
+ buf2 = pr_encaps_elem(buf, ie_type);
+ wpabuf_free(buf);
+
+ wpabuf_free(dev->ranging_wrapper);
+ dev->ranging_wrapper = buf2;
+ pr_parse_free(&msg);
+
+ return 0;
+}
+
+
+static int
+pr_process_pasn_ranging_wrapper_result(struct pr_data *pr,
+ struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ int ret = -1;
+ const u8 *ies;
+ size_t ies_len;
+ struct pr_message msg;
+ struct operation_mode op_mode;
+ struct pr_channels common_chan;
+
+ os_memset(&msg, 0, sizeof(msg));
+ ies = mgmt->u.auth.variable;
+ ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ if (pr_parse_elements(ies, ies_len, &msg) || !msg.op_mode) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to parse PR element in Auth3");
+ goto fail;
+ }
+
+ if (!msg.status_ie || !msg.status_ie_len) {
+ wpa_printf(MSG_INFO, "PR PASN: * No status attribute");
+ goto fail;
+ }
+
+ if (*msg.status_ie == PR_NEGOTIATION_FAIL) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: * Ranging Negotiation status fail");
+ goto fail;
+ }
+
+ if (!msg.op_mode || !msg.op_mode_len)
+ goto fail;
+
+ pr_process_op_mode(msg.op_mode, msg.op_mode_len, &op_mode);
+ if (op_mode.channels.op_classes != 1) {
+ wpa_printf(MSG_INFO, "PR: PASN received invalid channel list");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Frame 3: Operating mode data: Role=%u, protocol type=%u, operating class=%u, channel= %u",
+ op_mode.role, op_mode.protocol_type,
+ op_mode.channels.op_class[0].op_class,
+ op_mode.channels.op_class[0].channel[0]);
+
+ if (op_mode.protocol_type &
+ (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+ if ((op_mode.role & PR_ISTA_SUPPORT) &&
+ !pr->cfg->ntb_rsta_support)
+ goto fail;
+
+ if ((op_mode.role & PR_RSTA_SUPPORT) &&
+ !pr->cfg->ntb_ista_support)
+ goto fail;
+
+ if ((op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) &&
+ !pr->cfg->secure_he_ltf)
+ goto fail;
+
+ pr_channels_intersect(&pr->cfg->ntb_channels, &op_mode.channels,
+ &common_chan);
+ if (common_chan.op_classes == 0) {
+ wpa_printf(MSG_INFO, "PR PASN: No common channel");
+ goto fail;
+ }
+ } else if (op_mode.protocol_type & PR_EDCA_BASED_RANGING) {
+ if ((op_mode.role & PR_ISTA_SUPPORT) &&
+ !pr->cfg->edca_rsta_support)
+ goto fail;
+
+ if ((op_mode.role & PR_RSTA_SUPPORT) &&
+ !pr->cfg->edca_ista_support)
+ goto fail;
+
+ pr_channels_intersect(&pr->cfg->edca_channels,
+ &op_mode.channels, &common_chan);
+ if (common_chan.op_classes == 0)
+ goto fail;
+ }
+
+ if (op_mode.role & PR_RSTA_SUPPORT)
+ dev->ranging_role = PR_ISTA_SUPPORT;
+ else
+ dev->ranging_role = PR_RSTA_SUPPORT;
+ dev->protocol_type = op_mode.protocol_type;
+ dev->final_op_channel = op_mode.channels.op_class[0].channel[0];
+ dev->final_op_class = op_mode.channels.op_class[0].op_class;
+ ret = 0;
+
+fail:
+ pr_parse_free(&msg);
+ return ret;
+}
+
+
+static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int freq)
+{
+ int ret = -1;
+ u8 pasn_type;
+ u8 auth_mode = 0;
+ int pasn_groups[4] = { 0 };
+ struct wpa_ie_data rsn_data;
+ struct ieee802_11_elems elems;
+
+ pasn_type = pr->cfg->pasn_type;
+ if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH) &&
+ pasn_type & (PR_PASN_DH19_UNAUTH | PR_PASN_DH19_AUTH)) {
+ pasn_groups[0] = 20;
+ pasn_groups[1] = 19;
+ } else if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) {
+ pasn_groups[0] = 20;
+ } else {
+ pasn_groups[0] = 19;
+ }
+
+ if (pr_process_pasn_ranging_wrapper(pr, dev, mgmt, len, 1)) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to handle Auth1 action wrapper");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Failed parsing elements in Auth1 frame");
+ goto fail;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data) == 0) {
+ if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE && rsn_data.num_pmkid)
+ auth_mode = PR_PASN_AUTH_MODE_PMK;
+ else if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE)
+ auth_mode = PR_PASN_AUTH_MODE_SAE;
+ else
+ auth_mode = PR_PASN_AUTH_MODE_PASN;
+ }
+
+ dev->pasn_role = PR_ROLE_PASN_RESPONDER;
+ if (pr_pasn_initialize(pr, dev, mgmt->sa, auth_mode, freq,
+ dev->protocol_type, rsn_data.pmkid)) {
+ wpa_printf(MSG_INFO, "PR PASN: Initialize failed");
+ goto fail;
+ }
+
+ pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(dev->ranging_wrapper),
+ wpabuf_len(dev->ranging_wrapper));
+ os_free(dev->pasn->pasn_groups);
+ dev->pasn->pasn_groups = int_array_dup(pasn_groups);
+ if (handle_auth_pasn_1(dev->pasn, pr->cfg->dev_addr, mgmt->sa, mgmt,
+ len, false) < 0) {
+ wpa_printf(MSG_INFO, "PR PASN: Handle Auth1 failed");
+ goto fail;
+ }
+
+ if (!(dev->protocol_type & PR_EDCA_BASED_RANGING) &&
+ pr->cfg->set_keys &&
+ pr->cfg->set_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr, dev->pasn->cipher,
+ dev->pasn->akmp, &dev->pasn->ptk) < 0) {
+ wpa_printf(MSG_INFO, "PR PASN: Key configuration failed");
+ goto fail;
+ }
+
+ /* M1 received and M2 sent - notify that negotiation has started */
+ if (pr->cfg->negotiation_started)
+ pr->cfg->negotiation_started(pr->cfg->cb_ctx, mgmt->sa,
+ dev->ranging_role,
+ dev->protocol_type);
+ ret = 0;
+
+fail:
+ wpabuf_free(dev->ranging_wrapper);
+ dev->ranging_wrapper = NULL;
+ return ret;
+}
+
+
+static int pr_pasn_handle_auth_2(struct pr_data *pr, struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ int ret = -1;
+ struct wpa_pasn_params_data pasn_data;
+
+ if (dev->pasn_role != PR_ROLE_PASN_INITIATOR) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Auth2 not expected on responder");
+ return -1;
+ }
+
+ if (!dev->pasn)
+ return -1;
+
+ if (pr_process_pasn_ranging_wrapper(pr, dev, mgmt, len, 2)) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to handle Auth2 action wrapper");
+ return -1;
+ }
+ pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(dev->ranging_wrapper),
+ wpabuf_len(dev->ranging_wrapper));
+
+ if (wpa_pasn_auth_rx(dev->pasn, (const u8 *) mgmt, len,
+ &pasn_data) < 0) {
+ wpa_printf(MSG_INFO, "PR PASN: wpa_pasn_auth_rx() failed");
+ dev->pasn_role = PR_ROLE_IDLE;
+ goto fail;
+ }
+
+ if (!(dev->protocol_type & PR_EDCA_BASED_RANGING) &&
+ pr->cfg->set_keys &&
+ pr->cfg->set_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr, dev->pasn->cipher,
+ dev->pasn->akmp, &dev->pasn->ptk) < 0) {
+ wpa_printf(MSG_INFO, "PR PASN: Key configuration failed");
+ goto fail;
+ }
+ ret = 0;
+
+fail:
+ wpabuf_free(dev->ranging_wrapper);
+ dev->ranging_wrapper = NULL;
+ return ret;
+}
+
+
+static int pr_pasn_handle_auth_3(struct pr_data *pr, struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u8 self_format_bw, peer_format_bw;
+
+ if (dev->pasn_role != PR_ROLE_PASN_RESPONDER) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Auth3 not expected on initiator");
+ return -1;
+ }
+
+ if (!dev->pasn)
+ return -1;
+
+ if (pr_process_pasn_ranging_wrapper_result(pr, dev, mgmt, len)) {
+ wpa_printf(MSG_INFO,
+ "PR PASN: Failed to handle Auth3 action wrapper");
+ goto fail;
+ }
+
+ if (handle_auth_pasn_3(dev->pasn, pr->cfg->dev_addr, mgmt->sa, mgmt,
+ len) < 0) {
+ wpa_printf(MSG_INFO, "PR PASN: Failed to handle Auth3");
+ goto fail;
+ }
+
+ if (pr->cfg->pasn_result)
+ pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role,
+ dev->protocol_type, dev->final_op_class,
+ dev->final_op_channel, pr->cfg->country);
+
+ if (dev->protocol_type & PR_EDCA_BASED_RANGING) {
+ self_format_bw = pr->cfg->edca_format_and_bw;
+ peer_format_bw = dev->edca_caps.edca_hw_caps &
+ EDCA_FORMAT_AND_BW_MASK;
+
+ } else if ((dev->protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) ||
+ (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) {
+ self_format_bw = pr->cfg->ntb_format_and_bw;
+ peer_format_bw = dev->ntb_caps.ntb_hw_caps &
+ NTB_FORMAT_AND_BW_MASK;
+ } else {
+ wpa_printf(MSG_INFO, "PR PASN: Invalid protocol type: %u",
+ dev->protocol_type);
+ goto fail;
+ }
+
+ if (pr->cfg->get_ranging_params)
+ pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr,
+ dev->ranging_role,
+ dev->protocol_type,
+ dev->final_op_class,
+ dev->final_op_channel,
+ self_format_bw,
+ peer_format_bw);
+ pr_flush(pr);
+ return 0;
+
+fail:
+ /* Clear the keys as M3 processing failed */
+ if (pr->cfg->clear_keys)
+ pr->cfg->clear_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr,
+ dev->pr_device_addr);
+ return -1;
+}
+
+
+int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
+ size_t len, int freq)
+{
+ struct pr_device *dev;
+ u16 auth_alg, auth_transaction;
+
+ dev = pr_get_device(pr, mgmt->sa);
+ if (!dev) {
+ wpa_printf(MSG_INFO, "PR: Peer not found " MACSTR,
+ MAC2STR(mgmt->sa));
+ return -1;
+ }
+
+ if (!ether_addr_equal(mgmt->da, pr->cfg->dev_addr)) {
+ wpa_printf(MSG_INFO, "PR PASN: Not our frame");
+ return -1;
+ }
+
+ if (len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return -1;
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ if (auth_alg != WLAN_AUTH_PASN) {
+ wpa_printf(MSG_INFO,
+ "PR: Unexpected Authentication frame, auth_alg=%d",
+ auth_alg);
+ return -1;
+ }
+
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1)
+ return pr_pasn_handle_auth_1(pr, dev, mgmt, len, freq);
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2)
+ return pr_pasn_handle_auth_2(pr, dev, mgmt, len);
+ if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3)
+ return pr_pasn_handle_auth_3(pr, dev, mgmt, len);
+
+ return -1;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/contrib/wpa/src/common/proximity_ranging.h b/contrib/wpa/src/common/proximity_ranging.h
new file mode 100644
index 000000000000..6ce9f69224bd
--- /dev/null
+++ b/contrib/wpa/src/common/proximity_ranging.h
@@ -0,0 +1,683 @@
+/*
+ * Proxmity Ranging
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PROXIMITY_RANGING_H
+#define PROXIMITY_RANGING_H
+
+#include "wpa_common.h"
+#include "utils/list.h"
+#include "wps/wps_defs.h"
+
+struct ieee80211_mgmt;
+
+#define DEVICE_IDENTITY_KEY_LEN 16
+#define DEVICE_IDENTITY_TAG_LEN 8
+#define DEVICE_IDENTITY_NONCE_LEN 8
+#define DIR_STR_LEN 3
+#define DEVICE_MAX_HASH_LEN 32
+
+/* DIRA Cipher versions */
+#define DIRA_CIPHER_VERSION_128 0
+
+/**
+ * PR_MAX_OP_CLASSES - Maximum number of operating classes
+ */
+#define PR_MAX_OP_CLASSES 30
+
+/**
+ * PR_MAX_OP_CLASS_CHANNELS - Maximum number of channels per operating class
+ */
+#define PR_MAX_OP_CLASS_CHANNELS 60
+
+/**
+ * PR_MAX_PEER - Maximum number of Proximity Ranging peers that device can store
+ */
+#define PR_MAX_PEER 100
+
+/*
+ * Proximity Ranging negotiation status
+ * Proximity Ranging Implementation Considerations for P2P Operation D1.8,
+ * Table 5 (Proximity Ranging Status Attribute format).
+ */
+#define PR_NEGOTIATION_SUCCESS 0
+#define PR_NEGOTIATION_UPDATE 1
+#define PR_NEGOTIATION_FAIL 2
+
+/**
+ * enum pr_session_end_reason - Reason codes for ranging session end
+ */
+enum pr_session_end_reason {
+ PR_SESSION_END_TIMEOUT = 0,
+ PR_SESSION_END_USER_ABORT = 1,
+ PR_SESSION_END_PEER_COMPLETE = 2,
+ PR_SESSION_END_NEG_FAILED = 3,
+};
+
+enum pr_pasn_role {
+ PR_ROLE_IDLE = 0,
+ PR_ROLE_PASN_INITIATOR,
+ PR_ROLE_PASN_RESPONDER,
+};
+
+/**
+ * struct pr_channels - List of supported channels
+ */
+struct pr_channels {
+ /**
+ * struct pr_op_class - Supported operating class
+ */
+ struct pr_op_class {
+ /**
+ * op_class - Operating class
+ */
+ u8 op_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[PR_MAX_OP_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } op_class[PR_MAX_OP_CLASSES];
+
+ /**
+ * op_classes - Number of op_class entries in use
+ */
+ size_t op_classes;
+};
+
+/**
+ * Format and Bandwidth values for EDCA based ranging with range of 10-16
+ * from IEEE Std 802.11-2024, 9.4.2.166 (FTM Parameters element), Table 9-325
+ * (Format And Bandwidth subfield) as specified in Proximity Ranging
+ * Implementation Considerations for P2P Operation, Draft 1.8, Table 8
+ * (Proximity Ranging EDCA Capability Attribute format) for the the Ranging
+ * Parameters field B0-B3.
+ */
+enum edca_format_and_bw_value {
+ EDCA_FORMAT_AND_BW_VHT20 = 10,
+ EDCA_FORMAT_AND_BW_HT40 = 11,
+ EDCA_FORMAT_AND_BW_VHT40 = 12,
+ EDCA_FORMAT_AND_BW_VHT80 = 13,
+ EDCA_FORMAT_AND_BW_VHT80P80 = 14,
+ EDCA_FORMAT_AND_BW_VHT160_DUAL_LO = 15,
+ EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO = 16,
+ EDCA_FORMAT_AND_BW_INVALID
+};
+
+/**
+ * Format and Bandwidth values for NTB based ranging as from IEEE Std
+ * 802.11-2024, 9.4.2.300 (Ranging Parameters element), Table 9-412 (Format And
+ * Bandwidth subfield) as specified in Proximity Ranging Implementation
+ * Considerations for P2P Operation, Draft 1.8, Table 9 (Proximity Ranging 11az
+ * NTB Capability Attribute format) for the Ranging Parameter field B0-B2.
+ */
+enum ntb_format_and_bw_value {
+ NTB_FORMAT_AND_BW_HE20 = 0,
+ NTB_FORMAT_AND_BW_HE40 = 1,
+ NTB_FORMAT_AND_BW_HE80 = 2,
+ NTB_FORMAT_AND_BW_HE80P80 = 3,
+ NTB_FORMAT_AND_BW_HE160_DUAL_LO = 4,
+ NTB_FORMAT_AND_BW_HE160_SINGLE_LO = 5,
+ NTB_FORMAT_AND_BW_INVALID
+};
+
+struct pr_capabilities {
+ u8 pasn_type;
+
+ char device_name[WPS_DEV_NAME_MAX_LEN + 1];
+
+ bool edca_support;
+
+ bool ntb_support;
+
+ bool secure_he_ltf;
+
+ bool support_6ghz;
+};
+
+struct edca_capabilities {
+ bool ista_support;
+
+ bool rsta_support;
+
+/**
+ * Ranging Parameters field for device specific EDCA capabilities
+ * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8,
+ * Table 8 (Proximity Ranging EDCA Capability Attribute format).
+ */
+#define EDCA_FORMAT_AND_BW 0
+#define EDCA_MAX_TX_ANTENNA 4
+#define EDCA_MAX_RX_ANTENNA 7
+
+#define EDCA_FORMAT_AND_BW_MASK 0x000F
+#define EDCA_MAX_TX_ANTENNA_MASK 0x0007
+#define EDCA_MAX_RX_ANTENNA_MASK 0x0007
+ u16 edca_hw_caps;
+
+ char country[3];
+
+ struct pr_channels channels;
+};
+
+struct ntb_capabilities {
+ bool ista_support;
+
+ bool rsta_support;
+
+ bool secure_he_ltf;
+
+/**
+ * Ranging Parameter field for NTB capabilities
+ * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8,
+ * Table 9 (Proximity Ranging 11az NTB Capability Attribute format).
+ */
+#define NTB_FORMAT_AND_BW 0
+#define MAX_TX_LTF_REPETATIONS 3
+#define MAX_RX_LTF_REPETATIONS 6
+#define MAX_RX_LTF_TOTAL 9
+#define MAX_TX_LTF_TOTAL 11
+#define MAX_RX_STS_LE_80 13
+#define MAX_RX_STS_GT_80 16
+#define MAX_TX_STS_LE_80 19
+#define MAX_TX_STS_GT_80 22
+
+#define NTB_FORMAT_AND_BW_MASK 0x00000007
+
+/* Max TX LTF repetations supported for non trigger based ranging */
+#define MAX_TX_LTF_REPETATIONS_MASK 0x00000007
+
+/* Max RX LTF repetations supported for non trigger based ranging */
+#define MAX_RX_LTF_REPETATIONS_MASK 0x00000007
+
+/* Max RX LTF total supported for non trigger based ranging */
+#define MAX_RX_LTF_TOTAL_MASK 0x00000003
+
+/* Max TX LTF total supported for non trigger based ranging */
+#define MAX_TX_LTF_TOTAL_MASK 0x00000003
+
+/* To configure max R2I STS for Bandwidth less than or equal to 80 MHz */
+#define MAX_RX_STS_LE_80_MASK 0x00000007
+
+/* To configure max R2I STS for Bandwidth greater than 80Mz */
+#define MAX_RX_STS_GT_80_MASK 0x00000007
+
+/* To configure max I2R STS for Bandwidth less than or equal to 80 MHz */
+#define MAX_TX_STS_LE_80_MASK 0x00000007
+
+/* To configure max I2R STS for Bandwidth greater than 80 MHz */
+#define MAX_TX_STS_GT_80_MASK 0x00000007
+ u32 ntb_hw_caps;
+
+ char country[3];
+
+ struct pr_channels channels;
+};
+
+/*
+ * Proximity Ranging Attribute IDs
+ * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8,
+ * Table 4 (Proximity Ranging Attribute ID list).
+ */
+enum pr_attr_id {
+ PR_ATTR_STATUS = 0,
+ PR_ATTR_RANGING_CAPABILITY = 1,
+ PR_ATTR_EDCA_CAPABILITY = 2,
+ PR_ATTR_NTB_CAPABILITY = 3,
+ PR_ATTR_OPERATION_MODE = 4,
+ PR_ATTR_WLAN_AP_INFO = 5,
+ PR_ATTR_DEVICE_IDENTITY_RESOLUTION = 6,
+ PR_ATTR_VENDOR_SPECIFIC = 221,
+};
+
+/*
+ * Proximity Ranging capabilities in Ranging Protocol Type field,
+ * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8,
+ * Table 7 (Proximity Ranging Capability Attribute format).
+ */
+#define PR_EDCA_BASED_RANGING BIT(0)
+#define PR_NTB_SECURE_LTF_BASED_RANGING BIT(1)
+#define PR_NTB_OPEN_BASED_RANGING BIT(2)
+
+/**
+ * Ranging Role field in EDCA capabilities
+ * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8,
+ * Table 8 (Proximity Ranging EDCA Capability Attribute format).
+ */
+#define PR_ISTA_SUPPORT BIT(0)
+#define PR_RSTA_SUPPORT BIT(1)
+
+/*
+ * PASN capabilities in PASN Type field
+ * Proximity Ranging Implementation Considerations for P2P Operation D1.8,
+ * Table 7 (Proximity Ranging Capability Attribute).
+ */
+#define PR_PASN_DH19_UNAUTH BIT(0)
+#define PR_PASN_DH19_AUTH BIT(1)
+#define PR_PASN_DH20_UNAUTH BIT(2)
+#define PR_PASN_DH20_AUTH BIT(3)
+
+/* Authentication Mode */
+#define PR_PASN_AUTH_MODE_PASN 0
+#define PR_PASN_AUTH_MODE_SAE 1
+#define PR_PASN_AUTH_MODE_PMK 2
+
+/* Peer discovery type */
+#define PR_DISCOVERY_TYPE_USD 0
+#define PR_DISCOVERY_TYPE_OOB 1
+
+/**
+ * struct pr_pasn_ranging_params - Peer parameters to be used in PASN to trigger
+ * ranging in case PR PASN is successful.
+ */
+struct pr_pasn_ranging_params {
+ enum {
+ PR_PASN_AND_RANGING,
+ PR_STOP_RANGING,
+ } action;
+ u8 peer_addr[ETH_ALEN];
+ u8 ranging_type;
+ u8 ranging_role;
+ u8 pr_pasn_status;
+ u8 auth_mode;
+ int freq;
+ u32 ranging_timeout;
+ u8 src_addr[ETH_ALEN];
+ enum pr_pasn_role pasn_role;
+
+ /**
+ * EDCA based ranging specific parameters
+ *
+ * @burst_period: Burst period in units of 100 milliseconds
+ * @num_bursts_exp: Number of bursts exponent
+ * @ftms_per_burst: Number of FTM frames per burst
+ * @ftmr_retries: Number of retries for FTM Request frame
+ * @burst_duration: Burst duration as defined in IEEE Std 802.11-2024,
+ * Table 9-322 (Burst Duration subfield encoding).
+ */
+ u16 burst_period;
+ u8 num_bursts_exp;
+ u8 ftms_per_burst;
+ u8 ftmr_retries;
+ u8 burst_duration;
+
+ /**
+ * NTB ranging specific parameters
+ *
+ * @min_time_between_measurements: Minimum time between two consecutive
+ * range measurements in units of 100 microseconds.
+ * @max_time_between_measurements: Maximum time between two consecutive
+ * range measurements in units of 10 milliseconds, to avoid FTM
+ * negotiation.
+ * @availability_window: Duration of the Availability Window (AW) in
+ * units of 1 millisecond (0-255 ms).
+ * @nominal_time: Nominal duration between adjacent Availability Windows
+ * in units of milliseconds.
+ */
+ u32 min_time_between_measurements;
+ u32 max_time_between_measurements;
+ u8 availability_window;
+ u32 nominal_time;
+
+ /**
+ * @request_lci: Whether to request LCI
+ * @request_civicloc: Whether to request civic location
+ */
+ bool request_lci;
+ bool request_civicloc;
+
+ /**
+ * @lmr_feedback: Negotiate LMR feedback for NTB ranging.
+ * Only valid when NTB ranging type is set. Required for RSTA
+ * to report measurement results back to the initiator.
+ */
+ bool lmr_feedback;
+
+ /**
+ * @ingress_threshold: Ingress range threshold in millimeters.
+ * The kernel reports a measurement result when the device
+ * moves into this range.
+ */
+ u64 ingress_threshold;
+
+ /**
+ * @egress_threshold: Egress range threshold in millimeters.
+ * The kernel reports a measurement result when the device
+ * moves out of this range.
+ */
+ u64 egress_threshold;
+
+ /**
+ * @pr_suppress_results: Suppress ranging results for PD requests.
+ * Cannot be used with range_report or lmr_feedback.
+ */
+ bool pr_suppress_results;
+
+ u32 continuous_ranging_session_time;
+
+ int forced_pr_freq;
+ u8 ranging_op_class;
+ u16 channel_width; /* channel width in MHz (20/40/80/160/320) */
+ u8 format_bw;
+ u32 center_freq1;
+ u32 center_freq2;
+ u64 cookie;
+
+ /* Per-session credentials - if set, used instead of stored ones */
+ char password[100];
+ bool password_valid;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+};
+
+struct pr_dev_ik {
+ struct dl_list list;
+ u8 dik[DEVICE_IDENTITY_KEY_LEN];
+ char password[100];
+ bool password_valid;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ bool pmk_valid;
+};
+
+/**
+ * struct pr_device_info - Proximity ranging peer information
+ */
+struct pr_device {
+ struct dl_list list;
+ struct os_reltime last_seen;
+ int listen_freq;
+
+ /**
+ * pr_device_addr - PR Device Address of the peer
+ */
+ u8 pr_device_addr[ETH_ALEN];
+
+ struct pr_capabilities pr_caps;
+ struct edca_capabilities edca_caps;
+ struct ntb_capabilities ntb_caps;
+
+ /* Password to be used in PASN-SAE by the Seeker.
+ * This is updated with valid password if DIRA matches for the peer.
+ */
+ char password[100];
+ bool password_valid;
+
+ /* PMK to be used in PASN-PMK by the Seeker.
+ * This is updated with valid PMK if DIRA matches for the peer.
+ */
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ bool pmk_valid;
+
+ /* DevIK of the peer resolved via DIRA verification.
+ * Set to the matched dev_ik->dik when pr_validate_dira() succeeds.
+ * Cleared by pr_clear_dev_iks().
+ */
+ u8 dik[DEVICE_IDENTITY_KEY_LEN];
+ bool dik_valid;
+
+#ifdef CONFIG_PASN
+ /* PASN data structure */
+ struct pasn_data *pasn;
+ struct wpabuf *ranging_wrapper;
+ enum pr_pasn_role pasn_role;
+#endif /* CONFIG_PASN */
+
+ u8 ranging_role;
+ u8 protocol_type;
+ u8 final_op_class;
+ u8 final_op_channel;
+ u8 discovery_type;
+};
+
+
+/**
+ * struct pr_message - Proximity ranging peer information
+ */
+struct pr_message {
+ struct wpabuf *pr_attributes;
+
+ u8 pr_device_addr[ETH_ALEN];
+
+ const u8 *pr_capability;
+ size_t pr_capability_len;
+
+ const u8 *edca_capability;
+ size_t edca_capability_len;
+
+ const u8 *ntb_capability;
+ size_t ntb_capability_len;
+
+ const u8 *dira;
+ size_t dira_len;
+
+ const u8 *status_ie;
+ size_t status_ie_len;
+
+ const u8 *op_mode;
+ size_t op_mode_len;
+};
+
+
+struct pr_config {
+ u8 pasn_type;
+
+ int preferred_ranging_role;
+
+ char country[3];
+
+ u8 dev_addr[ETH_ALEN];
+
+ /**
+ * dev_name - Device Name
+ */
+ char *dev_name;
+
+ bool edca_ista_support;
+
+ bool edca_rsta_support;
+
+ /* Best single format_bw value derived from pd_format_bw_bitmap */
+ u8 edca_format_and_bw;
+
+ u32 edca_min_ranging_interval;
+
+ u8 max_tx_antenna;
+
+ u8 max_rx_antenna;
+
+ struct pr_channels edca_channels;
+
+ bool ntb_ista_support;
+
+ bool ntb_rsta_support;
+
+ bool concurrent_ista_rsta;
+
+ u32 pmsr_max_peers;
+
+ u32 pr_max_peer_ista_role;
+
+ u32 pr_max_peer_rsta_role;
+
+ u8 max_ftms_per_burst;
+
+ bool secure_he_ltf;
+
+ u8 max_tx_ltf_repetations;
+
+ u8 max_rx_ltf_repetations;
+
+ u8 max_tx_ltf_total;
+
+ u8 max_rx_ltf_total;
+
+ u8 max_rx_sts_le_80;
+
+ u8 max_rx_sts_gt_80;
+
+ u8 max_tx_sts_le_80;
+
+ u8 max_tx_sts_gt_80;
+
+ /* PD ranging preamble and bandwidth bitmaps (shared by EDCA and NTB) */
+ u32 pd_preamble_bitmap;
+ u32 pd_format_bw_bitmap;
+
+ /* Best single format_bw value derived from pd_format_bw_bitmap */
+ u8 ntb_format_and_bw;
+
+ u32 ntb_min_ranging_interval;
+
+ struct pr_channels ntb_channels;
+
+ bool support_6ghz;
+
+ /* Cipher version type */
+ int dik_cipher;
+
+ /* Buffer to hold the DevIK */
+ u8 dik_data[DEVICE_IDENTITY_KEY_LEN];
+
+ /* Length of DevIK in octets */
+ size_t dik_len;
+
+ /* DevIK expiration */
+ int expiration;
+
+ /* Global password to be used in PASN-SAE for Advertiser */
+ char global_password[100];
+
+ bool global_password_valid;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * pasn_send_mgmt - Function handler to transmit a Management frame
+ * @ctx: Callback context from cb_ctx
+ * @data: Frame to transmit
+ * @data_len: Length of frame to transmit
+ * @noack: No ack flag
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @wait: How many milliseconds to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait);
+
+ /**
+ * negotiation_started - Called when PASN negotiation begins
+ * @ctx: Callback context from cb_ctx
+ * @peer_addr: MAC address of the peer
+ * @role: Ranging role (initiator or responder)
+ * @protocol_type: Ranging protocol type
+ *
+ * Fired on the initiator after Auth frame 1 (M1) is sent, and on the
+ * responder after Auth frame 1 (M1) is received and M2 is sent back.
+ */
+ void (*negotiation_started)(void *ctx, const u8 *peer_addr, u8 role,
+ u8 protocol_type);
+
+ void (*pasn_result)(void *ctx, u8 role, u8 protocol_type, u8 op_class,
+ u8 op_channel, const char *country);
+
+ int (*set_keys)(void *ctx, const u8 *own_addr, const u8 *peer_addr,
+ int cipher, int akmp, struct wpa_ptk *ptk);
+
+ void (*clear_keys)(void *ctx, const u8 *own_addr, const u8 *peer_addr);
+
+ void (*get_ranging_params)(void *ctx, const u8 *dev_addr,
+ const u8 *peer_addr, u8 ranging_role,
+ u8 protocol_type, u8 op_class, u8 op_channel,
+ u8 self_format_bw, u8 peer_format_bw);
+
+ void (*device_found)(void *ctx, const struct pr_device *dev);
+};
+
+struct pr_data {
+ /**
+ * cfg - PR module configuration
+ *
+ * This is included in the same memory allocation with the
+ * struct pr_data and as such, must not be freed separately.
+ */
+ struct pr_config *cfg;
+
+ struct dl_list devices;
+
+ struct dl_list dev_iks;
+
+ /* PMKSA cache for PASN-PMK authentication */
+ struct rsn_pmksa_cache *initiator_pmksa;
+ struct rsn_pmksa_cache *responder_pmksa;
+
+ /* PR PASN request tracking - similar to pasn_params in wpa_supplicant
+ */
+ struct pr_pasn_ranging_params *pr_pasn_params;
+
+ /* Set when final measurement result received; blocks further results */
+ bool ranging_final_received;
+};
+
+/* PR Device Identity Resolution Attribute parameters */
+struct pr_dira {
+ /* Cipher version type */
+ int cipher_version;
+ /* Nonce used in DIRA attribute */
+ u8 nonce[DEVICE_IDENTITY_NONCE_LEN];
+ /* Length of nonce */
+ size_t nonce_len;
+ /* Tag computed for nonce using NIK */
+ u8 tag[DEVICE_IDENTITY_TAG_LEN];
+ /* Length of tag in octets */
+ size_t tag_len;
+};
+
+struct operation_mode {
+ /* Bitmap for Ranging Protocol type */
+ u8 protocol_type;
+
+ /* Bitmap for Role-ISTA/RSTA */
+ u8 role;
+
+ char country[3];
+
+ struct pr_channels channels;
+};
+
+struct pr_data * pr_init(const struct pr_config *cfg);
+void pr_flush(struct pr_data *pr);
+void pr_deinit(struct pr_data *pr);
+void pr_set_dev_addr(struct pr_data *pr, const u8 *addr);
+void pr_clear_dev_iks(struct pr_data *pr);
+void pr_add_dev_ik(struct pr_data *pr, const u8 *dik, const char *password,
+ const u8 *pmk, size_t pmk_len, bool own);
+int pr_set_peer_credentials(struct pr_data *pr, const u8 *addr,
+ const u8 *pmk, size_t pmk_len,
+ const char *password);
+struct wpabuf * pr_prepare_usd_elems(struct pr_data *pr, const u8 *src_addr);
+void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len,
+ const u8 *peer_addr, unsigned int freq);
+int pr_ensure_oob_peer(struct pr_data *pr, const u8 *addr, int freq);
+int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq,
+ u8 auth_mode, u8 ranging_role, u8 ranging_type,
+ int forced_pr_freq);
+int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
+ bool acked);
+int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr);
+int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
+ size_t len, int freq);
+
+#endif /* PROXIMITY_RANGING_H */
diff --git a/contrib/wpa/src/common/ptksa_cache.c b/contrib/wpa/src/common/ptksa_cache.c
index 918a1cc0c314..39faf0e01816 100644
--- a/contrib/wpa/src/common/ptksa_cache.c
+++ b/contrib/wpa/src/common/ptksa_cache.c
@@ -39,12 +39,21 @@ static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
struct ptksa_cache *ptksa = eloop_ctx;
struct ptksa_cache_entry *e, *next;
struct os_reltime now;
+ struct dl_list expired;
if (!ptksa)
return;
os_get_reltime(&now);
+ dl_list_init(&expired);
+ /*
+ * Move expired entries from the main ptksa list to a temporary
+ * 'expired' list. This prevents issues if the callback (e->cb)
+ * triggers operations like ptksa_cache_flush(), which would iterate
+ * over ptksa->ptksa. By removing entries first, flush operations
+ * will not double-process or double-free these entries.
+ */
dl_list_for_each_safe(e, next, &ptksa->ptksa,
struct ptksa_cache_entry, list) {
if (e->expiration > now.sec)
@@ -53,10 +62,17 @@ static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
MAC2STR(e->addr));
+ dl_list_del(&e->list);
+ ptksa->n_ptksa--;
+ dl_list_add_tail(&expired, &e->list);
+ }
+
+ dl_list_for_each_safe(e, next, &expired,
+ struct ptksa_cache_entry, list) {
+ dl_list_del(&e->list);
if (e->cb && e->ctx)
e->cb(e);
- else
- ptksa_cache_free_entry(ptksa, e);
+ bin_clear_free(e, sizeof(*e));
}
ptksa_cache_set_expiration(ptksa);
@@ -267,6 +283,7 @@ void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
* @life_time_expiry_cb: Callback for alternative expiration handling
* @ctx: Context pointer to save into e->ctx for the callback
* @akmp: The key management mechanism that was used to derive the PTK
+ * @auth_alg: The authentication algorithm that was used to derive the PTK
* Returns: Pointer to the added PTKSA cache entry or %NULL on error
*
* This function creates a PTKSA entry and adds it to the PTKSA cache.
@@ -280,7 +297,7 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
const struct wpa_ptk *ptk,
void (*life_time_expiry_cb)
(struct ptksa_cache_entry *e),
- void *ctx, u32 akmp)
+ void *ctx, u32 akmp, u16 auth_alg)
{
struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
struct os_reltime now;
@@ -306,6 +323,7 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
entry->cb = life_time_expiry_cb;
entry->ctx = ctx;
entry->akmp = akmp;
+ entry->auth_alg = auth_alg;
if (own_addr)
os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
@@ -374,7 +392,8 @@ int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
struct ptksa_cache_entry *
ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
- void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
+ void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp,
+ u16 auth_alg)
{
return NULL;
}
diff --git a/contrib/wpa/src/common/ptksa_cache.h b/contrib/wpa/src/common/ptksa_cache.h
index dd5e7db8f98c..bb0d1071a566 100644
--- a/contrib/wpa/src/common/ptksa_cache.h
+++ b/contrib/wpa/src/common/ptksa_cache.h
@@ -27,6 +27,7 @@ struct ptksa_cache_entry {
void (*cb)(struct ptksa_cache_entry *e);
void *ctx;
u32 akmp;
+ u16 auth_alg;
};
@@ -44,7 +45,8 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
const struct wpa_ptk *ptk,
void (*cb)
(struct ptksa_cache_entry *e),
- void *ctx, u32 akmp);
+ void *ctx, u32 akmp,
+ u16 auth_alg);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
#endif /* PTKSA_CACHE_H */
diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h
index 5b58ffb3943c..1290e36d8d0c 100644
--- a/contrib/wpa/src/common/qca-vendor.h
+++ b/contrib/wpa/src/common/qca-vendor.h
@@ -2,7 +2,7 @@
* Qualcomm Atheros OUI and vendor specific assignments
* Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
- * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -76,6 +76,10 @@ enum qca_radiotap_vendor_ids {
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_5GHZ: Configure TX_NSS in 5 or 6 GHz band
* @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_5GHZ: Configure RX_NSS in 5 or 6 GHz band
*
+ * To effectively clear or reset the NSS configuration, user-space can set the
+ * attribute to 0xFF or 255, allowing the driver to apply the default NSS
+ * configuration.
+ *
* Global chain configuration - Applies to all bands (2.4 GHz and 5/6 GHz)
* The following attributes are used to dynamically configure the number of
* chains to be used for transmitting or receiving the data in the 2.4 GHz and
@@ -111,6 +115,35 @@ enum qca_radiotap_vendor_ids {
* @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_5GHZ: The number of chains to be
* used for receiving the data in the 5/6 GHz band.
*
+ * To effectively clear or reset the chain configuration, user-space can set
+ * the attribute to 0xFF or 255, allowing the driver to apply the default chain
+ * configuration.
+ *
+ * Global chain-mask configuration - Applies to 2.4 GHz or 5/6 GHz band
+ * The following band specific attributes are used to dynamically configure the
+ * global chain masks (e.g., 0x1 for Chain 0, 0x2 for Chain 1, 0x4 for Chain 2)
+ * to be used for transmitting the data in the 2.4 GHz or 5/6 GHz band.
+ *
+ * These attributes can be used independently.
+ * This configuration is allowed when the driver is active.
+ * The driver/firmware will intersect (AND operation) the newly configured
+ * global TX chain mask with other chain configurations (e.g., the global
+ * chain-mask configuration, global chain configuration, and per band chain
+ * configuration) to determine the final active chain mask.
+ *
+ * If the intersection of these configurations results in no available chains,
+ * the driver/firmware will reject the configuration attempt.
+ *
+ * To effectively clear or reset the chain mask configuration, user-space
+ * can set the attribute to a bitmask representing all available chains
+ * (i.e., 0xFF or 255), allowing the driver to apply the default chain mask
+ * configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_2GHZ: Bitmask of chains to be used
+ * for transmitting the data in the 2.4 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_5GHZ: Bitmask of chains to be used
+ * for transmitting the data in the 5/6 GHz band.
+ *
* The following scenarios capture how the driver process the configuration when
* different TX/RX NSS and chain config attributes are used in the command.
*
@@ -230,7 +263,8 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
* supported by the driver. enum qca_wlan_vendor_features defines
- * the possible features.
+ * the possible features that are encoded in
+ * QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
* which supports DFS offloading, to indicate a channel availability check
@@ -1132,6 +1166,39 @@ enum qca_radiotap_vendor_ids {
* Uses the attributes defined in
* enum qca_wlan_vendor_attr_tdls_disc_rsp_ext.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH: This vendor subcommand is
+ * used to configure and indicate the audio transport switch in both
+ * command and event paths. This is used when two or more audio transports
+ * (e.g., WLAN and Bluetooth) are available between peers.
+ *
+ * If the driver needs to perform operations like scan, connection,
+ * roaming, RoC, etc. and AP concurrency policy is set to either
+ * QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO or
+ * QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING, the driver sends
+ * audio transport switch event to userspace. Userspace application upon
+ * receiving the event, can try to switch to the requested audio transport.
+ * The userspace uses this command to send the status of transport
+ * switching (either confirm or reject) to the driver using this
+ * subcommand. The driver continues with the pending operation either upon
+ * receiving the command from userspace or after waiting for a timeout from
+ * sending the event to userspace. The driver can request userspace to
+ * switch to WLAN upon availability of WLAN audio transport once after the
+ * concurrent operations are completed.
+ *
+ * Userspace can also request audio transport switch from non-WLAN to WLAN
+ * using this subcommand to the driver. The driver can accept or reject
+ * depending on other concurrent operations in progress. The driver returns
+ * success if it can allow audio transport when it receives the command or
+ * appropriate kernel error code otherwise. Userspace indicates the audio
+ * transport switch from WLAN to non-WLAN using this subcommand and the
+ * driver can do other concurrent operations without needing to send any
+ * event to userspace. This subcommand is used by userspace only when the
+ * driver advertises support for
+ * QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN.
+ *
+ * The attributes used with this command are defined in enum
+ * qca_wlan_vendor_attr_audio_transport_switch.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY: This vendor subcommand is used to
* configure, retrieve, and report per-link transmit latency statistics.
*
@@ -1227,6 +1294,235 @@ enum qca_radiotap_vendor_ids {
* collection of these statistics has been enabled by the command
* @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY. The attributes for this
* event are defined in enum qca_wlan_vendor_attr_flow_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_USD: Vendor subcommand to implement unsynchronized
+ * service discovery (USD). Based on the type of the USD subcommand the USD
+ * operation to publish, subscribe, update publish, cancel publish, or
+ * cancel subscribe is triggered.
+ *
+ * When used as an event, the driver notifies the status of an USD command.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_usd.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT: This is an extension to
+ * %NL80211_CMD_CONNECT command. Userspace can use this to indicate
+ * additional information to be considered for the subsequent
+ * (re)association request attempts with %NL80211_CMD_CONNECT. The
+ * additional information sent with this command is applicable for the
+ * entire duration of the connection established with %NL80211_CMD_CONNECT,
+ * including the roams triggered by the driver internally due to other
+ * vendor interfaces, driver internal logic, and BTM requests from the
+ * connected AP.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_connect_ext.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE: Vendor subcommand to configure
+ * Wi-Fi Direct mode. This command sets the configuration through
+ * the attributes defined in the enum qca_wlan_vendor_attr_set_p2p_mode.
+ * It is applicable for P2P Group Owner only. This command is used before
+ * starting the GO.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ: Vendor subcommand to request
+ * transmission of a channel usage request. It carries channel usage
+ * information for BSSs that are not infrastructure BSSs or an off channel
+ * TDLS direct link.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_chan_usage_req.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT: Vendor subcommand that can be
+ * used to fetch the current snapshot of scan data stored by firmware
+ * during the offload scans such as PNO (Preferred Network Offload), RTT,
+ * and roaming scans when the Apps or host is in suspended state. This scan
+ * data comprises of only limited information of the scanned BSSs due to
+ * memory limits of the firmware. The BSS information stored in the
+ * firmware may not be pushed to the kernel (cfg80211) scan cache after
+ * Apps or host coming out from suspended state if full Beacon or Probe
+ * Response frame information is not available.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_fw_scan_report.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN: If there are no active Wi-Fi
+ * interfaces for a certain duration, the host driver might trigger idle
+ * shutdown. The host driver rejects the user space commands between start
+ * and completion of the idle shutdown. If a command is rejected, user
+ * space can use this event to determine when to retry the specific
+ * command.
+ *
+ * This is a wiphy specific vendor event and it indicates user space that
+ * the host driver has reached the idle timer and has started or completed
+ * idle shutdown procedure.
+ *
+ * The attributes used with this event are defined in
+ * enum qca_wlan_vendor_attr_idle_shutdown.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE: This vendor subcommand/event is
+ * used for primary link migration.
+ *
+ * This subcommand is used to trigger primary link migration from
+ * user space. Either just one ML client or a bunch of clients can
+ * be migrated.
+ *
+ * This subcommand is used as an event to notify user applications and
+ * subsystems about primary link migration once it is completed
+ * successfully. This event will send the MAC address of the peer for which
+ * the primary link has been changed and the new link ID to ensure primary
+ * link changes in WLAN subsystem are communicated to user applications
+ * and also to manage the load of that primary link in a better way.
+ *
+ * The attributes used with this subcommand/event are defined in
+ * &enum qca_wlan_vendor_attr_pri_link_migrate.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR and
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID are mutually
+ * exclusive attributes. Migration should be requested for either one ML
+ * client or a bunch of ML clients.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG: Vendor subcommand that
+ * can be used to send periodic or on-demand directed Probe Response frames
+ * to a connected peer.
+ *
+ * This command is only applicable for AP/P2P GO mode.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS: Vendor subcommand that can
+ * be used to notify userspace about status updates of a classified flow
+ * learned by the driver with
+ * %QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT.
+ * The attributes for this event are defined in
+ * enum qca_wlan_vendor_attr_flow_status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG: Subcommand to update the
+ * RX MCS MAP capability. This configuration is only applicable to
+ * non-AP STA or non-AP MLD and allowed only in associated state and
+ * valid until disconnection. This command results in the driver triggering
+ * re-association to re-negotiate the updated RX MCS capability with the
+ * peer. The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_rx_mcs_map_params.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE: Vendor subcommand/event
+ * which is used to exchange Tx powerboost operations between
+ * userspace and driver (kernel space).
+ *
+ * The attributes used with this command/event are defined in
+ * enum qca_wlan_vendor_attr_iq_data_inference.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA: Vendor subcommand used to
+ * enable/disable Notice of Absence (NoA) as a GO in a P2P connection.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_p2p_set_noa.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WIPHY: Vendor subcommand/event
+ * which is used to get WLAN statistics from the driver to userspace
+ * application for the WIPHY level interfaces like device/radio.
+ *
+ * The attributes used for this subcommand are defined in
+ * enum qca_wlan_vendor_attr_wlan_telemetry.
+ *
+ * The attributes used for the event reply are defined in
+ * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WDEV: Vendor subcommand/event
+ * which is used to get WLAN statistics from the driver to userspace
+ * application for the WDEV level interfaces like vdev/station.
+ *
+ * The attributes used for this subcommand are defined in
+ * enum qca_wlan_vendor_attr_wlan_telemetry.
+ *
+ * The attributes used for the event reply are defined in
+ * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE: Vendor subcommand used to
+ * notify the userspace about the current active and inactive links
+ * among the affiliated MLO links, whenever there is a change in the
+ * state of any of the links along with the reason.
+ * The attributes used in this event are defined in the
+ * enum qca_wlan_vendor_attr_link_state_change.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DAR: This vendor subcommand is used to initiate
+ * the Dynamic Analytics Report (DAR) request or session terminate from
+ * the driver. The request or session terminate type is identified over
+ * the %QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE attribute.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_dar.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG: Vendor subcommand used to control
+ * a specific feature enablement based on the OUI data and capabilities of
+ * the AP. The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_feature_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS: Vendor subcommand used to retrieve
+ * Wi-Fi and other technologies (e.g., Bluetooth, UWB (Ultra-Wideband), MWS
+ * (Modem Wireless System) coexistence statistics from the driver.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_coex_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS: This vendor subcommand is used to
+ * configure airtime fairness. The attributes used with this subcommand
+ * are defined in enum qca_wlan_vendor_attr_atf_offload_ops.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG: Vendor subcommand used to get or set
+ * Dynamic Channel Selection (DCS) configuration parameters. This enables or
+ * disables different types of interference mitigation. DCS monitors
+ * wireless channels for periodic interference events, typically one event
+ * per second, and automatically switches to a cleaner channel when
+ * necessary.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_dcs.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS: Retrieve Qualcomm Sensing Hub (QSH)
+ * related Wi-Fi statistics from the sensor. Currently supports scan count;
+ * might be extended in the future.
+ *
+ * No attributes are used in the request. The response includes attributes
+ * defined in enum qca_wlan_vendor_attr_qsh_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS: This vendor subcommand is
+ * used to retrieve WLAN host driver side TX/RX statistics.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_host_txrx_stats_attr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS: This vendor subcommand is
+ * used to retrieve IP Accelerator (IPA) ring statistics.
+ * The attributes used with this command are defined in
+ * enum qca_wlan_ipa_ring_stats_attr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH: This vendor subcommand is used to
+ * offload only the authentication process to wpa_supplicant in scenarios
+ * where the SME is implemented in the driver.
+ *
+ * Authentication frames are exchanged between the driver and
+ * wpa_supplicant using %NL80211_CMD_FRAME, and the transmission status
+ * of each frame is reported through %NL80211_CMD_FRAME_TX_STATUS. If the
+ * authentication process results in establishing a PTKSA (e.g., EPPKE
+ * authentication), wpa_supplicant stores the PTKSA in its internal cache.
+ * After the final Authentication frame is successfully transmitted
+ * (confirmed via %NL80211_CMD_FRAME_TX_STATUS), wpa_supplicant installs
+ * the required temporal keys into the driver using %NL80211_CMD_NEW_KEY.
+ * After completing the authentication process, wpa_supplicant will
+ * indicate the success/failure status with the same command to the driver.
+ *
+ * Upon successful authentication, the driver proceeds with association
+ * phase and reports the connection result via %NL80211_CMD_CONNECT. If
+ * association completes successfully, wpa_supplicant retrieves the KCK
+ * and KEK from the PTKSA cache for use in PTK/GTK rekeying operations. If
+ * the association fails, wpa_supplicant clears the PTKSA cache for this
+ * session to avoid using stale key material.
+ *
+ * wpa_supplicant advertises the authentication algorithms it supports for
+ * this subcommand using %QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES via
+ * %QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_external_auth.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1442,7 +1738,7 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP = 229,
QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG = 230,
QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT = 231,
- /* 232 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH = 232,
QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY = 233,
/* 234 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_SDWF_PHY_OPS = 235,
@@ -1459,6 +1755,31 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT = 246,
QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY = 247,
QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT = 248,
+ QCA_NL80211_VENDOR_SUBCMD_USD = 249,
+ QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT = 250,
+ QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE = 251,
+ QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ = 252,
+ QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT = 253,
+ QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN = 254,
+ /* 255 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE = 256,
+ QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG = 257,
+ QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS = 258,
+ QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG = 259,
+ QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE = 260,
+ QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA = 261,
+ QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WIPHY = 262,
+ QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WDEV = 263,
+ QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE = 264,
+ QCA_NL80211_VENDOR_SUBCMD_DAR = 265,
+ QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG = 266,
+ QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS = 267,
+ QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS = 268,
+ QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG = 269,
+ QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS = 270,
+ QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS = 271,
+ QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS = 272,
+ QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH = 273,
};
/* Compatibility defines for previously used subcmd names.
@@ -1485,7 +1806,11 @@ enum qca_wlan_vendor_attr {
*/
QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
- /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+ /* Feature flags contained in a byte array. The feature flags are
+ * identified by their bit index (see &enum qca_wlan_vendor_features)
+ * with the first byte being the least significant one and the last one
+ * being the most significant one. Used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES. */
QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
QCA_WLAN_VENDOR_ATTR_TEST = 8,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
@@ -1688,6 +2013,24 @@ enum qca_wlan_vendor_attr {
*/
QCA_WLAN_VENDOR_ATTR_MLO_CAPABILITY_MAX_STR_LINK_COUNT = 45,
+ /* An array of unsigned 32-bit values used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the switch
+ * count for each antenna.
+ */
+ QCA_WLAN_VENDOR_ATTR_ANT_SWITCH_COUNT = 46,
+
+ /* An array of unsigned 32-bit values used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the active
+ * duration (in seconds) of each antenna.
+ */
+ QCA_WLAN_VENDOR_ATTR_ANT_DURATION = 47,
+
+ /* An array of unsigned 32-bit values used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the
+ * current RSSI of each antenna.
+ */
+ QCA_WLAN_VENDOR_ATTR_ANT_RSSI = 48,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -1839,6 +2182,11 @@ enum qca_wlan_vendor_attr_roam_auth {
* nested attribute are defined in enum qca_wlan_vendor_attr_mlo_links.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS = 15,
+ /* Flag attribute, The driver sets this flag to indicate that both the
+ * (Re)Association Request frame and the corresponding (Re)Association
+ * Response frame were transmitted encrypted over the air.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_ASSOC_ENCRYPTED = 16,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
@@ -1936,7 +2284,9 @@ enum qca_wlan_vendor_attr_p2p_listen_offload {
*
* @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC.
* Used with command to configure channel list using an array of
- * channel numbers (u8).
+ * channel numbers (u8). This represents the list of allowed channels for
+ * the primary and non-primary channel operation. Channels which are not present
+ * in the specified list shouldn't be used as a primary or non-primary channel.
* Note: If both the driver and user-space application supports the 6 GHz band,
* the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional.
@@ -1968,7 +2318,10 @@ enum qca_wlan_vendor_attr_p2p_listen_offload {
*
* @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC.
* Used with command to configure the channel list using an array of channel
- * center frequencies in MHz (u32).
+ * center frequencies in MHz (u32). This represents the list of allowed
+ * frequencies for the primary and non-primary channel operation. Frequencies
+ * which are not present in the specified list shouldn't be used as a primary or
+ * non-primary channel.
* Note: If both the driver and user-space application supports the 6 GHz band,
* the driver first parses the frequency list and if it fails to get a frequency
* list, parses the channel list specified using
@@ -2031,6 +2384,15 @@ enum qca_wlan_vendor_attr_p2p_listen_offload {
* Used with command to configure ACS operation for a specific link affiliated
* to an AP MLD.
*
+ * @QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY: Optional flag
+ * attribute. Used with command to indicate whether the driver is allowed to use
+ * a 6 GHz non-PSC channel as a primary channel. If this flag is indicated the
+ * driver shall not use 6 GHz non-PSC channels as a primary channel even if
+ * %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST includes 6 GHz non-PSC channels.
+ * However, the driver is still allowed to use 6 GHz non-PSC channels specified
+ * in %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST as non-primary channels. User space is
+ * allowed to specify this flag only when the driver indicates support for
+ * %QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC.
*/
enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -2055,6 +2417,7 @@ enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19,
QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME = 20,
QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID = 21,
+ QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY = 22,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -2180,6 +2543,81 @@ enum qca_wlan_vendor_acs_hw_mode {
* that the device supports enhanced audio experience over WLAN feature.
* @QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER: Flag indicates that the device
* in AP mode supports TWT responder mode in HT and VHT modes.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA: Flag indicates that the device
+ * supports RSNE/RSNXE overriding in STA mode. Supplicant should enable
+ * RSN overriding elements use only when the driver indicates this feature
+ * flag. For BSS selection offload to the driver case, the driver shall
+ * strip/modify the RSN Selection element indicated in connect request
+ * elements or add that element if none was provided based on the BSS
+ * selected by the driver.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD: Flag indicates that the driver
+ * supports Unsynchronized Service Discovery to be offloaded to it.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC: Flag indicates that the driver
+ * supports preferring 6 GHz PSC channel as a primary channel in ACS
+ * result.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_P2P_V2: Flag indicates that the driver supports
+ * P2P R2 functionality (P2P R2 Discovery, Pairing, TWT power save, etc).
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_PCC_MODE: Flag indicates that the driver supports
+ * P2P Connection Compatibility mode in which GO allows connection
+ * with both P2P R1 and R2 clients.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_TX_POWER_LIMIT: Flag indicates that the
+ * driver supports enabling TX power limit from the userspace.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_USER_SCENARIO_TO_DSI_MAPPING: Flag indicates
+ * that the driver supports enabling the user scenario to DSI index mapping.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_INDOOR_CH_SCC: Flag indicates driver
+ * support for SCC (Single Channel Concurrency) with a STA connected
+ * indoor channel for P2P GO, SAP, and NAN.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_DFS_CH_SCC_P2P: Flag indicates driver
+ * support for SCC (Single Channel Concurrency) with a STA connected
+ * DFS channel for P2P.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS: Flag indicates that the
+ * driver supports AP assisted DFS channel operation for P2P.
+ * When this feature is advertised, the driver stops transmitting Data
+ * frames on DFS channel and initiates Channel Switch Announcement on any
+ * P2P GO interface to connected P2P Clients present in the P2P Group
+ * immediately after receiving a Channel Switch Announcement from the
+ * connected DFS AP on its STA interface.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GO_CANCEL_ONE_SHOT_NOA: Flag indicates
+ * that the device supports cancellation of firmware-initiated one-shot NoA
+ * schedules on a P2P GO interface. This capability allows the P2P GO to cancel
+ * NoA early when a local STA interface (sharing the same radio) completes a
+ * connection or roaming operation.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA:
+ * Flag indicates that the device in P2P Client mode supports staying awake
+ * during one-shot NoA periods instead of entering sleep. This allows the
+ * group client to immediately receive frames when the P2P GO cancels an NoA
+ * early (using QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA), without
+ * waiting for the originally configured NoA duration to expire.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY: Flag indicates that
+ * the driver supports PMKSA caching privacy as defined in
+ * IEEE P802.11bi/D4.0, 12.16.7. Userspace should enable PMKSA caching
+ * privacy only when both %NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION and
+ * this feature flag are advertised by the driver.
+ *
+ * When both the AP and the non-AP STA support PMKSA caching privacy and
+ * association frame encryption, the driver shall include the PMKIDSNonce
+ * in the (Re)Association Request frame. Upon successful non-FT
+ * (re)association, indicated by %NL80211_CMD_CONNECT, %NL80211_CMD_ROAM,
+ * or %QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH, wpa_supplicant shall
+ * derive a new PMKID for the PMKSA entry used during the current
+ * connection. The derivation uses the PMKIDANonce from the (Re)Association
+ * Response frame and the PMKIDSNonce from the (Re)Association Request
+ * frame. PMKR0Name randomization during FT reassociation will be handled
+ * by the driver.
+ *
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -2208,6 +2646,19 @@ enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST = 22,
QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN = 23,
QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER = 24,
+ QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA = 25,
+ QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD = 26,
+ QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC = 27,
+ QCA_WLAN_VENDOR_FEATURE_P2P_V2 = 28,
+ QCA_WLAN_VENDOR_FEATURE_PCC_MODE = 29,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_TX_POWER_LIMIT = 30,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_USER_SCENARIO_TO_DSI_MAPPING = 31,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_INDOOR_CH_SCC = 32,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_DFS_CH_SCC_P2P = 33,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS = 34,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GO_CANCEL_ONE_SHOT_NOA = 35,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA = 36,
+ QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY = 37,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -2627,6 +3078,9 @@ enum qca_wlan_vendor_scan_priority {
* when AP is operating as MLD to specify which link is requesting the
* scan or which link the scan result is for. No need of this attribute
* in other cases.
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SKIP_CHANNEL_RECENCY_PERIOD: Optional (u32). Skip
+ * scanning channels which are scanned recently within configured time
+ * (in ms).
*/
enum qca_wlan_vendor_attr_scan {
QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
@@ -2645,6 +3099,7 @@ enum qca_wlan_vendor_attr_scan {
QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY = 13,
QCA_WLAN_VENDOR_ATTR_SCAN_PAD = 14,
QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID = 15,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SKIP_CHANNEL_RECENCY_PERIOD = 16,
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
@@ -3115,6 +3570,11 @@ enum qca_wlan_vendor_attr_config {
* %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
* %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes
* when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED.
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE attributes
+ * when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
@@ -3646,6 +4106,223 @@ enum qca_wlan_vendor_attr_config {
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_FOLLOW_AP_PREFERENCE_FOR_CNDS_SELECT = 121,
+ /* 16-bit unsigned value to configure P2P GO beacon interval in TUs.
+ * This attribute is used to update the P2P GO beacon interval
+ * dynamically.
+ *
+ * Updating the beacon interval while the GO continues operating the BSS
+ * will likely interoperability issues and is not recommended to be
+ * used. All the values should be multiples of the minimum used value to
+ * minimize risk of issues.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_P2P_GO_BEACON_INTERVAL = 122,
+
+ /* 8-bit unsigned value. Disable DFS owner capability
+ * 1: disable DFS owner capability in the driver.
+ * 0: reset DFS owner capability to the default DFS owner capability of
+ * the driver.
+ *
+ * If DFS owner capability is disabled, the driver will not start AP
+ * mode operations on DFS channels, and all the features depending on
+ * DFS owner functionality will not be supported.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_OWNER_DISABLE = 123,
+
+ /* 16-bit unsigned value. For probing RSSI on other antennas, this
+ * attribute specifies the number of WLAN probes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_WLAN = 124,
+
+ /* 16-bit unsigned value. For probing RSSI on other antennas, this
+ * attribute specifies the number of BT probes.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_BT = 125,
+
+ /* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+ * threshold. The firmware will start to probe RSSI on other antenna
+ * if WLAN RSSI is lower than the threshold.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_WLAN_RSSI_THRESHOLD = 126,
+
+ /* 16-bit unsigned value. This attribute specifies the BT RSSI
+ * threshold. The firmware will start to probe RSSI on other antenna
+ * if BT RSSI is lower than the threshold.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_BT_RSSI_THRESHOLD = 127,
+
+ /* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+ * difference. The firmware will select a better antenna if WLAN RSSI
+ * difference is larger than the value.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_WLAN_RSSI_DIFF = 128,
+
+ /* 16-bit unsigned value. This attribute specifies the BT RSSI
+ * difference. The firmware will select a better antenna if WLAN RSSI
+ * difference larger than the value.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_BT_RSSI_DIFF = 129,
+
+ /* 8-bit unsigned value to enable/disable setup link Reconfiguration
+ * feature support in STA mode.
+ * 1 - Enable
+ * 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_SETUP_LINK_RECONFIG_SUPPORT = 130,
+
+ /* 8-bit unsigned value to enable/disable DFS No Wait feature support
+ * in AP mode.
+ * 1 - Enable
+ * 0 - Disable.
+ *
+ * DFS No Wait allows AP to be started within the subset of channel
+ * bandwidth that does not require DFS while waiting for CAC to
+ * complete on the subset that requires DFS. If no radar was detected,
+ * switch to the full configured channel bandwidth.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_NO_WAIT_SUPPORT = 131,
+
+ /* Nested attribute to configure the EHT EMLSR operation and EHT MLO
+ * links for the EMLSR operation to the driver in STA mode. This is
+ * runtime configuration on STA after association and the configuration
+ * is valid only for the current association.
+ * Uses enum qca_wlan_vendor_attr_emlsr_info for values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_EMLSR_LINKS = 132,
+
+ /* 8-bit unsigned value
+ *
+ * This setting determines the intensity of the power-saving mode.
+ * The level ranges from 1 to 5, where:
+ * 1 represents the most aggressive (maximum power saving), and
+ * 5 represents the least aggressive (minimal power saving).
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+ * to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL = 133,
+
+ /* 16-bit unsigned value to configure latency tolerance value in
+ * milliseconds.
+ *
+ * This attribute configures latency tolerance value for
+ * STA and P2P Client mode. This is used by the firmware to achieve
+ * optimal power consumption within latency constraints.
+ *
+ * This attribute shall be configured along with
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+ * to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE = 134,
+
+ /* 8-bit unsigned value to control transmit TX power limit. When
+ * this flag attribute is present, the firmware shall apply the vendor
+ * configured TX power limit that is stored in the Board Data File
+ * (BDF) for all bands and all the interfaces.
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_POWER_LIMIT_ENABLE = 135,
+
+ /* 8-bit unsigned integer to configure the driver to enable or disable
+ * the A-MSDU address check validation.
+ * 1 - Enables A-MSDU address check for the
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC address.
+ * 0 - Disables A-MSDU address check for the
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC address
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_AMSDU_ADDR_CHECK_VALIDATION = 136,
+
+ /* 8-bit unsigned value. The TX chain mask to be configured dynamically
+ * for transmitting the data in the 2.4 GHz band.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_2GHZ = 137,
+
+ /* 8-bit unsigned value. The TX chain mask to be configured dynamically
+ * for transmitting the data in the 5/6 GHz band.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_5GHZ = 138,
+
+ /* 8-bit unsigned value to enable or disable the feature to allow
+ * SCC with STA connected indoor channel for P2P GO, SAP, and NAN
+ * to the driver in STA mode. This configuration is applicable only
+ * when a STA interface is in connected state.
+ * When the STA disconnects, any P2P_GO/SAP/NAN interface present in
+ * SCC with STA connected indoor channel will re-evaluate its operating
+ * channel and either remain on the current channel or switch to other
+ * valid channel, depending on regulatory and underlying driver policy.
+ * 1 - Enable, 0 - Disable.
+ *
+ * See
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC
+ * for a more granular configuration of this per peer protocol.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_INDOOR_CH_SCC = 139,
+
+ /* 8-bit unsigned value to enable or disable the feature to allow
+ * SCC with STA connected DFS channel for P2P GO/Client to the driver in
+ * STA mode. This configuration is applicable only when a STA interface
+ * is in connected state and can be updated/modified only in STA
+ * disconnected state.
+ *
+ * When enabled, behavior on a P2P Client is following: On STA
+ * disconnect/roam/CSA to different channel, the P2P Client
+ * interface operating in SCC with STA on DFS channel will disconnect
+ * internally. No Deauthentication frame is sent over the air to the
+ * P2P GO on the DFS channel.
+ *
+ * When enabled, behavior on a P2P GO is following: On STA disconnect/
+ * roam/CSA to different channel, the P2P GO interface will initiate a
+ * Channel Switch Announcement (CSA) to switch to a valid channel
+ * depending on regulatory and underlying driver policy. Beacon frames
+ * with CSA element will be sent in compliance with regulatories after
+ * STA moved away from the DFS channel due to disconnect/roam/CSA.
+ *
+ * 1 - Enable, 0 - Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_DFS_CH_SCC_P2P = 140,
+
+ /*
+ * 8-bit unsigned value to enable or disable QSH-initiated Wi-Fi scans,
+ * providing a mechanism to optimize power consumption. By default, this
+ * feature is enabled.
+ *
+ * Valid values:
+ * 0 - Disable
+ * 1 - Enable
+ *
+ * Values other than 0 or 1 are invalid and shall be rejected.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_QSH_SCAN_CTRL = 141,
+
+ /* 8-bit bitmap to enable the feature to allow SCC with STA connected
+ * indoor channel per peer protocol to the driver in STA mode. This
+ * configuration is applicable only when a STA interface is in
+ * connected state.
+ * When the STA disconnects, any peer protocol interface present in
+ * SCC with STA connected indoor channel will re-evaluate its operating
+ * channel and either remain on the current channel or switch to other
+ * valid channel, depending on regulatory and underlying driver policy.
+ *
+ * Either @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_INDOOR_CH_SCC attribute
+ * or @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC
+ * attribute can be used by userspace at a time to allow indoor channel
+ * SCC with STA.
+ *
+ * bit 0: Setting bit0 indicates to allow SCC with STA connected indoor
+ * channel for P2P
+ * bit 1: Setting bit1 indicates to allow SCC with STA connected indoor
+ * channel for NAN
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC = 142,
+
+ /* Nested attribute to configure or report Estimated Service Parameters
+ * (ESP). This contains nested attributes defined in
+ * enum qca_wlan_vendor_attr_config_esp_param.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PARAMS = 143,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -5309,6 +5986,69 @@ enum qca_wlan_vendor_attr_ll_stats_results {
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_MLO_LINK = 93,
+ /* Unsigned 32 bit value. It represents the number of MSDUs sent by the
+ * driver that were retransmitted and eventually transmitted
+ * successfully.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_RETRY_MSDU_CNT = 94,
+
+ /* Unsigned 32 bit value. It represents the number of MSDUs that were
+ * successfully transmitted by the driver, including those that were
+ * retransmitted and eventually succeeded.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_SUCC_MSDU_CNT = 95,
+
+ /* Unsigned 32 bit value. It represents the number of MSDUs that were
+ * handed off by the driver for transmission but were ultimately dropped
+ * by the firmware.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_FW_DROP_MSDU_CNT = 96,
+
+ /* Unsigned 32 bit value. It represents the number of MSDUs that were
+ * intended for transmission but were dropped by the driver before being
+ * handed off to the firmware.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_DRIVER_DROP_MSDU_CNT = 97,
+
+ /* Unsigned 32 bit value. It represents the number of MSDUs that were
+ * received from hardware fast receiving rings.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RX_DRIVER_MSDU_CNT = 98,
+
+ /* Unsigned 32 bit value. It represents the number of MPDUs that were
+ * received from hardware fast receiving rings.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_RX_DRIVER_MPDU_CNT = 99,
+
+ /* Nested attribute for SNR distribution statistics. This attribute
+ * contains a dynamic SNR distribution histogram and measurement
+ * duration for the interface's operating channel.
+ *
+ * SNR is measured by the target hardware and reported as a distribution
+ * across configurable bins. The bin range and step size are determined
+ * by the firmware and reported via the nested attributes defined in
+ * enum qca_wlan_vendor_attr_ll_stats_snr. Applications must read the
+ * config attributes (SNR_LOWER_BOUND, SNR_UPPER_BOUND, SNR_STEP,
+ * BIN_COUNT) to correctly interpret the BIN_DATA array.
+ *
+ * For non-MLO connection:
+ * - This attribute appears at the interface level and represents the
+ * SNR distribution for the single operating channel.
+ *
+ * For MLO connection:
+ * - This attribute ONLY appears inside
+ * %QCA_WLAN_VENDOR_ATTR_LL_STATS_MLO_LINK
+ * - Each link reports its own SNR distribution for its specific
+ * operating channel.
+ * - There is no aggregated SNR distribution at the interface level,
+ * as SNR distributions from different channels cannot be
+ * meaningfully combined.
+ *
+ * This is a nested attribute containing SNR distribution attributes
+ * defined in enum qca_wlan_vendor_attr_ll_stats_snr.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_SNR = 100,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -5328,6 +6068,64 @@ enum qca_wlan_vendor_attr_ll_stats_type {
};
/**
+ * enum qca_wlan_vendor_attr_ll_stats_snr - SNR distribution attributes
+ *
+ * These attributes report the distribution of received SNR samples collected
+ * by the target hardware. The bin configuration (range and step) is dynamic and
+ * determined by the firmware. Applications must use the config attributes
+ * (SNR_LOWER_BOUND, SNR_UPPER_BOUND, SNR_STEP, BIN_COUNT) to correctly
+ * interpret the BIN_DATA array.
+ *
+ * Bin layout (num_bins = ((upper_bound - lower_bound) / step) + 2):
+ * Bin[0]: SNR < lower_bound dB
+ * Bin[1]: SNR in [lower_bound, lower_bound + step) dB
+ * Bin[i]: SNR in [lower_bound + (i-1)*step, lower_bound + i*step) dB
+ * Bin[num_bins-1]: SNR >= upper_bound dB
+ *
+ * Example (lower=0, upper=100, step=10, num_bins=12):
+ * Bin[0]=SNR<0, Bin[1]=SNR[0,10), ..., Bin[10]=SNR[90,100), Bin[11]=SNR>=100
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MEAS_DUR_US: u32
+ * Total measurement duration in microseconds during which SNR samples
+ * were collected on the operating channel.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_LOWER_BOUND: u32
+ * Lower bound of the SNR measurement range in dB.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_UPPER_BOUND: u32
+ * Upper bound of the SNR measurement range in dB.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_STEP: u32
+ * Step size in dB for each SNR bin.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_COUNT: u32
+ * Total number of bins = ((upper_bound - lower_bound) / step) + 2.
+ * The +2 accounts for out-of-bound bins (below lower_bound and above
+ * upper_bound). This is also the number of u32 elements in BIN_DATA.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_DATA: NLA_BINARY
+ * Array of u32 values, BIN_COUNT elements total. Each element is the
+ * count of SNR samples that fell in the corresponding bin during the
+ * measurement period. See bin layout description above.
+ * The u32 values are in host byte order; no byte-order conversion is
+ * required by the application.
+ */
+enum qca_wlan_vendor_attr_ll_stats_snr {
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MEAS_DUR_US = 1,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_LOWER_BOUND = 2,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_UPPER_BOUND = 3,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_STEP = 4,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_COUNT = 5,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_DATA = 6, /* NLA_BINARY: u32[] */
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MAX =
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
* TDLS configuration to the host driver.
*
@@ -5434,6 +6232,12 @@ enum qca_wlan_vendor_tdls_trigger_mode {
* limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power
* limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V3_0: Select the SAR power
+ * limits version 3.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V4_0: Select the SAR power
+ * limits version 4.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V5_0: Select the SAR power
+ * limits version 5.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
*
* This enumerates the valid set of values that may be supplied for
* attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
@@ -5450,6 +6254,197 @@ enum qca_vendor_attr_sar_limits_selections {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V3_0 = 8,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V4_0 = 9,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V5_0 = 10,
+};
+
+/**
+ * enum qca_wlan_power_scenario - Represents different power scenarios that can
+ * be configured through %QCA_WLAN_VENDOR_ATTR_CONFIG_POWER_SCENARIO.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_VOICE_CALL: Power scenario optimized for voice
+ * calls, balancing connectivity with reduced interference to voice
+ * communications.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF: Power settings for a device held
+ * near head with cellular radio disabled, optimizing Wi-Fi transmission power
+ * to minimize SAR exposure while maintaining connectivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON: Power settings for a device held
+ * near head with cellular radio enabled, managing combined RF exposure from
+ * Wi-Fi and cellular radios to comply with regulatory SAR limits.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF: Power settings for a device
+ * carried on body with cellular radio disabled, optimized for body-worn
+ * position while ensuring regulatory compliance for body SAR limits.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON: Power settings for a device carried
+ * on body with cellular radio enabled, balancing Wi-Fi performance with
+ * cellular coexistence in body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT: Power settings for a device carried on
+ * body with Bluetooth active, optimizing Wi-Fi and Bluetooth coexistence in
+ * body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT: Power settings for a device used as
+ * a hotspot while held near head, optimizing tethering functionality while
+ * managing RF exposure near the user's head.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW: Power settings for a device
+ * used as a hotspot near head with millimeter wave technology active, with
+ * special power settings for high-frequency operation near head.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT: Power settings for a device on
+ * body with both cellular and Bluetooth active, managing triple-radio
+ * coexistence in body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT: Power settings for a device used as
+ * a hotspot while carried on body, optimizing tethering performance in
+ * body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT: Power settings for a device used
+ * as a hotspot on body with Bluetooth active, balancing hotspot and Bluetooth
+ * coexistence in body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW: Power settings for a device
+ * used as a hotspot on body with mmWave technology, optimizing high-frequency
+ * hotspot operation in body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW: Power settings for a device
+ * used as a hotspot on body with both Bluetooth and mmWave active, managing
+ * complex triple-technology coexistence in body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, held near head with cellular radio
+ * disabled, optimizing Wi-Fi power for a larger form factor near head.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, held near head with cellular radio
+ * enabled, managing combined RF exposure in unfolded configuration near head.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, used as hotspot near head, optimizing
+ * tethering performance for unfolded configuration.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, used as a mmWave hotspot near head, with
+ * special power settings for high-frequency tethering in unfolded
+ * configuration.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, carried on body with cellular radio
+ * disabled, optimizing Wi-Fi power for unfolded body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED: Power settings for a foldable
+ * device in unfolded state, carried on body with Bluetooth active, managing
+ * Wi-Fi-Bluetooth coexistence in unfolded body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, carried on body with cellular radio
+ * enabled, balancing Wi-Fi and cellular performance in unfolded body-worn
+ * position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, on body with both cellular and Bluetooth
+ * active, managing triple-radio coexistence in unfolded body-worn
+ * configuration.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, used as a hotspot while on body,
+ * optimizing tethering performance in unfolded body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, used as a hotspot on body with Bluetooth
+ * active, balancing hotspot and Bluetooth operation in unfolded body-worn
+ * position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW_UNFOLDED: Power settings for a
+ * foldable device in unfolded state, used as a mmWave hotspot on body,
+ * optimizing high-frequency tethering in unfolded body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW_UNFOLDED: Power settings for
+ * a foldable device in unfolded state, used as a hotspot on body with both
+ * Bluetooth and mmWave active, managing complex triple-technology coexistence
+ * in unfolded body-worn position.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_REAR_CAMERA: Power settings for a device on
+ * body with rear camera active, optimizing Wi-Fi power to minimize
+ * interference with camera operations while maintaining connectivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED_CAP: Power settings for a
+ * foldable device with capacitive touch in unfolded state, on body with
+ * cellular radio disabled, optimizing Wi-Fi power while ensuring touch
+ * sensitivity is maintained.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED_CAP: Power settings for a
+ * foldable device with capacitive touch in unfolded state, on body with
+ * Bluetooth active, balancing Wi-Fi-Bluetooth coexistence while maintaining
+ * touch sensitivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED_CAP: Power settings for a
+ * foldable device with capacitive touch in unfolded state, on body with
+ * cellular radio enabled, managing Wi-Fi-cellular coexistence while
+ * maintaining touch sensitivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED_CAP: Power settings for
+ * a foldable device with capacitive touch in unfolded state, on body with both
+ * cellular and Bluetooth active, managing triple-radio coexistence while
+ * maintaining touch sensitivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_CAP: Power settings for a device
+ * with capacitive touch on body with cellular radio disabled, optimizing Wi-Fi
+ * power in folded state while ensuring touch sensitivity is maintained.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_CAP: Power settings for a device with
+ * capacitive touch on body with Bluetooth active, balancing Wi-Fi-Bluetooth
+ * coexistence in folded state while maintaining touch sensitivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_CAP: Power settings for a device
+ * with capacitive touch on body with cellular radio enabled, managing
+ * Wi-Fi-cellular coexistence in folded state while maintaining touch
+ * sensitivity.
+ *
+ * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_CAP: Power settings for a device
+ * with capacitive touch on body with both cellular and Bluetooth active,
+ * managing triple-radio coexistence in folded state while maintaining touch
+ *sensitivity.
+ */
+enum qca_wlan_power_scenario {
+ QCA_WLAN_POWER_SCENARIO_VOICE_CALL = 0,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF = 1,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON = 2,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF = 3,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON = 4,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_BT = 5,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT = 6,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW = 7,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT = 8,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT = 9,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT = 10,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW = 11,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW = 12,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF_UNFOLDED = 13,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON_UNFOLDED = 14,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_UNFOLDED = 15,
+ QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW_UNFOLDED = 16,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED = 17,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED = 18,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED = 19,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED = 20,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_UNFOLDED = 21,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_UNFOLDED = 22,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW_UNFOLDED = 23,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW_UNFOLDED = 24,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_REAR_CAMERA = 25,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED_CAP = 26,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED_CAP = 27,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED_CAP = 28,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED_CAP = 29,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_CAP = 30,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_CAP = 31,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_CAP = 32,
+ QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_CAP = 33,
};
/**
@@ -5534,6 +6529,11 @@ enum qca_vendor_attr_sar_limits_spec_modulations {
* This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0.
*
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_USER_SCENARIO: Optional (u32)
+ * value to indicate SAR user scenario to map them to respective
+ * device state indexes. Valid values of user scenarios are enumerated
+ * in enum %qca_wlan_power_scenario.
+ *
* These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS
* and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS.
*/
@@ -5547,6 +6547,7 @@ enum qca_vendor_attr_sar_limits {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8,
+ QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_USER_SCENARIO = 9,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
@@ -5673,11 +6674,21 @@ enum qca_vendor_attr_scan_freq_list_scheme {
* @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to
* trigger the scan on all the valid frequencies to find better
* candidates to roam.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_NEIGHBOR_REPORT_SCAN: Indicates the driver/firmware
+ * to trigger the scan only on the channels obtained from the neighbor
+ * report (e.g., neighbor request/response, RNR, BTM request).
+ *
+ * @QCA_ROAM_SCAN_SCHEME_DEFAULT: Indicates the driver/firmware to clear any
+ * scan scheme configured earlier and revert to the default scan scheme
+ * behavior.
*/
enum qca_roam_scan_scheme {
QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0,
QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1,
QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2,
+ QCA_ROAM_SCAN_SCHEME_NEIGHBOR_REPORT_SCAN = 3,
+ QCA_ROAM_SCAN_SCHEME_DEFAULT = 4,
};
/*
@@ -5715,6 +6726,11 @@ enum qca_roam_scan_scheme {
* @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
* based on the scan results obtained from an external scan (not triggered
* to aim roaming).
+ * @QCA_ROAM_TRIGGER_REASON_WTC: Set if the roam has to be triggered
+ * due to Wireless to Cellular BSS Transition Management (BTM) request.
+ * @QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY: Set if the roam has to be triggered
+ * due to Bluetooth connection is established when the station is connected
+ * in the 2.4 GHz band.
*
* Set the corresponding roam trigger reason bit to consider it for roam
* trigger.
@@ -5735,6 +6751,8 @@ enum qca_vendor_roam_triggers {
QCA_ROAM_TRIGGER_REASON_IDLE = 1 << 10,
QCA_ROAM_TRIGGER_REASON_TX_FAILURES = 1 << 11,
QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12,
+ QCA_ROAM_TRIGGER_REASON_WTC = 1 << 13,
+ QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY = 1 << 14,
};
/*
@@ -6033,6 +7051,11 @@ enum qca_vendor_attr_roam_candidate_selection_criteria {
* qca_roam_scan_scheme.
* It's an optional attribute. If this attribute is not configured, the
* driver shall proceed with default behavior.
+ * The validity of this configuration is controlled by
+ * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION. When that
+ * flag is present, the configuration is valid only until the next
+ * disconnection. When that flag is absent, the configuration persists
+ * across disconnections.
*
* @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
* signifying the RSSI threshold of the current connected AP, indicating
@@ -6187,6 +7210,10 @@ enum qca_vendor_attr_roam_candidate_selection_criteria {
* QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD where the configured
* threshold is used only when the connected AP asks the STA to roam
* through a BTM request.
+ * This attribute should not be used together with
+ * QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS in the same command.
+ * When both have been configured across separate commands, the most recent
+ * configuration takes effect and overwrites the previous one.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF: Unsigned 8-bit value.
* This attribute signifies the RSSI difference threshold between the
@@ -6214,6 +7241,112 @@ enum qca_vendor_attr_roam_candidate_selection_criteria {
* better Wi-Fi bands. E.g., STA would initially connect to a 2.4 GHz BSSID
* and would migrate to 5/6 GHz when it comes closer to the AP (high RSSI
* for 2.4 GHz BSS).
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_2P4GHZ: Unsigned 8-bit
+ * value.
+ * Represents the weightage in percentage (%) of the total score that is
+ * given for the roam scan candidates present on the 2.4 GHz band. The
+ * configuration is valid until next disconnection. If this attribute is
+ * not present, the existing configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_5GHZ: Unsigned 8-bit
+ * value.
+ * Represents the weightage in percentage (%) of the total score that is
+ * given for the roam scan candidates present on the 5 GHz band.
+ * The configuration is valid until next disconnection. If this attribute
+ * is not present, the existing configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_6GHZ: Unsigned 8-bit
+ * value.
+ * Represents the weightage in percentage (%) of the total score that is
+ * given for the roam scan candidates present on the 6 GHz band.
+ * The configuration is valid until next disconnection. If this attribute
+ * is not present, the existing configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_THRESHOLD_PERCENTAGE: Unsigned 8-bit
+ * value.
+ * This attribute indicates the minimum roam score difference in
+ * percentage (%). The roam candidate AP will be ignored if the score
+ * difference percentage between the roam candidate AP and the current
+ * connected AP is less than current connected AP score roam score delta.
+ * The configuration is valid until next disconnection.
+ * If this attribute is not present, the existing configuration shall be
+ * used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT: Unsigned 8-bit
+ * value in dB.
+ * This attribute indicates the RSSI decrement value from the current low
+ * RSSI threshold for the next low RSSI roam trigger when no candidate is
+ * found during the current low RSSI roam trigger. This value is applicable
+ * only for low RSSI roam triggers. This configuration is valid until next
+ * disconnection. If this attribute is not present, the existing
+ * configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_PERIODIC_ROAM_SCAN_INTERVAL: Unsigned 32-bit
+ * value in seconds.
+ * This attribute defines the interval after which the next roam scan will
+ * start if the current scan finds no candidates. The scan repeats at this
+ * interval until a candidate is found.
+ * This configuration is valid until next disconnection. If this attribute
+ * is not present, the existing configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_MIN_DELTA_THRESHOLD: Unsigned 32-bit
+ * value.
+ * This attribute indicates the minimum roam score difference for an AP to
+ * be considered as a candidate. A roam candidate AP will be ignored if
+ * the score difference between the roam candidate AP and the current
+ * connected AP is less than the sum of the current connected AP score and
+ * the roam score delta.
+ *
+ * The configuration is valid until next disconnection. If this attribute
+ * is not present, the existing configuration shall be used.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_BSS_RECONNECT_DISALLOW_PERIOD: Unsigned
+ * 32-bit value.
+ * This attribute specifies the duration (in seconds) of the current BSS
+ * connection from the last successful association, after which the
+ * connected BSS can be considered as a roaming candidate upon receiving
+ * a Deauthentication or Disassociation frame from the BSS, provided no
+ * alternative candidate is available. The connection timer to monitor the
+ * disallow period should start after each successful connection.
+ *
+ * This configuration is valid until next disconnection. If this attribute
+ * is not present, the existing configuration shall be used.
+ * 0 - Always disallow roaming to the current connected BSS when a
+ * Deauthentication or Disassociation frame is received from the connected
+ * BSS.
+ * Other values - Disallow roaming to the current connected BSS for the
+ * specified duration from the last successful connection time when a
+ * Deauthentication or Disassociation frame is received from the connected
+ * BSS.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS: Nested attribute to
+ * configure low RSSI thresholds per band. The nl80211_band enum value of
+ * the respective band is used as the attribute type, and the data in each
+ * such attribute is a signed 32-bit RSSI value in dBm, signifying the low
+ * RSSI threshold applicable when the STA is connected on the corresponding
+ * band. The STA keeps monitoring the connected AP's RSSI and will start
+ * scanning for neighboring APs once the RSSI falls below the threshold
+ * configured for the current band, triggering the roam eventually.
+ * When QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET and
+ * QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT are
+ * configured together with this attribute, the per-band threshold
+ * configured here serves as the low RSSI threshold for the respective
+ * band, and those attributes apply to the per-band threshold accordingly.
+ * The configured per-band thresholds are obtained with the same attribute
+ * for get. Clears the per-band thresholds in the driver when specified
+ * with the clear command.
+ * This attribute should not be used together with
+ * QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD in the same command.
+ * When both have been configured across separate commands, the most recent
+ * configuration takes effect and overwrites the previous one.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION: Flag
+ * attribute to configure the lifetime of the scan scheme set through
+ * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME. When this flag is present, the scan
+ * scheme configuration is valid only until the next disconnection. When
+ * this flag is absent, the scan scheme configuration persists across
+ * disconnections.
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -6247,6 +7380,16 @@ enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF = 29,
QCA_ATTR_ROAM_CONTROL_6GHZ_CANDIDATE_ROAM_RSSI_DIFF = 30,
QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET = 31,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_2P4GHZ = 32,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_5GHZ = 33,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_6GHZ = 34,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_THRESHOLD_PERCENTAGE = 35,
+ QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT = 36,
+ QCA_ATTR_ROAM_CONTROL_PERIODIC_ROAM_SCAN_INTERVAL = 37,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_MIN_DELTA_THRESHOLD = 38,
+ QCA_ATTR_ROAM_CONTROL_CONNECTED_BSS_RECONNECT_DISALLOW_PERIOD = 39,
+ QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS = 40,
+ QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION = 41,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -7417,6 +8560,10 @@ enum qca_wlan_vendor_attr_external_acs_event {
* for External ACS
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AFC_CAPABILITY = 15,
+ /* Link ID attibute (u8) is used to identify a specific link affiliated
+ * to an AP MLD.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LINK_ID = 16,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
@@ -8386,6 +9533,16 @@ enum qca_wlan_vendor_hang_reason {
QCA_WLAN_HANG_FLUSH_LOGS = 35,
/* Host wakeup because of page fault */
QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT = 36,
+ /* Timeout waiting for response of unified vdev connect command from the
+ * firmware. This command combines peer create, vdev start, peer assoc,
+ * and vdev up operations to optimize link switch time.
+ */
+ QCA_WLAN_HANG_VDEV_UNIFIED_CONNECT_RESP_TIMEOUT = 37,
+ /* Timeout waiting for response of unified vdev disconnect command from
+ * the firmware. This command combines peer delete, vdev stop, and vdev
+ * down operations to optimize link switch time.
+ */
+ QCA_WLAN_HANG_VDEV_UNIFIED_DISCONNECT_RESP_TIMEOUT = 38,
};
/**
@@ -8940,6 +10097,22 @@ enum qca_wlan_vendor_attr_ndp_params {
* frames from each other.
*/
QCA_WLAN_VENDOR_ATTR_NDP_GTK_REQUIRED = 33,
+ /* Unsigned 32-bit attribute. Indicates the maximum latency of the NAN
+ * data packets to be transmitted/received in milliseconds.
+ * This attribute is optional, and it is configured for active NDP
+ * session of a given NDP instance ID
+ * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID using the command
+ * %QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_MAX_LATENCY_MS = 34,
+ /* 32-bit unsigned value to indicate throughput in Mbps for the NDP
+ * data of a given NDP session.
+ * This attribute is optional, and it is configured for active NDP
+ * session of a given NDP instance ID
+ * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID using the command
+ * %QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG.
+ */
+ QCA_WLAN_VENDOR_ATTR_NDP_TPUT = 35,
/* keep last */
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
@@ -8987,7 +10160,14 @@ enum qca_wlan_ndp_sub_cmd {
/* Command to indicate the peer about the end request being received */
QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11,
/* Command to indicate the peer of schedule update */
- QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12
+ QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12,
+ /* Command to update dynamic configurations of active NDP sessions.
+ * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID is a mandatory attribute and
+ * at least one of the attributes
+ * %QCA_WLAN_VENDOR_ATTR_NDP_MAX_LATENCY_MS and
+ * %QCA_WLAN_VENDOR_ATTR_NDP_TPUT must be configured in this command.
+ */
+ QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG = 13,
};
/**
@@ -9724,6 +10904,14 @@ enum qca_wlan_vendor_attr_omi_tx {
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT20: EHT20
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40: EHT40
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40PLUS: EHT40 (ext ch +1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40MINUS: EHT40 (ext ch -1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80: EHT80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80P80: EHT 80P80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT160: EHT160
+ * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT320: EHT320
*/
enum qca_wlan_vendor_phy_mode {
QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0,
@@ -9755,6 +10943,14 @@ enum qca_wlan_vendor_phy_mode {
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT20 = 29,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40 = 30,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40PLUS = 31,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40MINUS = 32,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80 = 33,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80P80 = 34,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT160 = 35,
+ QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT320 = 36,
};
/* Attributes for data used by
@@ -10358,6 +11554,97 @@ enum qca_wlan_vendor_attr_wifi_test_config {
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_SCS_TRAFFIC_SUPPORT = 73,
+ /* 8-bit unsigned value to disable or not disable the channel switch
+ * initiation in P2P GO mode.
+ * 0 - Not-disable, 1 - Disable
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_CHAN_SWITCH_INITIATION = 74,
+
+ /* 8-bit unsigned value. This indicates number of random PMKIDs to be
+ * added in the RSNE of the (Re)Association request frames. This is
+ * exclusively used for the scenarios where the device is used as a test
+ * bed device with special functionality and not recommended for
+ * production. Default value is zero. If the user space configures a
+ * non-zero value, that remains in use until the driver is unloaded or
+ * the user space resets the value to zero.
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RSNE_ADD_RANDOM_PMKIDS = 75,
+
+ /* 8-bit unsigned value to configure Triggered SU Beamforming Feedback
+ * support in the EHT capabilities of an Association Request frame.
+ * 1-enable, 0-disable
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TRIG_SU_BFORMING_FEEDBACK = 76,
+
+ /* 8-bit unsigned value to configure the extra EHT-LTFs support in the
+ * EHT capabilities of an Association Request frame.
+ * 1-enable, 0-disable
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_EXTRA_LTF = 77,
+
+ /* 8-bit unsigned integer to configure the firmware to reject AP's BSS
+ * Transition Management (BTM) request frame by sending a BTM response
+ * with error status code.
+ *
+ * 1 - STA rejects AP's BTM request frame
+ * 0 - STA accepts AP's BTM request frame
+ *
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_REJECT = 78,
+
+ /* Nested attribute to control the response of the driver upon receiving
+ * a BTM request from the AP.
+ * Uses the enum qca_wlan_vendor_attr_btm_req_resp attributes.
+ * This attribute is used to configure the STA.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP = 79,
+
+ /* 8-bit unsigned value to configure Restricted TWT feature support
+ * within the EHT capabilities element of the Association Request frame.
+ *
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_RTWT_SUPPORT = 80,
+
+ /* 8-bit unsigned value to configure BTM MLD Recommendation For Multiple
+ * APs Support within the Extended MLD capability of Multi-link element
+ * in the Association Request frame.
+ *
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BTM_RECOMM_MULTI_AP_SUPPORT = 81,
+
+ /* 8-bit unsigned value to set the reserved fields within the MLE
+ * starting from Multi-Link Control to STA control within Per-STA
+ * profile. It does not set the subsequent STA Info or HT/HE/EHT
+ * Capabilities elements.
+ * This configuration is for Probe Request variant in Multi-link Probe
+ * Request frames and Basic variant in Association Request frames.
+ *
+ * This attribute is used to configure the testbed device.
+ * 1-set the reserved fields, 0-default behavior
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_SET_MLE_RESERVED_FIELDS = 82,
+
+ /* 8-bit unsigned value to configure the STA interface to operate in
+ * 20 MHz only mode.
+ *
+ * This attribute is used to configure the testbed device.
+ * 1-set the 20 MHz only mode, 0-default behavior
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_20MHZ_ONLY_STA = 83,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -10439,6 +11726,12 @@ enum qca_wlan_vendor_attr_wifi_test_config {
* related information for example TWT required bit in AP capabilities etc.
* The reason for the notification is sent using
* QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS.
+ *
+ * @QCA_WLAN_TWT_EARLY_TERMINATION_IND: Indicate early termination for the
+ * ongoing SP. Required parameters are configured through
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refer the enum
+ * qca_wlan_vendor_attr_twt_early_termination_ind.
+ * Valid only after the TWT session is successfully setup.
*/
enum qca_wlan_twt_operation {
QCA_WLAN_TWT_SET = 0,
@@ -10453,6 +11746,7 @@ enum qca_wlan_twt_operation {
QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9,
QCA_WLAN_TWT_SET_PARAM = 10,
QCA_WLAN_TWT_NOTIFY = 11,
+ QCA_WLAN_TWT_EARLY_TERMINATION_IND = 12,
};
/**
@@ -10467,8 +11761,9 @@ enum qca_wlan_twt_operation {
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the
* parameters configured for TWT. These parameters are represented by
* enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume,
- * enum qca_wlan_vendor_attr_twt_set_param, or
- * enum qca_wlan_vendor_attr_twt_stats based on the operation.
+ * enum qca_wlan_vendor_attr_twt_set_param, enum qca_wlan_vendor_attr_twt_stats,
+ * or enum qca_wlan_vendor_attr_twt_early_termination_ind based on the
+ * operation.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS: Size is u8, mandatory when
* QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION is set to QCA_WLAN_TWT_NOTIFY.
@@ -10651,11 +11946,48 @@ enum qca_wlan_twt_setup_state {
};
/**
+ * enum qca_wlan_twt_session_suspendable: The values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE: TWT session cannot be suspended.
+ * @QCA_WLAN_TWT_SESSION_SUSPENDABLE: TWT session can be suspended.
+ */
+enum qca_wlan_twt_session_suspendable {
+ QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE = 0,
+ QCA_WLAN_TWT_SESSION_SUSPENDABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_updatable: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_UPDATABLE: TWT session cannot be updated.
+ * @QCA_WLAN_TWT_SESSION_UPDATABLE: TWT session can be updated.
+ */
+enum qca_wlan_twt_session_updatable {
+ QCA_WLAN_TWT_SESSION_NOT_UPDATABLE = 0,
+ QCA_WLAN_TWT_SESSION_UPDATABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_implicit: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_IMPLICIT: TWT session cannot be implicit.
+ * @QCA_WLAN_TWT_SESSION_IMPLICIT: TWT session can be implicit.
+ */
+enum qca_wlan_twt_session_implicit {
+ QCA_WLAN_TWT_SESSION_NOT_IMPLICIT = 0,
+ QCA_WLAN_TWT_SESSION_IMPLICIT = 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
* TWT (Target Wake Time) setup request. These attributes are sent as part of
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
* %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
- * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT and
+ * %QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
* Disable (flag attribute not present) - Individual TWT
@@ -10841,9 +12173,10 @@ enum qca_wlan_twt_setup_state {
* The Broadcast TWT Recommendation subfield contains a value that indicates
* recommendations on the types of frames that are transmitted by TWT
* scheduled STAs and scheduling AP during the broadcast TWT SP.
- * The allowed values are 0 - 3.
+ * The allowed values are 0 - 4.
* This parameter is used for
* 1. TWT SET Request
+ * 2. R-TWT SET Request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8)
* This attribute is used to configure Broadcast TWT Persistence.
@@ -10878,6 +12211,38 @@ enum qca_wlan_twt_setup_state {
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD: Attribute used for padding for 64-bit
* alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated can be
+ * suspended.
+ * Refers the enum qca_wlan_twt_session_suspendable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure downlink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure uplink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE: Optional (u8)
+ * This attribute indicates whether the parameters of the TWT session being
+ * negotiated (like wake interval, wake duration, etc.) can be updated after
+ * session setup.
+ * Refers the enum qca_wlan_twt_session_updatable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated is
+ * an implicit TWT, where the requesting STA calculates the start time of the
+ * next TWT service period, or an explicit TWT, where the responding STA
+ * calculates the start time of the next TWT service period.
+ * Refers the enum qca_wlan_twt_session_implicit.
+ * This parameter is used for
+ * 1. TWT SET Response
*/
enum qca_wlan_vendor_attr_twt_setup {
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -10915,6 +12280,11 @@ enum qca_wlan_vendor_attr_twt_setup {
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD = 27,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE = 28,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP = 29,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP = 30,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE = 31,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT = 32,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
@@ -10962,7 +12332,9 @@ enum qca_wlan_vendor_attr_twt_setup {
* @QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS: FW rejected the TWT
* setup request due to channel switch in progress.
* @QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS: FW rejected the TWT setup
- * request due to scan in progress.
+ * request due to scan in progress. This is also used in TWT_TERMINATE
+ * notification from the driver to indicate TWT session termination is due to
+ * scan in progress.
* QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE: The driver requested to
* terminate an existing TWT session on power save exit request from userspace.
* Used on the TWT_TERMINATE notification from the driver/firmware.
@@ -10970,6 +12342,22 @@ enum qca_wlan_vendor_attr_twt_setup {
* required bit in its capabilities.
* @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED: The peer has cleared
* the TWT required bit(1->0) in its capabilities.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE: FW terminated
+ * the TWT session due to more than one MLO link becoming active. Used on the
+ * TWT_TERMINATE notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED: TWT session already in
+ * resumed state. Used on the TWT_RESUME notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED: Requested TWT operation is
+ * rejected by the peer. Used on the TWT_SET notification from the
+ * driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT: Requested TWT operation has timed out.
+ * Used on the TWT_SET, TWT_TERMINATE notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_CHAN_SWITCH_24GHZ: FW terminated the TWT
+ * session due to channel switch triggered to a 2.4 GHz channel. Used on the
+ * TWT_TERMINATE notification from the driver.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_MLO_LINK_INACTIVE: FW terminated the TWT session
+ * due to the link inactivation triggered on the TWT session established
+ * link. Used on the TWT_TERMINATE notification from the driver.
*/
enum qca_wlan_vendor_twt_status {
QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
@@ -10997,6 +12385,12 @@ enum qca_wlan_vendor_twt_status {
QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE = 22,
QCA_WLAN_VENDOR_TWT_STATUS_TWT_REQUIRED = 23,
QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED = 24,
+ QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE = 25,
+ QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED = 26,
+ QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED = 27,
+ QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT = 28,
+ QCA_WLAN_VENDOR_TWT_STATUS_CHAN_SWITCH_24GHZ = 29,
+ QCA_WLAN_VENDOR_TWT_STATUS_MLO_LINK_INACTIVE = 30,
};
/**
@@ -11070,7 +12464,7 @@ enum qca_wlan_vendor_attr_twt_resume {
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE: Required (u32)
* This attribute represents the next TWT subfield size.
* Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
- * and 4 for 64 bits.
+ * and 3 for 64 bits.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer to which TWT Suspend and Resume is
@@ -11179,6 +12573,16 @@ enum qca_wlan_vendor_attr_twt_nudge {
* Status of the TWT GET STATISTICS request.
* This contains status values in enum qca_wlan_vendor_twt_status
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US: Optional (u32)
+ * Average of duration of the early terminated TWT service periods
+ * in micro seconds.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT: Optional (u32)
+ * Number of early terminated TWT service periods observed over
+ * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*/
enum qca_wlan_vendor_attr_twt_stats {
QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0,
@@ -11194,6 +12598,8 @@ enum qca_wlan_vendor_attr_twt_stats {
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12,
+ QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US = 13,
+ QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST,
@@ -11251,12 +12657,28 @@ enum qca_wlan_twt_capa {
* @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16).
* Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in
* enum qca_wlan_twt_capa.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL: (u32).
+ * Minimum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL: (u32).
+ * Maximum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION: (u32).
+ * Minimum tolerance limit of wake duration parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION: (u32).
+ * Maximum tolerance limit of wake duration parameter in microseconds.
*/
enum qca_wlan_vendor_attr_twt_capability {
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3,
+ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL = 4,
+ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL = 5,
+ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION = 6,
+ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION = 7,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST,
@@ -11325,11 +12747,58 @@ enum qca_wlan_vendor_twt_setup_resp_type {
* which the STA may accept.
* @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
* alternate parameters than the requested ones.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING: TWT responding STA suggests TWT
+ * group parameters that are different from the suggested or demanded TWT
+ * parameters of the TWT requesting STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT: TWT responding STA or TWT scheduling
+ * AP accepts the TWT request with the TWT parameters indicated in the TWT
+ * element transmitted by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT: TWT responding STA or TWT
+ * scheduling AP suggests TWT parameters that are different from those suggested
+ * by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT: TWT responding STA or TWT scheduling
+ * dictates TWT parameters that are different from those suggested by the
+ * TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT: A TWT responding STA or TWT scheduling
+ * AP rejects setup or terminates an existing broadcast TWT, or a TWT scheduled
+ * STA terminates its membership in a broadcast TWT.
*/
enum qca_wlan_vendor_twt_setup_req_type {
QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
+ QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING = 4,
+ QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT = 5,
+ QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT = 6,
+ QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT = 7,
+ QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT = 8,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_early_termination_ind: Represents attributes
+ * for TWT early SP termination operation. Attributes are sent as part of
+ * %QCA_WLAN_TWT_EARLY_TERMINATION_IND operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_ID: Required (u8)
+ * Flow ID is the unique identifier for the TWT session.
+ * Flow ID values from 0 to 254 represent a single TWT session.
+ * Flow ID value of 255 represents all TWT sessions.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_PEER_MAC_ADDR: Optional, 6-byte
+ * MAC address.
+ * Represents the MAC address of the peer for which the TWT Flow ID is
+ * provided. This is a mandatory attribute in AP mode to represent the
+ * respective client.
+ */
+enum qca_wlan_vendor_attr_twt_early_termination_ind {
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_PEER_MAC_ADDR = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_AFTER_LAST - 1,
};
/**
@@ -11442,13 +12911,100 @@ enum qca_wlan_vendor_cfr_capture_type {
};
/**
+ * enum qca_wlan_vendor_cfr_ltf_type - CSI/LTF type for CFR events.
+ *
+ * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_HT: CSI from HT-LTF (IEEE 802.11n)
+ * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_VHT: CSI from VHT-LTF (IEEE 802.11ac)
+ * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_HE: CSI from HE-LTF (IEEE 802.11ax)
+ * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_EHT: CSI from EHT-LTF (IEEE 802.11be)
+ * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_LEGACY: CSI from Legacy (IEEE 802.11a/g)
+ */
+enum qca_wlan_vendor_cfr_ltf_type {
+ QCA_WLAN_VENDOR_CFR_LTF_TYPE_HT = 0,
+ QCA_WLAN_VENDOR_CFR_LTF_TYPE_VHT = 1,
+ QCA_WLAN_VENDOR_CFR_LTF_TYPE_HE = 2,
+ QCA_WLAN_VENDOR_CFR_LTF_TYPE_EHT = 3,
+ QCA_WLAN_VENDOR_CFR_LTF_TYPE_LEGACY = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_chip_id - WLAN Chip identifier code.
+ */
+enum qca_wlan_vendor_chip_id {
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3680B = 0,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3910 = 1,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3950 = 2,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3988 = 3,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3991 = 4,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN3998 = 5,
+ QCA_WLAN_VENDOR_CHIP_ID_QCA639x = 6,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN685x = 7,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN6750 = 8,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN785x = 9,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN7750 = 10,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN7950 = 11,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN7880 = 12,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN7881 = 13,
+ QCA_WLAN_VENDOR_CHIP_ID_WCN8850 = 14,
+};
+
+/**
+ * qca_wlan_vendor_cfr_stop_reason - Reason codes for CFR stop indication used
+ * by attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_UNSPEC: Unspecified or unknown reason.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_COMPLETED: CFR collection completed
+ * successfully as planned.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_USER_ABORTED: CFR collection stopped
+ * explicitly upon userspace abort/stop request.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_PEER_UNAVAILABLE: Peer disconnected or
+ * unavailable.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_CONCURRENCY: Stopped to accommodate a
+ * higher-priority concurrency operation.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_ROAMING: Stopped due to roaming activity.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_FW_ERROR: Stopped because of a firmware or
+ * internal error.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_CHANNEL_SWITCHED: Channel changed (CSA).
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_SWITCHED: Stopped due to MLO link
+ * switch.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_RECONFIG: Stopped due to MLO link
+ * reconfiguration.
+ *
+ * @QCA_WLAN_VENDOR_CFR_STOP_REASON_RECOVERY: Stopped as part of driver
+ * recovery, restart, or assert handling.
+ */
+enum qca_wlan_vendor_cfr_stop_reason {
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_UNSPEC = 0,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_COMPLETED = 1,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_USER_ABORTED = 2,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_PEER_UNAVAILABLE = 3,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_CONCURRENCY = 4,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_ROAMING = 5,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_FW_ERROR = 6,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_CHANNEL_SWITCHED = 7,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_SWITCHED = 8,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_RECONFIG = 9,
+ QCA_WLAN_VENDOR_CFR_STOP_REASON_RECOVERY = 10,
+};
+
+/**
* enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer
* Channel Frequency Response capture parameters and enable periodic CFR
* capture.
*
* @QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR: Optional (6-byte MAC address)
- * MAC address of peer. This is for CFR version 1 only.
+ * MAC address of peer. This is for CFR version 1 and in peer CFR event for
+ * version 3.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE: Required (flag)
* Enable peer CFR capture. This attribute is mandatory to enable peer CFR
@@ -11456,24 +13012,26 @@ enum qca_wlan_vendor_cfr_capture_type {
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH: Optional (u8)
* BW of measurement, attribute uses the values in enum nl80211_chan_width
- * Supported values: 20, 40, 80, 80+80, 160.
+ * Supported bandwidth: 20, 40, 80, 80+80, 160, 320.
* Note that all targets may not support all bandwidths.
- * This attribute is mandatory for version 1 if attribute
+ * This attribute is mandatory for version 1 and version 3 if attribute
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ * This attribute is also applicable for peer CFR event with CFR data format
+ * version 3
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY: Optional (u32)
* Periodicity of CFR measurement in milliseconds.
* Periodicity should be a multiple of Base timer.
* Current Base timer value supported is 10 milliseconds (default).
* 0 for one shot capture.
- * This attribute is mandatory for version 1 if attribute
- * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ * This attribute is mandatory for version 1 and optional for version 3 if
+ * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD: Optional (u8)
* Method used to capture Channel Frequency Response.
* Attribute uses the values defined in enum qca_wlan_vendor_cfr_method.
- * This attribute is mandatory for version 1 if attribute
- * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ * This attribute is mandatory for version 1 and optional for version 3 if
+ * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
*
* @QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE: Optional (flag)
* Enable periodic CFR capture.
@@ -11481,9 +13039,9 @@ enum qca_wlan_vendor_cfr_capture_type {
* If this attribute is not present, periodic CFR capture is disabled.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION: Optional (u8)
- * Value is 1 or 2 since there are two versions of CFR capture. Two versions
- * can't be enabled at same time. This attribute is mandatory if target
- * support both versions and use one of them.
+ * Value is 1, 2, or 3 since there are three versions of CFR capture.
+ * Only one version can be enabled at a time. This attribute is mandatory
+ * if the target supports multiple versions and uses one of the versions.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP: Optional (u32)
* This attribute is mandatory for version 2 if
@@ -11538,7 +13096,7 @@ enum qca_wlan_vendor_cfr_capture_type {
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA: Optional (6-byte MAC address)
* Transmitter address which is used to filter frames. This MAC address takes
* effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK. This is for CFR
- * version 2 only.
+ * version 2 and version 3.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA: Optional (6-byte MAC address)
* Receiver address which is used to filter frames. This MAC address takes
@@ -11547,7 +13105,7 @@ enum qca_wlan_vendor_cfr_capture_type {
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK: Optional (6-byte MAC address)
* Mask of transmitter address which is used to filter frames. This is for CFR
- * version 2 only.
+ * version 2 and version 3.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK: Optional (6-byte MAC address)
* Mask of receiver address which is used to filter frames. This is for CFR
@@ -11555,8 +13113,9 @@ enum qca_wlan_vendor_cfr_capture_type {
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS: Optional (u32)
* Indicates frames with a specific NSS will be filtered for CFR capture.
- * This is for CFR version 2 only. This is a bitmask. Bits 7:0 request CFR
- * capture to be done for frames matching the NSS specified within this bitmask.
+ * This is for CFR version 2 and version 3. This is a bitmask. Bits 7:0 request
+ * CFR capture to be done for frames matching the NSS specified within this
+ * bitmask.
* Bits 31:8 are reserved for future use. Bits 7:0 map to NSS:
* bit 0 : NSS 1
* bit 1 : NSS 2
@@ -11565,11 +13124,10 @@ enum qca_wlan_vendor_cfr_capture_type {
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW: Optional (u32)
* Indicates frames with a specific bandwidth will be filtered for CFR capture.
- * This is for CFR version 2 only. This is a bitmask. Bits 4:0 request CFR
- * capture to be done for frames matching the bandwidths specified within this
- * bitmask. Bits 31:5 are reserved for future use. Bits 4:0 map to bandwidth
- * numerated in enum nl80211_band (although not all bands may be supported
- * by a given device).
+ * This is for CFR version 2 only. This is a bitmask where each bit N
+ * corresponds to BIT(N), with N being a value from enum nl80211_chan_width.
+ * Setting a bit requests CFR capture for frames matching that bandwidth
+ * (although not all bandwidths may be supported by a given device).
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER: Optional (u32)
* Management frames matching the subtype filter categories will be filtered in
@@ -11613,6 +13171,132 @@ enum qca_wlan_vendor_cfr_capture_type {
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD: Attribute used for padding for 64-bit
* alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREQ: Optional (u32)
+ * Frequency (in MHz) used for CFR capture.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_TYPE: Optional (u8)
+ * IEEE 802.11 WLAN frame type configuration.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SUBTYPE: Optional (u8)
+ * IEEE 802.11 WLAN frame subtype configuration.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL: Optional (u32)
+ * Interval (in milliseconds) at which CSI reports are generated
+ * and delivered to the user.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_OUI: Optional (u8 array, 3 or
+ * 5 bytes) Organizationally Unique Identifier (OUI) for the CFR data format.
+ * The OUI is assigned by IEEE and uniquely identifies the vendor or
+ * organization.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_VERSION: Optional (u8)
+ * Version of the CFR data format associated with the specified OUI.
+ * If not set, version 1 is used by default.
+ * OUI and version together define the vendor-specific format to interpret CFR
+ * data.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_TIMESTAMP_US: Optional (u64)
+ * Timestamp (in microseconds) indicating when the packet was received,
+ * based on the receiver's internal clock. This value represents the local
+ * timing reference for the captured frame.
+ * Applicable for peer CFR event and CFR data format version 3 only.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INFO: Optional (nested)
+ * Nested attribute containing one or more antenna entries. Each entry is a
+ * nested attribute that includes:
+ * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX,
+ * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI,
+ * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC,
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX: Optional (u8)
+ * Index of the receiving antenna corresponding to each entry.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI: Optional (s8)
+ * RSSI value (in dBm) measured on the antenna specified by
+ * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC: Optional (u8)
+ * AGC (Automatic Gain Control) value in dB for the antenna
+ * specified by %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_IS_LAST_REPORT: Optional (flag)
+ * Indicates that this event is the last entry in the current CSI reporting
+ * period. This flag is used when %QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL
+ * is configured to help the receiver determine the end of a CSI report batch.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SEQUENCE_NUMBER: Optional (u16)
+ * Sequence number of the IEEE 802.11 frame (without the fragment number) that
+ * triggered the CSI capture.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CHIP_ID: Optional (u16)
+ * Vendor-specific chip identifier of the reporting device. Values are defined
+ * in enum qca_wlan_vendor_chip_id.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TSF: Optional (u64)
+ * TSF (Timing Synchronization Function) timestamp in microseconds.
+ * This value is derived at the receiver of the frame by aligning with
+ * the TSF provided by the AP through periodic Beacon or Probe Response frames.
+ * Applicable for peer CFR events using CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CFO: Optional (s16)
+ * Carrier Frequency Offset (in 0.01 ppm) indicating frequency drift between the
+ * transmitter and receiver for the captured frame.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_LTF_TYPE: Optional (u8)
+ * CSI LTF type for the CFR event. Values are defined by
+ * enum qca_wlan_vendor_cfr_ltf_type.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_NUM_SPATIAL_STREAMS: Optional (u8)
+ * Number of spatial streams used to capture the CFR data.
+ * Applicable for peer CFR event with CFR data format version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON: Optional (u32)
+ * This attribute signifies that CFR collection for a peer has been stopped
+ * and provides the corresponding reason code. The reason codes are defined
+ * in enum qca_wlan_vendor_cfr_stop_reason.
+ * Applicable for peer CFR events when CFR data format version is 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FIXED_AGC: Optional (flag)
+ * This attribute indicates that the Wi-Fi firmware should fix RX antenna gain
+ * during CSI capturing. This is for CFR version 2 and version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_ONLY_LAST_FRAME: Optional (flag)
+ * Report only the last captured frame per MAC address in each reporting
+ * interval configured by %QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL,
+ * i.e., one report per interval per MAC address. When this flag is not
+ * included, all captured frames in the reporting interval are reported.
+ * Applicable only for CFR version 3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_TOTAL_LEN: Optional (u32)
+ * Total length (in bytes) of the full CFR data being delivered across
+ * multiple fragmented vendor events. Present in every fragment when
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA is bifurcated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_OFFSET: Optional (u32)
+ * Byte offset of the QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA payload
+ * within the full CFR data buffer. Present in every fragment when
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA is bifurcated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_IS_LAST_FRAG: Optional (flag)
+ * When present, indicates that this vendor event carries the final fragment
+ * of a bifurcated QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA payload.
+ *
*/
enum qca_wlan_vendor_peer_cfr_capture_attr {
QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
@@ -11645,6 +13329,30 @@ enum qca_wlan_vendor_peer_cfr_capture_attr {
QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID = 27,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA = 28,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD = 29,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREQ = 30,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_TYPE = 31,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SUBTYPE = 32,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL = 33,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_OUI = 34,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_VERSION = 35,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_TIMESTAMP_US = 36,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INFO = 37,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX = 38,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI = 39,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC = 40,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_IS_LAST_REPORT = 41,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SEQUENCE_NUMBER = 42,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CHIP_ID = 43,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TSF = 44,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CFO = 45,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_LTF_TYPE = 46,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_NUM_SPATIAL_STREAMS = 47,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON = 48,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_FIXED_AGC = 49,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_ONLY_LAST_FRAME = 50,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_TOTAL_LEN = 51,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_OFFSET = 52,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_IS_LAST_FRAG = 53,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
@@ -12411,6 +14119,12 @@ enum qca_wlan_vendor_attr_add_sta_node_params {
*/
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_IS_ML = 3,
+ /*
+ * This is u8 attribute used to identify a specific link affiliated
+ * to an AP MLD.
+ */
+ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_LINK_ID = 4,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX =
@@ -12421,18 +14135,22 @@ enum qca_wlan_vendor_attr_add_sta_node_params {
* enum qca_btc_chain_mode - Specifies BT coex chain mode.
* This enum defines the valid set of values of BT coex chain mode.
* These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of
- * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
+ * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE or
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS.
*
* @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4 GHz are shared.
* @QCA_BTC_CHAIN_SEPARATED_HYBRID: chains of BT and WLAN 2.4 GHz are
* separated, hybrid mode.
* @QCA_BTC_CHAIN_SEPARATED_FDD: chains of BT and WLAN 2.4 GHz are
* separated, fixed FDD mode.
+ * @QCA_BTC_CHAIN_DBAM: DBAM (Dedicated BT Antenna Mode) for BT/WLAN
+ * coexistence.
*/
enum qca_btc_chain_mode {
QCA_BTC_CHAIN_SHARED = 0,
QCA_BTC_CHAIN_SEPARATED_HYBRID = 1,
QCA_BTC_CHAIN_SEPARATED_FDD = 2,
+ QCA_BTC_CHAIN_DBAM = 3,
};
/* deprecated legacy name */
@@ -12463,6 +14181,99 @@ enum qca_vendor_attr_btc_chain_mode {
};
/**
+ * enum qca_btc_policy - Specifies Bluetooth coexistence (BTC) policy.
+ * This enum defines the valid set of BTC policy values.
+ * These values are used by attribute %QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY.
+ *
+ * @QCA_BTC_POLICY_FREE_FRUN: "Free run" mode for BTC. WLAN operates without
+ * strict BTC constraints.
+ *
+ * @QCA_BTC_POLICY_OCS: "OCS" (Off Channel Scheme) mode for BTC. Specific
+ * coexistence mechanisms are enforced to manage interference between Bluetooth
+ * and WLAN.
+ *
+ * @QCA_BTC_POLICY_NO_COEX: "No Coex" mode. No active coexistence conflict
+ * between WLAN and Bluetooth (e.g., when operating in different bands).
+ *
+ * @QCA_BTC_POLICY_INVALID: Invalid policy value. Used for error handling or
+ * uninitialized state.
+ */
+enum qca_btc_policy {
+ QCA_BTC_POLICY_FREE_FRUN = 0,
+ QCA_BTC_POLICY_OCS = 1,
+ QCA_BTC_POLICY_NO_COEX = 2,
+ QCA_BTC_POLICY_INVALID = 255
+};
+
+/**
+ * enum qca_coex_policy_type: Specifies coexistence policy for non-BT
+ * technologies like MWS and UWB.
+ *
+ * @QCA_COEX_POLICY_NO_COEX: No active coexistence conflict between WLAN and
+ * other technologies (e.g., when operating in different bands).
+ *
+ * @QCA_COEX_POLICY_COEX: Coexistence mechanisms are enforced to manage
+ * interference between WLAN and other technologies (e.g., UWB, MWS).
+ *
+ * @QCA_COEX_POLICY_INVALID: Invalid policy value. Used for error handling.
+ */
+enum qca_coex_policy_type {
+ QCA_COEX_POLICY_NO_COEX = 0,
+ QCA_COEX_POLICY_COEX = 1,
+ QCA_COEX_POLICY_INVALID = 255
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coex_stats - Coexistence statistics attributes
+ * @QCA_WLAN_VENDOR_ATTR_COEX_STATS_ARRAY_INDEX: Nested attribute containing
+ * the following attributes:
+ * %QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID
+ * %QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY
+ * %QCA_VENDOR_ATTR_BTC_CHAIN_MODE
+ * %QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY
+ * %QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY
+ * %QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD
+ * %QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT
+ * %QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT
+ * @QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID: 8-bit unsigned value to indicate PDEV ID.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY: 8-bit unsigned value to current BTC
+ * policy. The policies are defined in enum qca_btc_policy.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY: 8-bit unsigned value to current MWS
+ * (Modem Wireless System) policy. MWS refers to modem coexistence where modem
+ * and WLAN may share frequency bands. The policies are defined in
+ * enum qca_coex_policy_type.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY: 8-bit unsigned value to current UWB
+ * (Ultra-Wideband) policy. UWB is a ranging technology that may conflict with
+ * WLAN if operating in the same band. The policies are defined in
+ * enum qca_coex_policy_type.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD: A 32-bit unsigned value
+ * specifying the monitoring interval in milliseconds. This defines the time
+ * window over which statistics are collected.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT: 8-bit unsigned value to
+ * indicate the percentage of OCS (Off Channel Scheduling) policy active period
+ * in %QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT: 8-bit unsigned value to
+ * indicate the percentage OCS policy active period given to non-WLAN
+ * technology.
+ */
+enum qca_wlan_vendor_attr_coex_stats {
+ QCA_WLAN_VENDOR_ATTR_COEX_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COEX_STATS_ARRAY_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID = 2,
+ QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY = 3,
+ QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY = 4,
+ QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY = 5,
+ QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD = 6,
+ QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT = 7,
+ QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COEX_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COEX_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_COEX_STATS_AFTER_LAST - 1,
+};
+
+/**
* enum qca_vendor_wlan_sta_flags - Station feature flags
* Bits will be set to 1 if the corresponding features are enabled.
* @QCA_VENDOR_WLAN_STA_FLAG_AMPDU: AMPDU is enabled for the station
@@ -12799,6 +14610,63 @@ enum qca_vendor_wlan_sta_guard_interval {
* used in STA mode. This represents the number of MSDU packets
* (unicast/multicast/broadcast) transmitted/received with each NSS value. See
* enum qca_wlan_vendor_attr_nss_pkt.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT: Array of nested attributes,
+ * used in STA mode. This represents the number of PPDUs
+ * (unicast/multicast/broadcast) transmitted/received with each MCS value. See
+ * enum qca_wlan_vendor_attr_mcs_pkt.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT: Array of nested attributes,
+ * used in STA mode. This represents the number of PPDUs
+ * (unicast/multicast/broadcast) transmitted/received on each bandwidth value.
+ * See enum qca_wlan_vendor_attr_bw_pkt.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT: Array of nested attributes
+ * representing the CCA statistics for the affiliated AP(s) in STA mode. This
+ * uses attributes defined in enum qca_wlan_vendor_attr_cca_stat.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT: Array of nested
+ * attributes representing the beacon miss data for the affiliated AP(s) in STA
+ * mode. This uses attributes defined in
+ * enum qca_wlan_vendor_attr_beacon_miss_stat.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS: Array of nested attributes and
+ * applicable only in STA mode. Contains per-link statistics for ML connection.
+ * Each link is identified by %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID: u8 attribute and applicable
+ * only in STA mode. MLO link ID for which the statistics are being reported.
+ * Used only for ML connections. This is mandatory attribute inside
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS and represents the MLO link ID
+ * of the nested attribute set.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_NSS: u8 attribute and applicable only
+ * in STA mode. This represents the current operating Tx NSS with the
+ * affiliated AP. For non-ML connection, this attribute will be reported
+ * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection,
+ * this attribute will be reported inside
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_NSS: u8 attribute and applicable only
+ * in STA mode. This represents the current operating Rx NSS with the
+ * affiliated AP. For non-ML connection, this attribute will be reported
+ * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection,
+ * this attribute will be reported inside
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_CHAINS: u8 attribute and applicable
+ * only in STA mode. This represents the current operating Tx chains with the
+ * affiliated AP. For non-ML connection, this attribute will be reported
+ * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection,
+ * this attribute will be reported inside
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_CHAINS: u8 attribute and applicable
+ * only in STA mode. This represents the current operating Rx chains with the
+ * affiliated AP. For non-ML connection, this attribute will be reported
+ * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection,
+ * this attribute will be reported inside
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -12857,6 +14725,16 @@ enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD = 53,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER = 54,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT = 55,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT = 56,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT = 57,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT = 58,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT = 59,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS = 60,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID = 61,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_NSS = 62,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_NSS = 63,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_CHAINS = 64,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_CHAINS = 65,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -12946,6 +14824,21 @@ enum qca_wlan_vendor_attr_update_sta_info {
* able to move to the channel mentioned by the AP in CSA.
*
* @QCA_DISCONNECT_REASON_USER_TRIGGERED: User triggered disconnection.
+ *
+ * @QCA_DISCONNECT_REASON_KEY_FAIL_TO_INSTALL: Disconnection triggered because
+ * the encryption keys couldn't be installed during the secure handshake
+ * process.
+ *
+ * @QCA_DISCONNECT_REASON_FW_TRIGGERED_LINK_SWITCH: Firmware initiated a switch
+ * from the active link to a standby link, resulting in disconnection from the
+ * current link.
+ *
+ * @QCA_DISCONNECT_REASON_HOST_TRIGGERED_LINK_DELETE: Host system dynamically
+ * removed a link, causing the associated connection to drop.
+ *
+ * @QCA_DISCONNECT_REASON_HOST_OCI_MISMATCH: Disconnect occurred due to a
+ * mismatch in Operating Channel Information between the AP and STA, violating
+ * channel coordination.
*/
enum qca_disconnect_reason_codes {
QCA_DISCONNECT_REASON_UNSPECIFIED = 0,
@@ -12965,6 +14858,10 @@ enum qca_disconnect_reason_codes {
QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE = 14,
QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE = 15,
QCA_DISCONNECT_REASON_USER_TRIGGERED = 16,
+ QCA_DISCONNECT_REASON_KEY_FAIL_TO_INSTALL = 17,
+ QCA_DISCONNECT_REASON_FW_TRIGGERED_LINK_SWITCH = 18,
+ QCA_DISCONNECT_REASON_HOST_TRIGGERED_LINK_DELETE = 19,
+ QCA_DISCONNECT_REASON_HOST_OCI_MISMATCH = 20,
};
/**
@@ -14357,6 +16254,22 @@ enum qca_wlan_vendor_monitor_ctrl_frame_type {
QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME = BIT(1),
};
+/*
+ * enum qca_wlan_vendor_monitor_operating_type: Attributes used by vendor
+ * attribute %QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC: Local packet capture.
+ * Capture frames sent and received by the current client interface from the
+ * BSS.
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC: Operating channel capture.
+ * Capture all frames on the current operating channel of client interface.
+ */
+enum qca_wlan_vendor_monitor_operating_type {
+ QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC = 0,
+ QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC = 1,
+};
+
/**
* enum qca_wlan_vendor_attr_set_monitor_mode - Used by the
* vendor command QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE to set the
@@ -14393,6 +16306,12 @@ enum qca_wlan_vendor_monitor_ctrl_frame_type {
* Represents the interval in milliseconds only for the connected Beacon frames,
* expecting the connected BSS's Beacon frames to be sent on the monitor
* interface at this specific interval.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE: u32 attribute.
+ * Represents the monitor operating type (u32). These operating types are
+ * defined in enum qca_wlan_vendor_monitor_operating_type.
+ * If this attribute is not included, default operating type LPC ("local
+ * packet capture") used.
*/
enum qca_wlan_vendor_attr_set_monitor_mode {
QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_INVALID = 0,
@@ -14403,6 +16322,7 @@ enum qca_wlan_vendor_attr_set_monitor_mode {
QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE = 5,
QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE = 6,
QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL = 7,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE = 8,
/* keep last */
QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST,
@@ -14632,6 +16552,105 @@ enum qca_wlan_vendor_attr_ratemask_params {
};
/**
+ * enum qca_wlan_rx_mcs_map_params_phy_type - RX MCS map config PHY type
+ *
+ * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_VHT: VHT PHY type
+ * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_HE: HE PHY type
+ * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_EHT: EHT PHY type
+ */
+enum qca_wlan_rx_mcs_map_params_phy_type {
+ QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_VHT = 0,
+ QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_HE = 1,
+ QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_EHT = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_rx_mcs_map_params - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LIST:
+ * Array of nested attributes containing
+ * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE and
+ * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP and optionally
+ * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE: u8, represents
+ * the PHY type to which RX MCS Map config is to be applied.
+ * The values for this attribute are referred from enum
+ * qca_wlan_rx_mcs_map_params_phy_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP: binary, RX MCS map bitmap.
+ *
+ * For EHT targets 3 bit correspond to one NSS setting.
+ * b0-2 => NSS1
+ * b3-5 => NSS2
+ * b6-8 => NSS3
+ * b9-11 => NSS4
+ * b12-14 => NSS5
+ * b15-17 => NSS6
+ * b18-20 => NSS7
+ * b21-23 => NSS8
+ *
+ * Below are the possible values
+ * 000 - Disabled/Not supported
+ * 001 - MCS 0-7
+ * 010 - MCS 0-9
+ * 011 - MCS 0-11
+ * 100 - MCS 0-13
+ * 110 - MCS 0-14
+ * 111 - MCS 0-15
+ *
+ * For HE targets, 2 bits correspond to one NSS setting
+ * b0-1 => NSS1
+ * b2-3 => NSS2
+ * b4-5 => NSS3
+ * b6-7 => NSS4
+ * b8-9 => NSS5
+ * b10-11 => NSS6
+ * b12-13 => NSS7
+ * b14-15 => NSS8
+ *
+ * Below are the possible values
+ * 00 - Disabled/Not supported
+ * 01 - MCS 0-7
+ * 10 - MCS 0-9
+ * 11 - MCS 0-11
+ *
+ * for VHT targets, 2 bits correspond to one NSS setting.
+ * b0-1 => NSS1
+ * b2-3 => NSS2
+ * b4-5 => NSS3
+ * b6-7 => NSS4
+ * b8-9 => NSS5
+ * b10-11 => NSS6
+ * b12-13 => NSS7
+ * b14-15 => NSS8
+ *
+ * Below are the possible values
+ * 00 - Disabled/Not supported
+ * 01 - MCS 0-7
+ * 10 - MCS 0-8
+ * 11 - MCS 0-9
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID: u8, used to specify the
+ * MLO link ID of a link to be configured. Optional attribute.
+ * No need of this attribute in non-MLO cases. If the attribute is
+ * not provided, MCS map will be applied for the association link.
+ */
+enum qca_wlan_vendor_attr_rx_mcs_map_params {
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LIST = 1,
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP = 3,
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_MAX =
+ QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_audio_data_path - Defines the data path to be used for audio
* traffic.
*
@@ -14693,6 +16712,56 @@ enum qca_wlan_vendor_pasn_action {
* If present, it indicates the successful PASN handshake with the peer. If
* this flag is not present, it indicates that the PASN handshake with the
* peer device failed.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM: Optional u32 attribute. It indicates the
+ * AKM suite that is preferred in the PASN handshake in the event from the
+ * driver to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace
+ * to the driver, it indicates the actual AKM suite used in the handshake.
+ * Userspace can select the AKM based on the AP's capabilities, if the
+ * given AKM suite is not applicable. Possible values are defined in
+ * IEEE Std 802.11-2020, 9.4.2.24.3 (AKM suites) (e.g., 0x000FAC04)
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER: Optional u32 attribute. It indicates
+ * the pairwise cipher suite that is preferred in the PASN handshake in
+ * the event from the driver to userspace when
+ * %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace
+ * to the driver, it indicates the actual cipher used in the handshake.
+ * Userspace can select the cipher suite based on the capabilities of the
+ * P, if the given cipher suite is not applicable. Possible values are
+ * defined in IEEE Std 802.11-2020, 9.4.2.24.2 (Cipher suites)
+ * (e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD: This is a variable length byte
+ * array attribute. This attribute is present if the AKM suite specified
+ * in %QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM requires a password. The
+ * password is used in PASN handshake request in an event from the driver
+ * to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID: This is a byte array attribute with a
+ * size of 16 bytes. When this attribute is present this PMKSA caching
+ * using the PMKSA identified by this PMKID is preferred to be used with
+ * PASN. This attribute is sent along with PASN handshake request in an
+ * event from the driver to userspace when
+ * %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER: u16 attribute in units for
+ * TUs (1024 microseconds). This attribute is sent from userspace along
+ * with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE to the
+ * driver in the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN
+ * subcommand when the AP request PASN to be retried later.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE: This is a variable length byte array
+ * attribute. In case an AP refused PASN temporarily, the STA can retry
+ * PASN handshake by attaching this attribute data to PASN request after
+ * the time period mentioned in the attribute
+ * %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER.
+ * In case the AP refused the PASN handshake temporarily, cookie data is
+ * received from the AP and it is sent from userspace to the driver along
+ * with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER in
+ * the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN subcommand.
+ * When the driver wants to retry PASN with the same AP after having
+ * received this information, this attribute must be sent along with PASN
+ * handshake request in an event from the driver to
+ * userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
*/
enum qca_wlan_vendor_attr_pasn_peer {
QCA_WLAN_VENDOR_ATTR_PASN_PEER_INVALID = 0,
@@ -14700,6 +16769,12 @@ enum qca_wlan_vendor_attr_pasn_peer {
QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR = 2,
QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED = 3,
QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS = 4,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM = 5,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER = 6,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD = 7,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID = 8,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER = 9,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE = 10,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PASN_PEER_AFTER_LAST,
@@ -15510,10 +17585,14 @@ enum qca_wlan_vendor_attr_sr_stats {
*
* @QCA_WLAN_SR_REASON_CODE_CONCURRENCY: The SR feature is disabled/enabled due
* to change in concurrent interfaces that are supported by the driver.
+ *
+ * @QCA_WLAN_SR_REASON_CODE_BCN_IE_CHANGE: The SR feature is disabled/enabled
+ * on non-AP STA, due to BSS SR parameter(s) update.
*/
enum qca_wlan_sr_reason_code {
QCA_WLAN_SR_REASON_CODE_ROAMING = 0,
QCA_WLAN_SR_REASON_CODE_CONCURRENCY = 1,
+ QCA_WLAN_SR_REASON_CODE_BCN_IE_CHANGE = 2,
};
/**
@@ -15693,12 +17772,37 @@ enum qca_wlan_vendor_attr_sr_params {
* This attribute is used in response from the driver to a command in which
* %QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to
* %QCA_WLAN_SR_OPERATION_GET_STATS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS: Array of nested links each identified
+ * by %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID.
+ * This attribute enables user to configure a single or multiple links by
+ * nesting %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID and corresponding SR
+ * configuration attributes defined in enum qca_wlan_vendor_attr_sr.
+ * In %QCA_WLAN_SR_OPERATION_GET_PARAMS or %QCA_WLAN_SR_OPERATION_GET_STATS
+ * in enum qca_wlan_sr_operation, %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID shall
+ * be used to pack the per link configuration that are currently in use.
+ * For STA interface, this attribute is applicable only in connected state
+ * when the current connection is MLO capable. The valid values of
+ * %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID are the link IDs of the connected AP
+ * MLD links.
+ * For AP interface, this attribute is applicable only after adding MLO
+ * links to the AP interface with %NL80211_CMD_ADD_LINK and the valid values
+ * of %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID are the link IDs specified in
+ * %NL80211_CMD_ADD_LINK while adding the MLO links to the AP interface.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID: 8-bit unsigned value. Used to specify
+ * the MLO link ID of a link that is being configured. This attribute must be
+ * included in each record nested under %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS and
+ * may be included without nesting to indicate the target link for
+ * configuration attributes.
*/
enum qca_wlan_vendor_attr_sr {
QCA_WLAN_VENDOR_ATTR_SR_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_SR_OPERATION = 1,
QCA_WLAN_VENDOR_ATTR_SR_PARAMS = 2,
QCA_WLAN_VENDOR_ATTR_SR_STATS = 3,
+ QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS = 4,
+ QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID = 5,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST,
@@ -16736,12 +18840,17 @@ enum qca_wlan_vendor_attr_tdls_state {
* to configure power save inactivity timeout and speculative wake up
* interval through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
* %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes.
+ * @QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED: Enable optimized power-save mode
+ * based on interface level latency tolerance value which is configured
+ * through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE and
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL.
*/
enum qca_wlan_vendor_opm_mode {
QCA_WLAN_VENDOR_OPM_MODE_DISABLE = 0,
QCA_WLAN_VENDOR_OPM_MODE_ENABLE = 1,
QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED = 2,
+ QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED = 3,
};
/*
@@ -17126,6 +19235,30 @@ enum qca_wlan_vendor_attr_tpc_links {
};
/**
+ * enum qca_wlan_vendor_attr_emlsr_info: Represent attributes to configure
+ * the EHT MLO links for EHT EMLSR operation and the EMLSR operation in STA
+ * mode. These attributes are used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_EMLSR_LINKS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EMLSR_OPERATION: Required attribute, u8.
+ * 0 - Enter, 1 - Exit
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EMLSR_LINKS_BITMAP: Required, u16 attribute. This
+ * indicates the bitmap of the link IDs to specify the links corresponding
+ * to the bit set to be used for the EHT EMLSR operation.
+ */
+enum qca_wlan_vendor_attr_emlsr_info {
+ QCA_WLAN_VENDOR_ATTR_EMLSR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_EMLSR_OPERATION = 1,
+ QCA_WLAN_VENDOR_ATTR_EMLSR_LINKS_BITMAP = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_EMLSR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_EMLSR_MAX =
+ QCA_WLAN_VENDOR_ATTR_EMLSR_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_fw_page_fault_report - Used by the vendor
* command %QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT.
*
@@ -17408,6 +19541,9 @@ enum qca_wlan_vendor_attr_ap_suspend {
* @QCA_TRAFFIC_TYPE_SCREEN_SHARE: Traffic type is screen share
* @QCA_TRAFFIC_TYPE_UNKNOWN: Traffic type is unknown
* @QCA_TRAFFIC_TYPE_INVALID: Invalid traffic type
+ * @QCA_TRAFFIC_TYPE_BROWSING: Traffic type is browsing website
+ * @QCA_TRAFFIC_TYPE_APERIODIC_BURSTS: Traffic type is aperiodic bursts
+ * @QCA_TRAFFIC_TYPE_LIVESTREAM: Traffic type is livestream
*/
enum qca_traffic_type {
QCA_TRAFFIC_TYPE_STREAMING = 0,
@@ -17417,6 +19553,9 @@ enum qca_traffic_type {
QCA_TRAFFIC_TYPE_SCREEN_SHARE = 4,
QCA_TRAFFIC_TYPE_UNKNOWN = 5,
QCA_TRAFFIC_TYPE_INVALID = 6,
+ QCA_TRAFFIC_TYPE_BROWSING = 7,
+ QCA_TRAFFIC_TYPE_APERIODIC_BURSTS = 8,
+ QCA_TRAFFIC_TYPE_LIVESTREAM = 9,
};
/**
@@ -17703,11 +19842,22 @@ enum qca_wlan_vendor_attr_flow_stats {
* @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE: Mandatory u8
* attribute indicates the traffic type learned for this flow tuple. Uses the
* enum qca_traffic_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_UL_TID: Optional u8 attribute
+ * indicates the TID value to be used by the driver in uplink direction for this
+ * flow tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS: Optional nested
+ * attribute array containing classify results insights. Each array element
+ * contains traffic type and probability information using attributes from
+ * enum qca_wlan_vendor_attr_classify_insights.
*/
enum qca_wlan_vendor_attr_flow_classify_result {
QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_FLOW_TUPLE = 1,
QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_UL_TID = 3,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST,
@@ -17716,6 +19866,29 @@ enum qca_wlan_vendor_attr_flow_classify_result {
};
/**
+ * enum qca_wlan_vendor_attr_classify_insights - Classify results insights
+ * used by @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS as nested
+ * attributes within each array element.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABLE_TRAFFIC_TYPE: Mandatory u8
+ * attribute indicates probable traffic type. Uses values from the
+ * enum qca_traffic_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABILITY: Mandatory u8 attribute
+ * indicates probability percentages for probable traffic type. Range: 0-100.
+ */
+enum qca_wlan_vendor_attr_classify_insights {
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABLE_TRAFFIC_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABILITY = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_MAX =
+ QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_AFTER_LAST - 1
+};
+
+/**
* enum qca_async_stats_sub_module - The statistics type used in async
* statistics policy.
* Used by @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE.
@@ -17802,6 +19975,67 @@ enum qca_wlan_vendor_attr_nss_pkt {
};
/**
+ * enum qca_wlan_vendor_attr_mcs_pkt - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX: u8 attribute. This
+ * represents the value of MCS index.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_TX_PACKET_COUNT: u64 attribute. This
+ * represents the number of PPDUs transmitted with the MCS index
+ * specified in %QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_RX_PACKET_COUNT: u64 attribute. This
+ * represents the number of PPDUs received with the MCS index
+ * specified in %QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_PAD: Attribute used for padding for 64-bit
+ * alignment.
+ */
+enum qca_wlan_vendor_attr_mcs_pkt {
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_TX_PACKET_COUNT = 2,
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_RX_PACKET_COUNT = 3,
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_PAD = 4,
+
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_MAX =
+ QCA_WLAN_VENDOR_ATTR_MCS_PKT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_bw_pkt - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_CHAN_WIDTH: u8 attribute. This
+ * represents the value of bandwidth. This attribute uses values defined in
+ * enum nl80211_chan_width.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BW_PKT_TX_PACKET_COUNT: u64 attribute. This
+ * represents the number of PPDUs transmitted with the bandwidth
+ * specified in %QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_VALUE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BW_PKT_RX_PACKET_COUNT: u64 attribute. This
+ * represents the number of PPDUs received with the bandwidth
+ * specified in %QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_VALUE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BW_PKT_PAD: Attribute used for padding for 64-bit
+ * alignment.
+ */
+enum qca_wlan_vendor_attr_bw_pkt {
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_CHAN_WIDTH = 1,
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_TX_PACKET_COUNT = 2,
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_RX_PACKET_COUNT = 3,
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_PAD = 4,
+
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_MAX =
+ QCA_WLAN_VENDOR_ATTR_BW_PKT_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_intf_offload_type - Definition of available values for
* QCA_WLAN_VENDOR_ATTR_CONFIG_INTF_OFFLOAD_TYPE to specify the offload path for
* packets handled through a network device.
@@ -17849,4 +20083,3861 @@ enum qca_wlan_intf_offload_type {
QCA_WLAN_INTF_OFFLOAD_TYPE_PPE_DS = 3,
};
+/**
+ * enum qca_wlan_vendor_attr_usd_op_type: Attribute values for
+ * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE to the vendor subcmd
+ * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a mandatory u8 attribute which
+ * represents the USD command type.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH: Indicates USD tear down of all active
+ * publish and subscribe sessions.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH: Indicates USD solicited publish
+ * operation that enables to offer a service for other devices based on
+ * given parameters.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE: Indicates USD active subscribe
+ * operation that requests for a given service with given parameters from
+ * other devices that offer the service.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH: Indicates update of an instance
+ * of the publish function of given publish id.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH: Indicates cancellation of an
+ * instance of the publish function.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE: Indicates cancellation of an
+ * instance of the subscribe function.
+ */
+enum qca_wlan_vendor_attr_an_usd_op_type {
+ QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH = 0,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH = 1,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE = 2,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH = 3,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH = 4,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE = 5,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_service_protocol_type: Attribute values for
+ * %QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE to the vendor subcmd
+ * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a u8 attribute which represents the
+ * USD service protocol type for service specific information.
+ *
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR: Indicates SSI info is
+ * of type Bonjour
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC: Indicates SSI info is
+ * of type generic
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER: Indicates SSI info
+ * is of type CSA/Matter
+ */
+enum qca_wlan_vendor_attr_usd_service_protocol_type {
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR = 1,
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC = 2,
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_chan_config - Attributes used inside nested
+ * attribute %QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ: Required
+ * u32 attribute containing the default channel frequency (MHz).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST: Optional array of channel
+ * frequencies in MHz (u32) to publish or subscribe.
+ */
+enum qca_wlan_vendor_attr_usd_chan_config {
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ = 1,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST = 2,
+
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_status
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS: USD request success status.
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED: USD request failed status.
+ */
+enum qca_wlan_vendor_attr_usd_status {
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS,
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED,
+};
+
+/* enum qca_wlan_vendor_attr_usd: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_USD.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR: 6-byte source MAC address
+ * Mandatory attribute used with type
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE: Required u8 attribute.
+ * It indicates the type of the USD command. It uses values defined in enum
+ * qca_wlan_vendor_attr_usd_op_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID: Required u8 attribute.
+ * It contains the publisher/subscribe id that is specific to the
+ * publish/subscribe instance.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID: Required 6-byte attribute.
+ * It contains the service id that is specific to the service being
+ * published/subscribed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE: u8 attribute that indicates
+ * the service protocol type of service specific info. It uses values
+ * defined in enum qca_wlan_vendor_attr_usd_service_protocol_type. It is
+ * applicable when %QCA_WLAN_VENDOR_ATTR_USD_SSI is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SSI: u8 array containing service specific
+ * information that has to be conveyed in publish/subscribe message.
+ * Optional attribute used with type
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH,
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE, and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG: Nested attribute containing USD
+ * channel configuration parameters.
+ * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ * See enum qca_wlan_vendor_attr_usd_chan_config for nested attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER: u8 array containing a USD
+ * element container buffer that has to be conveyed in publish/subscribe
+ * message.
+ * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_TTL: u16 attribute. Indicates the timeout
+ * for each request in seconds. Timeout value 0 represents single time
+ * operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS: u8 attribute. Status received in event
+ * indicating whether the underlying driver/firmware has started the USD
+ * operation as indicated by attributes
+ * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE and
+ * %QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID.
+ * enum qca_wlan_vendor_attr_usd_status indicates status values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID: 6-byte MAC address that has to be
+ * used in publish/subscribe multicast frames. It is an optional attribute
+ * used with the types %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ */
+enum qca_wlan_vendor_attr_usd {
+ QCA_WLAN_VENDOR_ATTR_USD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID = 4,
+ QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE = 5,
+ QCA_WLAN_VENDOR_ATTR_USD_SSI = 6,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG = 7,
+ QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER = 8,
+ QCA_WLAN_VENDOR_ATTR_USD_TTL = 9,
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS = 10,
+ QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID = 11,
+
+ QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_USD_MAX =
+ QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_audio_transport_switch_type - Represents the possible transport
+ * switch types.
+ *
+ * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_NON_WLAN: Request to route audio data
+ * via non-WLAN transport (e.g., Bluetooth).
+ *
+ * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_WLAN: Request to route audio data via
+ * WLAN transport.
+ */
+enum qca_wlan_audio_transport_switch_type {
+ QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_NON_WLAN = 0,
+ QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_WLAN = 1,
+};
+
+/**
+ * enum qca_wlan_audio_transport_switch_status - Represents the status of audio
+ * transport switch request.
+ *
+ * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_REJECTED: Request to switch transport
+ * has been rejected. For example, when transport switch is requested from WLAN
+ * to non-WLAN transport, user space modules and peers would evaluate the switch
+ * request and may not be ready for switch and hence switch to non-WLAN
+ * transport gets rejected.
+ *
+ * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_COMPLETED: Request to switch
+ * transport has been completed. This is sent only in the command path. For
+ * example, when the driver had requested for audio transport switch and
+ * userspace modules as well as peers are ready for the switch, userspace module
+ * switches the transport and sends the subcommand with status completed to the
+ * driver.
+ */
+enum qca_wlan_audio_transport_switch_status {
+ QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_REJECTED = 0,
+ QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_COMPLETED = 1,
+};
+
+/**
+ * enum qca_wlan_audio_transport_switch_reason - Represents the reason of audio
+ * transport switch request.
+ *
+ * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_REASON_TERMINATING: Requester transport is
+ * terminating. After this indication, requester module may not be available to
+ * process further request on its transport. For example, to handle a high
+ * priority concurrent interface, WLAN transport needs to terminate and hence
+ * indicates switch to a non-WLAN transport with reason terminating. User space
+ * modules switch to non-WLAN immediately without waiting for further
+ * confirmation.
+ */
+enum qca_wlan_audio_transport_switch_reason {
+ QCA_WLAN_AUDIO_TRANSPORT_SWITCH_REASON_TERMINATING = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_audio_transport_switch - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_TYPE: u8 attribute. Indicates
+ * the transport switch type from one of the values in enum
+ * qca_wlan_audio_transport_switch_type. This is mandatory attribute in both
+ * command and event path. This attribute is included in both requests and
+ * responses.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_STATUS: u8 attribute. Indicates
+ * the transport switch status from one of the values in enum
+ * qca_wlan_audio_transport_switch_status. This is optional attribute and used
+ * in both command and event path. This attribute must not be included in
+ * requests.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_REASON: u8 attribute. Indicates
+ * the transport switch reason from one of the values in enum
+ * qca_wlan_audio_transport_switch_reason. This is optional attribute and used
+ * in both command and event path.
+ */
+enum qca_wlan_vendor_attr_audio_transport_switch {
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_STATUS = 2,
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_REASON = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_MAX =
+ QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_AFTER_LAST - 1,
+};
+
+
+/**
+ * enum qca_wlan_connect_ext_features - Feature flags for
+ * %QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES
+ *
+ * @QCA_CONNECT_EXT_FEATURE_RSNO: Flag attribute. This indicates supplicant
+ * support for RSN overriding. The driver shall enable RSN overriding in the
+ * (re)association attempts only if this flag is indicated. This functionality
+ * is available only when the driver indicates support for
+ * @QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA.
+ *
+ * @QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE: Flag attribute. This indicates
+ * supplicant support for external authentication with EPPKE authentication
+ * algorithm. The driver can offload EPPKE authentication to supplicant via
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH only if this flag is enabled.
+ *
+ * @QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X: Flag attribute. This indicates
+ * supplicant support for external authentication with IEEE 802.1X
+ * Authentication algorithm (EAP over Authentication frames) as specified
+ * in IEEE P802.11bi/D4.0, 12.16.5. The driver can offload authentication to
+ * supplicant via QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH only if this flag is
+ * enabled.
+ *
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits.
+ */
+enum qca_wlan_connect_ext_features {
+ QCA_CONNECT_EXT_FEATURE_RSNO = 0,
+ QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE = 1,
+ QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X = 2,
+ NUM_QCA_CONNECT_EXT_FEATURES /* keep last */
+};
+
+/* enum qca_wlan_vendor_attr_connect_ext: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES: Feature flags contained in a byte
+ * array. The feature flags are identified by their bit index (see &enum
+ * qca_wlan_connect_ext_features) with the first byte being the least
+ * significant one and the last one being the most significant one.
+ * @QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS: Nested attribute of
+ * BSSIDs to indicate allowed BSSIDs for association. This configuration stays
+ * in effect only for the current connection request and for the next connect
+ * request if there is no connection currently.
+ */
+enum qca_wlan_vendor_attr_connect_ext {
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES = 1,
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS = 2,
+
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_p2p_mode - Defines the values used with
+ * %QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG.
+ *
+ * @QCA_P2P_MODE_WFD_R1: Wi-Fi Direct R1 only.
+ * @QCA_P2P_MODE_WFD_R2: Wi-Fi Direct R2 only.
+ * @QCA_P2P_MODE_WFD_PCC: P2P Connection Compatibility Mode which supports both
+ * Wi-Fi Direct R1 and R2.
+ */
+enum qca_wlan_vendor_p2p_mode {
+ QCA_P2P_MODE_WFD_R1 = 0,
+ QCA_P2P_MODE_WFD_R2 = 1,
+ QCA_P2P_MODE_WFD_PCC = 2,
+};
+
+/* enum qca_wlan_vendor_attr_set_p2p_mode: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG: u8 attribute. Sets the P2P device
+ * mode. The values used are defined in enum qca_wlan_vendor_p2p_mode.
+ * This configuration is valid until the interface is brought up next time after
+ * this configuration and the driver shall use this configuration only when the
+ * interface is brought up in NL80211_IFTYPE_P2P_GO mode.
+ * When this parameter has not been set, the interface is brought up with
+ * Wi-Fi Direct R1 only configuration by default.
+ */
+enum qca_wlan_vendor_attr_set_p2p_mode {
+ QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG = 1,
+
+ QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_MAX =
+ QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_chan_usage_req_chan_list: Attributes used inside
+ * nested attributes %QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_CHAN: u8 attribute. Indicates
+ * the channel number of the channel list entry.
+ * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_OP_CLASS: u8 attribute.
+ * Indicates the operating class of the channel list entry.
+ */
+enum qca_wlan_vendor_attr_chan_usage_req_chan_list {
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_CHAN = 1,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_OP_CLASS = 2,
+
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_MAX =
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_chan_usage_req_mode: Defines the values used
+ * with %QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE.
+ *
+ * @QCA_CHAN_USAGE_MODE_UNAVAILABILITY_INDICATION: Mode set by STA to indicate
+ * the AP about its unavailability during a peer-to-peer TWT agreement.
+ *
+ * @QCA_CHAN_USAGE_MODE_CHANNEL_SWITCH_REQ: Mode set by the STA that is in a
+ * channel-usage-aidable BSS to request a channel switch. Other Channel Usage
+ * elements are not required. Optional HT/VHT/HE Capabilities are present.
+ */
+enum qca_wlan_vendor_attr_chan_usage_req_mode {
+ QCA_CHAN_USAGE_MODE_UNAVAILABILITY_INDICATION = 3,
+ QCA_CHAN_USAGE_MODE_CHANNEL_SWITCH_REQ = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_chan_usage_req: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE: Required u8 attribute. Identifies
+ * the usage of the channel list entry provided in the channel usage request.
+ * Channel switch request and unavailability channel usage modes are
+ * configured on a STA/P2P Client.
+ * See enum qca_wlan_vendor_attr_chan_usage_req_mode for attribute values.
+ * See IEEE P802.11-REVme/D7.0, 9.4.2.84, Table 9-268 for more information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST: Required array of nested
+ * attributes containing channel usage parameters.
+ * Required when channel usage mode is Channel-usage-aidable BSS channel
+ * switch request.
+ * See enum qca_wlan_vendor_attr_req_chan_list for nested attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_UNAVAILABILITY_CONFIG_PARAMS: Nested
+ * attribute representing the parameters configured for unavailability
+ * indication. Required when channel usage mode is unavailability indication.
+ *
+ * Below attributes from enum qca_wlan_vendor_attr_twt_setup are used inside
+ * this nested attribute:
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION,
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA.
+ */
+enum qca_wlan_vendor_attr_chan_usage_req {
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE = 1,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST = 2,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_UNAVAILABILITY_CONFIG_PARAMS = 3,
+
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MAX =
+ QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_fw_scan_bss_flags - Flags for
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HT_OPS: This indicates HT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.56) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_VHT_OPS: This indicates VHT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.158) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HE_OPS: This indicates HE Operation element
+ * (IEEE Std 802.11ax-2021, 9.4.2.249) is present in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_EHT_OPS: This indicates EHT Operation element
+ * (IEEE Std 802.11be-2024, 9.4.2.321) is present in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER: This indicates Fine Timing Measurement
+ * Responder bit is set to 1 in the Extended Capabilities field of the Extended
+ * Capabilities element (IEEE Std 802.11-2020, 9.4.2.26) in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS: Number of assigned feature bits.
+ */
+enum qca_wlan_fw_scan_bss_flags {
+ QCA_WLAN_FW_SCAN_BSS_HT_OPS = 0,
+ QCA_WLAN_FW_SCAN_BSS_VHT_OPS = 1,
+ QCA_WLAN_FW_SCAN_BSS_HE_OPS = 2,
+ QCA_WLAN_FW_SCAN_BSS_EHT_OPS = 3,
+ QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER = 4,
+
+ NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS /* keep last */
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_bss: Attributes used inside
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO: Required (u32). Indicates how many
+ * milliseconds ago from %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP this BSS
+ * was last scanned (i.e., Beacon or Probe Response frame received).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID: Required (6-byte MAC address). BSSID
+ * of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID: Required (binary attribute,
+ * 0..32 octets). SSID of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI: Required (s8). RSSI of the last
+ * received Beacon or Probe Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY: Required (CPU byte order, u16).
+ * The Capability Information field from the last received Beacon or Probe
+ * Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS: BSS capability flags contained in a
+ * byte array. The flags are identified by their bit index (see &enum
+ * qca_wlan_fw_scan_bss_flags) with the first byte being the least significant
+ * one and the last one being the most significant one. This information will be
+ * populated from the last received Beacon or Probe Response frame. This is a
+ * mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ: Required (u32). Indicates
+ * primary 20 MHz channel center frequency in MHz of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH: Required (u8). Indicates
+ * channel width of the BSS. This uses values defined in
+ * enum nl80211_chan_width.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1: Required (u32). Indicates the
+ * center frequency (MHz) of the first segment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2: Optional (u32). Indicates the
+ * center frequency (MHz) of the second segment. Used only for
+ * %NL80211_CHAN_WIDTH_80P80 value in
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH.
+ */
+enum qca_wlan_vendor_attr_fw_scan_bss {
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO = 1,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID = 2,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID = 3,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI = 4,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY = 5,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS = 6,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ = 7,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH = 8,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1 = 9,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2 = 10,
+
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MAX =
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST - 1,
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_report: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP: 64-bit unsigned value to
+ * indicate the timestamp when this report is generated, timestamp in
+ * microseconds from system boot. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST: Nested attribute of u32
+ * attributes. This indicates the list of frequencies that were scanned. This is
+ * an optional attribute. If this is not specified, all frequencies allowed in
+ * the current regulatory domain were scanned.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST: Nested attribute.
+ * This indicates information of the scanned BSSs by the firmware. This is an
+ * optional attribute.
+ *
+ * The attributes defined in enum qca_wlan_vendor_attr_fw_scan_bss are nested
+ * in this attribute.
+ */
+enum qca_wlan_vendor_attr_fw_scan_report {
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP = 1,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST = 2,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST = 3,
+
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_MAX =
+ QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_idle_shutdown_status: Represents idle shutdown status.
+ *
+ * @QCA_WLAN_IDLE_SHUTDOWN_STARTED: Indicates idle shutdown is started in the
+ * host driver.
+ * @QCA_WLAN_IDLE_SHUTDOWN_COMPLETED: Indicates idle shutdown is completed in
+ * the host driver.
+ */
+enum qca_wlan_idle_shutdown_status {
+ QCA_WLAN_IDLE_SHUTDOWN_STARTED = 0,
+ QCA_WLAN_IDLE_SHUTDOWN_COMPLETED = 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_idle_shutdown: Attributes used by vendor event
+ * %QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS: Required u8 attribute. Indicates
+ * the status of the idle shutdown from one of the values in enum
+ * qca_wlan_idle_shutdown_status.
+ */
+enum qca_wlan_vendor_attr_idle_shutdown {
+ QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS = 1,
+
+ QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_MAX =
+ QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_pri_link_migrate: Attributes used by the vendor
+ * subcommand/event %QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR: 6 byte MAC address.
+ * (a) Used in a subcommand to indicate that primary link migration
+ * will occur only for the ML client with the given MLD MAC address.
+ * (b) Used in an event to specify the MAC address of the peer for which
+ * the primary link has been modified.
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID: Optional u8
+ * attribute. Used with subcommand only.
+ * When specified, all ML clients having their current primary
+ * link as specified will be considered for migration.
+ * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID: Optional u8 attribute.
+ * (a) Used in subcommand, to indicate the new primary link to which the
+ * selected ML clients should be migrated to. If not provided, the driver
+ * will select a suitable primary link on its own.
+ * (b) Used in event, to indicate the new link ID which is set as the
+ * primary link.
+ */
+enum qca_wlan_vendor_attr_pri_link_migrate {
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID = 2,
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID = 3,
+
+ /* keep this last */
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MAX =
+ QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_btm_req_resp_type: Represents response types to follow
+ * upon receiving BTM request from AP.
+ *
+ * @QCA_WLAN_BTM_REQ_RESP_DEFAULT: Reset to default behavior.
+ * @QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME: Send link reconfiguration request
+ * frames with specified info.
+ * @QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME: Send TTLM request frame.
+ * @QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME: Send Reassociation Request frame.
+ */
+enum qca_wlan_vendor_btm_req_resp_type {
+ QCA_WLAN_BTM_REQ_RESP_DEFAULT = 0,
+ QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME = 1,
+ QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME = 2,
+ QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_reconfig_frame_info - Attribute used by
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK: u16 attribute. Bitmask of
+ * link IDs to be added.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK: u16 attribute bitmask of
+ * link IDs to be removed.
+ */
+enum qca_wlan_vendor_attr_reconfig_frame_info {
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK = 1,
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK = 2,
+
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_btm_req_resp - Attribute used by
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE: u8 attribute. Indicates type of
+ * response to send. Possible values for this attribute are defined in
+ * enum qca_wlan_vendor_btm_req_resp_type. This is a mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO: Array of nested
+ * attributes containing information about one or more setup link
+ * reconfiguration request frames, each set represents one link reconfiguration
+ * frame information. The driver shall send a separate link reconfiguration
+ * frame for each nested attribute set. It takes attributes as defined in enum
+ * qca_wlan_vendor_attr_reconfig_frame_info. This attribute must be present
+ * when %QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME specified in
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP: TID to Link Mapping to
+ * be used in TTLM request frame. This nested attribute with
+ * %NL80211_ATTR_MLO_TTLM_DLINK and %NL80211_ATTR_MLO_TTLM_ULINK is used to
+ * specify the TID to Link mapping for downlink/uplink traffic. This attribute
+ * must be present when %QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME specified in
+ * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute.
+ */
+enum qca_wlan_vendor_attr_btm_req_resp {
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO = 2,
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP = 3,
+
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_MAX =
+ QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg: Attributes used
+ * by vendor subcmd QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR: Connected peer
+ * MAC address to which Probe Response frames are to be sent.
+ * Multicast/Broadcast addresses are not supported.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD: 32-bit unsigned value.
+ * This attribute specifies the interval (in microseconds) in which directed
+ * Probe Response frames are sent periodically to the peer as specified in
+ * attribute QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR. When the peer
+ *is in power save, sending of the frames might be delayed until the device
+ * comes out of power save. Attribute value can be in the range of minimum value
+ * of 50000 and maximum value of 1500000.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_COUNT: 8-bit unsigned value.
+ * Specifies number of directed Probe Responses frames that can be sent as per
+ * interval defined in QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD. When
+ * attribute value is 255, directed Probe Response frames are sent continuously
+ * until this attribute is sent as 0 in the command to disable period
+ * transmission. When the attribute value is 1, one directed Probe Response
+ * frame will be sent and the attribute
+ * QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD will not be considered.
+ */
+enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg {
+ QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PEER_MAC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PERIOD = 2,
+ QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_COUNT = 3,
+
+ QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_MAX =
+ QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_flow_status_update_type - Attribute values for
+ * %QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE.
+ * @QCA_FLOW_STATUS_UPDATE_PAUSE: Flow paused.
+ * @QCA_FLOW_STATUS_UPDATE_RESUME: Flow resumed.
+ * @QCA_FLOW_STATUS_UPDATE_DELETE: Flow deleted.
+ */
+enum qca_flow_status_update_type {
+ QCA_FLOW_STATUS_UPDATE_PAUSE = 0,
+ QCA_FLOW_STATUS_UPDATE_RESUME = 1,
+ QCA_FLOW_STATUS_UPDATE_DELETE = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_status - Definition of attributes to
+ * specify status updates of a classified flow. This enum is used by
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TUPLE: Mandatory nested attribute
+ * containing attributes defined by enum qca_wlan_vendor_attr_flow_tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TRAFFIC_TYPE: Mandatory u8 attribute
+ * indicates the traffic type of this flow tuple. Uses the
+ * enum qca_traffic_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE: Mandatory u8 attribute
+ * indicates the flow status update type.
+ * Uses the enum qca_flow_status_update_type values.
+ */
+enum qca_wlan_vendor_attr_flow_status {
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TUPLE = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TRAFFIC_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_iq_data_inference - Represents the attributes sent
+ * as part of IQ data inference messages.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CMD_TYPE: u32 attribute represents
+ * the command type sent from userspace to the driver.
+ * The values are defined in enum qca_wlan_vendor_iq_inference_cmd_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_BW: u32 attribute containing
+ * one of the values of &enum nl80211_chan_width, describing the channel width.
+ * See the documentation of the enum for more information.
+ *
+ * @CA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CHANNEL_FREQ: u32 attribute represents
+ * the channel frequency (in MHz).
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_1: u32 attribute
+ * represents the primary center frequency (in MHz).
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_2: u32 attribute
+ * represents the secondary center frequency (in MHz); valid only for
+ * 80+80 MHz channels.
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MCS: u32 attribute represents the MCS
+ * Index w.r.t. various PHY mode as per
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE.
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TEMPERATURE: s32 attribute represents
+ * the device temperature in degree Celcius.
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STAGE: u32 attributes represents the
+ * inference stage. The values are defined in
+ * enum qca_wlan_vendor_iq_inference_stage.
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_EVM: s32 attribute represents the
+ * Error Vector Magniture (in dB). Userspace App derives EVM based on the IQ
+ * samples data and send it to the driver.
+ * This attribute is used with @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MASK_MARGIN: s32 attribute represents
+ * the Spectral Mask Margin (in dB). Userspace app derives mask margin based on
+ * the IQ samples data and send it to the driver.
+ * This attribute is used with @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE: u32 attribute represents
+ * the PHY mode for which inference is being sent.
+ * This is sent from the driver to user space as part of the event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_SAMPLE_SIZE: u32 attribute represents
+ * the IQ sample size (in bytes).
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ * User space application can read the IQ samples from the memory mapped
+ * IO file /dev/txpb.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STATUS: u32 attribute represents the
+ * inference status.
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ * The values are defined in enum qca_wlan_vendor_iq_inference_status.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_COOKIE: u64 cookie provided by the
+ * driver for the specific inference request.
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE and the same cookie needs to be
+ * passed back to the driver as part of the command
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE
+ * to maintain synchronization between commands and asynchronous events.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PAD: Attribute used for padding for
+ * 64-bit alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_POWER: s32 attribute represents
+ * the TX power of an inferencing packet (in dBm).
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_CHAIN_IDX: u32 attribute
+ * represents the TX chain index on which inferencing is requested for.
+ * This is sent from the driver to user space as part of event
+ * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE.
+ */
+enum qca_wlan_vendor_attr_iq_data_inference {
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CMD_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_BW = 2,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CHANNEL_FREQ = 3,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_1 = 4,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_2 = 5,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MCS = 6,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TEMPERATURE = 7,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STAGE = 8,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_EVM = 9,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MASK_MARGIN = 10,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE = 11,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_SAMPLE_SIZE = 12,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STATUS = 13,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_COOKIE = 14,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PAD = 15,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_POWER = 16,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_CHAIN_IDX = 17,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MAX =
+ QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_iq_inference_cmd_type - Represents the command types
+ * userspace app sends to the driver.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_START: Represents the stage where
+ * userspace app has started and is ready to receive IQ samples.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_STOP: Represents the stage where
+ * userspace app has stopped and is no longer ready to receive IQ samples.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT: Represents the inference result
+ * sent from userspace to the driver.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_FAILURE: Represents the inference failure
+ * sent from userspace to the driver.
+ *
+ */
+enum qca_wlan_vendor_iq_inference_cmd_type {
+ QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_START = 0,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_STOP = 1,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT = 2,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_FAILURE = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_iq_inference_stage - Represents the inference stage
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FIRST_PASS: Represents the first
+ * pass in inference stage.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_SECOND_PASS: Represents the second
+ * pass in inference stage.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_THIRD_PASS: Represents the third
+ * pass in inference stage.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FOURTH_PASS: Represents the fourth
+ * pass in inference stage.
+ */
+enum qca_wlan_vendor_iq_inference_stage {
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FIRST_PASS = 0,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_SECOND_PASS = 1,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_THIRD_PASS = 2,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FOURTH_PASS = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_iq_inference_status - Represents the inference
+ * status sent from driver to user space.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_START_INFERENCE: Represents the stage where
+ * userspace app to start the inference.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_ABORT: Represents the stage where
+ * userspace app to stop the inference.
+ *
+ * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_COMPLETE: Represents the stage where
+ * inference is complete.
+ *
+ */
+enum qca_wlan_vendor_iq_inference_status {
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_START_INFERENCE = 0,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_ABORT = 1,
+ QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_COMPLETE = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_p2p_set_noa - Represents the attributes sent as a
+ * part of set NoA message. This enum is used by
+ * %QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT: u8 mandatory attribute.
+ * 0 - Disable NoA
+ * 1 to 254 - Number of absence periods to be scheduled during the NoA.
+ * 255 - Absence periods shall repeat until the NoA is disabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_DURATION: u32 optional attribute. This is
+ * mandatory attribute when %QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT is set to
+ * non-zero value. This specifies the duration (in TUs) of each absence period.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INTERVAL: u32 optional attribute. This is
+ * mandatory attribute when %QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT is set to
+ * non-zero value. This specifies time (in TUs) between consecutive absence
+ * periods.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_START: u32 optional attribute. This
+ * specifies the start offset time (in milliseconds) of the first absence period
+ * after the beacon advertising the NoA. If this attribute not present, the
+ * driver will use the default start offset value.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA: Optional u8 attribute
+ * for use with QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA on a P2P GO interface.
+ *
+ * In multi-channel concurrency (MCC) scenarios, when a local STA interface
+ * sharing the same radio with the P2P GO performs a connection or roaming
+ * operation on a different channel, the firmware automatically initiates a
+ * one-shot NoA schedule on the P2P GO to facilitate the STA's channel
+ * operations. This attribute controls whether the firmware should automatically
+ * cancel this one-shot NoA schedule when the STA operation completes.
+ *
+ * This feature should only be enabled when all connected P2P Clients support
+ * staying awake during one-shot NoA (see
+ * QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA). Peer device
+ * capability should be determined through vendor-specific mechanisms such as
+ * device model/OUI whitelists or capability handshake via Bluetooth.
+ *
+ * Values:
+ * 1 - Enable: The firmware will cancel the current firmware-initiated one-shot
+ * NoA schedule when the local STA interface completes its connection or
+ * roaming operation. The cancellation will be reflected in subsequent
+ * Beacon frames (typically the next Beacon frame, though timing may vary
+ * based on beacon scheduling). This allows immediate resumption of P2P data
+ * transmission.
+ * 0 - Disable (default): NoA schedules are not automatically cancelled and
+ * will run for their full configured duration.
+ *
+ * Note: This attribute only affects firmware-initiated one-shot NoA schedules
+ * created for MCC STA operations. It does not affect NoA schedules explicitly
+ * configured by userspace through other attributes of this command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA: Optional
+ * u8 attribute for use with QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA on a P2P
+ * Client interface.
+ *
+ * This attribute controls whether the P2P Client should stay awake during
+ * one-shot NoA periods instead of entering sleep. This is designed to work in
+ * conjunction with QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA on the
+ * P2P GO side.
+ *
+ * This feature should only be enabled when the peer P2P GO supports early NoA
+ * cancellation (see QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA).
+ * Peer device capability should be determined through vendor-specific
+ * mechanisms such as device model/OUI whitelists or capability handshake via
+ * Bluetooth.
+ *
+ * Values:
+ * 1 - Enable: The P2P Client will stay awake during one-shot NoA periods
+ * instead of entering sleep. This allows the client to immediately receive
+ * frames when the P2P GO cancels a one-shot NoA early, without waiting for
+ * the originally configured NoA duration to expire.
+ * 0 - Disable (default): The P2P Client enters sleep during NoA periods as
+ * specified in the NoA schedule received from the P2P GO.
+ *
+ * Note: This attribute only affects behavior during one-shot NoA periods. It
+ * does not affect behavior during continuous NoA schedules.
+ */
+enum qca_wlan_vendor_attr_p2p_set_noa {
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT = 1,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_DURATION = 2,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INTERVAL = 3,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_START = 4,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA = 5,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_MAX =
+ QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wlan_telemetry - Represents the attributes sent
+ * from userspace application to the driver to get WLAN statistics.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_OBJECT: u8 attribute representing the
+ * object type for which the statistics are requested. The types of objects are
+ * defined in enum qca_wlan_vendor_wlan_telemetry_obj_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEATURE: Nested attribute representing
+ * the set of WLAN features for which statistics are requested.
+ * The nested attributes are of type NLA_FLAG and allow userspace applications
+ * to request multiple feature-specific statistics simultaneously. When a
+ * userspace application needs specific feature statistics, the corresponding
+ * feature attribute will be present. If a feature is not specified by the user,
+ * all feature attributes will be present by default. The nested flag
+ * attributes identifying the types of features are defined in
+ * enum qca_wlan_vendor_wlan_telemetry_feat_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_STA_MAC: 6 byte MAC address attribute of
+ * a station which is required when the statistics are being requested for a
+ * specific station. This can be either an MLD MAC address or a link MAC
+ * address. In case the MLD MAC address and the link MAC address are same,
+ * an additional attribute @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID to
+ * specify the Link ID is required.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_REQUEST_ID: u64 attribute representing
+ * the request ID for a non-blocking statistics request. This request ID will
+ * be returned back in the corresponding reply event from the driver to allow
+ * userspace to correlate the event reply with its request command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID: u8 attribute representing the
+ * link ID for which the statistics are requested.
+ */
+enum qca_wlan_vendor_attr_wlan_telemetry {
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_OBJECT = 1,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEATURE = 2,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_STA_MAC = 3,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_REQUEST_ID = 4,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID = 5,
+
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_MAX =
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_wlan_telemetry_obj_type - Represents the object types for
+ * which the statistics are requested.
+ *
+ * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_STA: Represents the station object. This
+ * enum value will be present if the userspace application requests for station
+ * level statistics from the driver.
+ *
+ * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_VDEV: Represents the vdev object which
+ * corresponds to the virtual interface of the radio. This enum
+ * value will be present if the userspace application requests for vdev level
+ * statistics from the driver.
+ *
+ * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_RADIO: Represents the radio interface
+ * of the device. This enum value will be present if the userspace application
+ * requests for radio level statistics from the driver.
+ *
+ * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_DEVICE: Represents the device object.
+ * This enum value will be present if the userspace application requests
+ * for device level statistics from the driver.
+ */
+
+enum qca_wlan_vendor_wlan_telemetry_obj_type {
+ QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_STA = 1,
+ QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_VDEV = 2,
+ QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_RADIO = 3,
+ QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_DEVICE = 4,
+};
+
+/**
+ * qca_wlan_vendor_attr_wlan_telemetry_feat_type - Represents the feature types
+ * for which the statistics are requested.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_TX: Flag attribute representing the
+ * TX feature type which will be present if the userspace application requests
+ * for TX statistics from the driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX: Flag attribute representing the
+ * RX feature type which will be present if the userspace application requests
+ * for RX statistics from the driver.
+ *
+ */
+enum qca_wlan_vendor_attr_wlan_telemetry_feat_type {
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_TX = 1,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX = 2,
+
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_MAX =
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply - Represents the set of
+ * attributes included in the event reply from the driver to the userspace
+ * application in response to the WLAN statistics request command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_OBJECT: u8 attribute representing
+ * the object type of the statistics event reply. The types of objects are
+ * defined in enum qca_wlan_vendor_wlan_telemetry_obj_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_LINK_ID: u8 attribute representing
+ * the link ID of the statistics event reply.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_REQUEST_ID: u64 attribute
+ * representing the request ID of the statistics event reply. This ID is taken
+ * from the statistics request command sent by userspace application and
+ * included in the statistics event reply to correlate the event reply with its
+ * request command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_RX_STATS: Nested attribute
+ * representing the types of WLAN RX statistics. This attribute will be present
+ * if the userspace application requests for RX statistics
+ * (QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX) in the request command.
+ * The types of RX statistics are defined in enum
+ * qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_TX_STATS: Nested attribute
+ * representing the attributes that define the WLAN TX statistic types.
+ * This attribute will be present if the userspace application requests
+ * for TX statistics (QCA_WLAN_VENDOR_ATTR_TELEMETRY_FEAT_TX) in the
+ * request command. The types of TX statistics are defined in enum
+ * qca_wlan_vendor_attr_telemetry_tx_stats_types.
+ */
+enum qca_wlan_vendor_attr_wlan_telemetry_event_reply {
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_OBJECT = 1,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_LINK_ID = 2,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_REQUEST_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_RX_STATS = 4,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_TX_STATS = 5,
+
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_MAX =
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types - Represents the types of
+ * RX statistics included in the event reply from driver to userspace
+ * application.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_RXDMA_ERR_EVENT: Nested
+ * attribute representing the types of RXDMA error statistics included in the
+ * event reply. The types of RXDMA errors are defined in
+ * enum qca_wlan_vendor_wlan_telemetry_rxdma_error_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_ERR_EVENT: Nested attribute
+ * representing the types of REO error statistics included in the event reply.
+ * The types of REO errors are defined in
+ * enum qca_wlan_vendor_wlan_telemetry_reo_error_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_WBM_SW_DROP_RSN: Nested
+ * attribute representing the attributes that define the WBM software error
+ * types. The types of WBM software drop attributes are defined in
+ * enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_SW_DROP_RSN: Nested
+ * attribute representing the REO ring attributes which are defined in
+ * enum qca_wlan_vendor_telemetry_reo_ring_attr. Each REO ring attribute is
+ * further a nested attribute which represents the attributes that define the
+ * REO software error drops. The types of REO software error drops are defined
+ * in enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason. Since this error
+ * drop is applicable across all REO rings this is first nested with REO ring
+ * attributes and each REO ring attribute is further nested with the REO
+ * software error drop attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_PER_PKT_STATS: Nested attribute
+ * representing the REO ring attributes which are defined in
+ * enum qca_wlan_vendor_telemetry_reo_ring_attr. Each REO ring attribute is
+ * further a nested attribute which represents the attributes that define the
+ * RX per packet statistics. The types of RX per packet statistics are defined
+ * in enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types. Since this
+ * statistics is applicable across all REO rings this is first nested with REO
+ * ring attributes and each REO ring attribute is further nested with the RX
+ * per packet statistics.
+ */
+
+enum qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types {
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_RXDMA_ERR_EVENT = 1,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_ERR_EVENT = 2,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_WBM_SW_DROP_RSN = 3,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_SW_DROP_RSN = 4,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_PER_PKT_STATS = 5,
+
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_tx_stats_types - Represents the
+ * attributes that define the types of TX statistics included in the event
+ * reply from the driver to a userspace application.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_COMP_ERR: Nested attribute representing
+ * the TCL ring attributes which are defined in
+ * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is
+ * further a nested attribute which represents the attributes that define the
+ * TX completion errors. The types of TX completion errors are defined in
+ * enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types. Since this error is
+ * applicable across all TCL rings this is first nested with TCL ring
+ * attributes and each TCL ring attribute is further nested with the TX
+ * completion errors.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_PER_PKT_STATS: Nested attribute
+ * representing the TCL ring attributes which are defined in
+ * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is
+ * further a nested attribute which represents the attributes that define the
+ * TX per packet statistics. The types of TX per packet statistics are defined
+ * in enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats. Since this
+ * statistics is applicable across all TCL rings this is first nested with TCL
+ * ring attributes and each TCL ring attribute is further nested with the TX
+ * per packet statistics.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INGRESS_STATS: Nested attribute
+ * representing the TCL ring attributes which are defined in
+ * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is
+ * further a nested attribute which represents the attributes that define the
+ * TX ingress statistics. The types of TX ingress statistics are defined in
+ * enum qca_wlan_vendor_attr_telemetry_ingress_stats. Since this statistics is
+ * applicable across all TCL rings this is first nested with TCL ring
+ * attributes and each TCL ring attribute is further nested with the TX ingress
+ * statistics.
+ */
+enum qca_wlan_vendor_attr_telemetry_tx_stats_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_COMP_ERR = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_PER_PKT_STATS = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INGRESS_STATS = 3,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_telemetry_tcl_ring_attr - Represents the attributes
+ * that define the types of TCL rings. These attributes are used for the
+ * statistics that are applicable per TCL ring. For statistics that are
+ * applicable to each individual TCL ring, the corresponding statistics
+ * attributes are nested under each TCL ring. This design ensures that every
+ * relevant statistics attribute is distinctly represented for each TCL ring
+ * enabling precise and ring-specific statistics collection.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_0: Nested attribute representing
+ * TCL ring 0. This will be nested with the statistics that are applicable
+ * per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_1: Nested attribute representing
+ * TCL ring 1. This will be nested with the statistics that are applicable
+ * per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_2: Nested attribute representing
+ * TCL ring 2. This will be nested with the statistics that are applicable
+ * per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_3: Nested attribute representing
+ * TCL ring 3. This will be nested with the statistics that are applicable
+ * per TCL ring.
+ */
+enum qca_wlan_vendor_telemetry_tcl_ring_attr {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_0 = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_1 = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_2 = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_3 = 4,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_telemetry_reo_ring_attr - Represents the attributes
+ * that define the types of REO rings. These attributes are used for the
+ * statistics that are applicable per REO ring. For statistics that are
+ * applicable to each individual REO ring, the corresponding statistics
+ * attributes are nested under each REO ring. This design ensures that every
+ * relevant statistics attribute is distinctly represented for each REO ring
+ * enabling precise and ring-specific statistics collection.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_0: Nested attribute representing
+ * REO ring 0. This will be nested with the statistics that are applicable
+ * per REO ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_1: Nested attribute representing
+ * REO ring 1. This will be nested with the statistics that are applicable
+ * per REO ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_2: Nested attribute representing
+ * REO ring 2. This will be nested with the statistics that are applicable
+ * per REO ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_3: Nested attribute representing
+ * REO ring 3. This will be nested with the statistics that are applicable
+ * per REO ring
+ */
+enum qca_wlan_vendor_telemetry_reo_ring_attr {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_0 = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_1 = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_2 = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_3 = 4,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_wlan_telemetry_rxdma_error_types - Representing the
+ * types of RXDMA errors.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_OVERFLOW: u32 attribute
+ * representing the number of MPDU frames that are not completed due to a
+ * FIFO overflow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MPDU_LENGTH: u32 attribute
+ * representing the number of MPDU frames that are not complete due to receiving
+ * an incomplete MPDU from the PHY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FCS: u32 attribute representing
+ * the FCS check failure on the MPDU frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DECRYPT: u32 attribute representing
+ * the decryption error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_TKIP_MIC: u32 attribute
+ * representing the TKIP MIC error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNENCRYPTED: u32 attribute
+ * representing the number of frames which are expected to be encrypted but was
+ * not encrypted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LEN: u32 attribute
+ * representing the failures due to MPDU length errors.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LIMIT: u32 attribute
+ * representing the number of MSDUs which exceeded the max allowed in MPDUs.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_WIFI_PARSE: u32 attribute
+ * representing the wifi parsing error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_PARSE: u32 attribute
+ * representing the A-MSDU parsing error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_SA_TIMEOUT: u32 attribute
+ * representing the source address search timeout.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DA_TIMEOUT: u32 attribute
+ * representing the destination address search timeout.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLOW_TIMEOUT: u32 attribute
+ * representing the flow search timeout.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLUSH_REQUEST: u32 attribute
+ * representing the RXDMA FIFO flush requests.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_AMSDU_FRAGMENT: u32 attribute
+ * representing the presence of fragmented MPDUs as well as A-MSDUs, as
+ * reported by the RX PCU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_MULTICAST_ECHO: u32 attribute
+ * representing multicast echo as reported by the RX OLE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_ADDR_MISMATCH: u32 attribute
+ * representing the count of address mismatch in A-MSDU frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNAUTH_WDS: u32 attribute
+ * representing the count of unauthenticated WDS frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_GCAST_AMSDU_OR_WDS: u32 attribute
+ * representing count of the groupcast A-MSDU or WDS frames.
+ */
+enum qca_wlan_vendor_attr_wlan_telemetry_rxdma_error_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_OVERFLOW = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MPDU_LENGTH = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FCS = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DECRYPT = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_TKIP_MIC = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNENCRYPTED = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LEN = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LIMIT = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_WIFI_PARSE = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_PARSE = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_SA_TIMEOUT = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DA_TIMEOUT = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLOW_TIMEOUT = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLUSH_REQUEST = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_AMSDU_FRAGMENT = 15,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_MULTICAST_ECHO = 16,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_ADDR_MISMATCH = 17,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNAUTH_WDS = 18,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_GCAST_AMSDU_OR_WDS = 19,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_wlan_telemetry_reo_error_types - Represents the
+ * types of REO errors.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_ADDR_0: u32 attribute
+ * representing the number of times the REO queue descriptor provided in the
+ * REO_ENTRANCE ring is 0.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_INVALID: u32 attribute
+ * representing the number of times the REO queue descriptor valid bit is not
+ * set.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AMPDU_IN_NON_BA: u32 attribute
+ * representing the number of times A-MPDU frames are received without BA
+ * session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_NON_BA_DUPLICATE: u32 attribute
+ * representing the duplicate frames in a non BA session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BA_DUPLICATE: u32 attribute
+ * representing the duplicate frame in a BA session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_2K_JUMP: u32 attribute
+ * representing the number of times a normal frame (Management/Data frame)
+ * received with 2K jump in SN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_2K_JUMP: u32 attribute
+ * representing the number of times a BAR frame received with 2K jump in SSN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_OOR: u32 attribute
+ * representing the number of times a normal frame (Management/Data frame)
+ * received with SN falling within the OOR window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_OOR: u32 attribute
+ * representing the number of times a BAR is received with SN falling within
+ * the OOR window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_NO_BA_SESSION: u32
+ * attribute representing the number of times a BAR received without a BA
+ * session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_SN_EQUALS_SSN: u32
+ * attribute representing the number of times a BAR received with SSN equal to
+ * SN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_CHECK_FAILED: u32 attribute
+ * representing the number of times PN check failed for a frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_2K_ERROR_HANDLING_FLAG_SET: u32
+ * attribute representing the number of times a frame is forwarded due to
+ * Seq_2k_error_detected_flag being set in the REO queue descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_ERROR_HANDLING_FLAG_SET: u32
+ * attribute representing the number of times a frame is forwarded due to
+ * pn_error_detected_flag being set in the REO queue descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_BLOCKED_SET: u32
+ * attribute representing the number of times a frame is forwarded as a result
+ * of the queue descriptor (address) being blocked as software/firmware appears
+ * to be in the process of updating this descriptor.
+ */
+enum qca_wlan_vendor_attr_wlan_telemetry_reo_error_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_ADDR_0 = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_INVALID = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AMPDU_IN_NON_BA = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_NON_BA_DUPLICATE = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BA_DUPLICATE = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_2K_JUMP = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_2K_JUMP = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_OOR = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_OOR = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_NO_BA_SESSION = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_SN_EQUALS_SSN = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_CHECK_FAILED = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_2K_ERROR_HANDLING_FLAG_SET = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_ERROR_HANDLING_FLAG_SET = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_BLOCKED_SET = 15,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason - Represents the
+ * attributes that define the types of RX WBM software drop reasons.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_ERR: u32 attribute
+ * representing the number of software drop from WBM due to descriptor error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CK_ERR: u32 attribute
+ * representing the number of software drop from WBM due to ck error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PEER_ID: u32 attribute
+ * representing the number of software drop from WBM due to invalid peer id.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_PARSE_ERR: u32 attribute
+ * representing the number of software drop from WBM due to descriptor parse
+ * error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_COOKIE: u32 attribute
+ * representing the number of software drop from WBM due to invalid cookie.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PUSH_RSN: u32 attribute
+ * representing the number of software drop from WBM due to invalid push reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_HW_ID: u32 attribute
+ * representing the number of software drop from WBM due to invalid hardware id.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PTNR_DP_NA: u32 attribute
+ * representing the number of software drop from WBM due to NULL partner
+ * datapath member.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PROC_NA_PTNR_DP: u32 attribute
+ * representing the number of software drop from WBM while processing NULL
+ * partner datapath member.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_PDEV: u32 attribute
+ * representing the number of software drop from WBM due to NULL pdev.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_AR: u32 attribute
+ * representing the number of software drop from WBM due to NULL ar.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CAC_RUNNING: u32 attribute
+ * representing the number of software drop from WBM due to CAC running.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_SCATTER_GATHER: u32 attribute
+ * representing the number of software drop from WBM due to scatter gather.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_NWIFI_HLEN: u32 attribute
+ * representing the number of software drop from WBM due to invalid native
+ * Wi-Fi header length.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_REO_GENERIC: u32 attribute
+ * representing the number of software drop from WBM due to generic REO error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_RXDMA_GENERIC: u32 attribute
+ * representing the number of software drop from WBM due to generic RXDMA error.
+ */
+enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_ERR = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CK_ERR = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PEER_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_PARSE_ERR = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_COOKIE = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PUSH_RSN = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_HW_ID = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PTNR_DP_NA = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PROC_NA_PTNR_DP = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_PDEV = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_AR = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CAC_RUNNING = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_SCATTER_GATHER = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_NWIFI_HLEN = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_REO_GENERIC = 15,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_RXDMA_GENERIC = 16,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason - Represents the
+ * attributes that define the types of REO software drops for each REO ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MISC: u32 attribute
+ * representing the number of software drop from REO due to miscellaneous
+ * reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_CK_ERR: u32 attribute
+ * representing the number of software drop from REO due to ck error in
+ * software descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_DESC_ERR: u32 attribute
+ * representing the number of software drop from REO due to error in software
+ * descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_REPLENISH: u32 attribute
+ * representing the number of software drop from REO due to replenish error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PTNR_DP_NA: u32 attribute
+ * representing the number of software drop from REO due to NULL partner
+ * datapath member.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PDEV_NA: u32 attribute
+ * representing the number of software drop from REO due to NULL pdev.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_LAST_MSDU_NA: u32 attribute
+ * representing the number of software drop from REO due to last MSDU bit not
+ * found.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_NWIFI_HLEN_INVAL: u32 attribute
+ * representing the number of software drop from REO due to invalid native
+ * Wi-Fi header length.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_MSDU_LEN: u32 attribute
+ * representing the number of software drop from REO due to invalid MSDU length.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MSDU_COAL_FAIL: u32 attribute
+ * representing the number of software drop from REO due to MSDU coalesce
+ * failure.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MPDU: u32 attribute
+ * representing the number of software drop from REO due to MPDU drop.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PPDU: u32 attribute
+ * representing the number of software drop from REO due to PPDU drop.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_PEER: u32 attribute
+ * representing the number of software drop from REO due to invalid peer.
+ */
+enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MISC = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_CK_ERR = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_DESC_ERR = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_REPLENISH = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PTNR_DP_NA = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PDEV_NA = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_LAST_MSDU_NA = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_NWIFI_HLEN_INVAL = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_MSDU_LEN = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MSDU_COAL_FAIL = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MPDU = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PPDU = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_PEER = 13,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types: Represents the
+ * attributes that define the various per packet RX statistics populated
+ * for each REO rings.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_FROM_REO: Nested attribute
+ * representing the attributes that defines the information of MSDUs received
+ * from REO ring in per packet path. The types of packet information are
+ * defined in enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK: Nested attribute
+ * representing the attributes that define the information of MSDUs sent to
+ * stack in per packet path. The types of packet information are defined in
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK_FAST: Nested attribute
+ * representing the attributes that define the information of MSDUs sent to
+ * stack in fast path. The types of packet information are defined in
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MCAST: u32 attribute representing
+ * the number of RX multicast MSDUs received in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_UCAST: u32 attribute representing
+ * the number of RX unicast MSDUs received in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_NON_AMSDU: u32 attribute
+ * representing the number of RX MSDUs received in per packet path that were
+ * not part of an A-MSDU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MSDU_IN_AMSDU: u32 attribute
+ * representing the number of RX MSDUs received in per packet path that were
+ * part of an A-MSDU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MPDU_RETRY: u32 attribute
+ * representing the number of RX MPDU retries happened in per packet path.
+ */
+enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_FROM_REO = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK_FAST = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MCAST = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_UCAST = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_NON_AMSDU = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MSDU_IN_AMSDU = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MPDU_RETRY = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types - Represents the
+ * attributes that define the types of TX completion error for each TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_MISC: u32 attribute representing
+ * the number of MSDUs with TX completion error due to miscellaneous reasons.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_DESC: u32 attribute
+ * representing the number of MSDUs with TX completion error due to invalid
+ * descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PDEV: u32 attribute
+ * representing the number of MSDUs with TX completion error due to invalid
+ * pdev.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_VIF: u32 attribute
+ * representing the number of MSDUs with TX completion error due to invalid vif.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PEER: u32 attribute
+ * representing the number of MSDUs with TX completion error due to invalid
+ * peer.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_LINK_PEER: u32 attribute
+ * representing the number of MSDUs with TX completion error due to invalid
+ * link peer.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_DESC_INUSE: u32 attribute
+ * representing the number of MSDUs with TX completion error due to
+ * descriptor in use.
+ */
+enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_MISC = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_DESC = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PDEV = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_VIF = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PEER = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_LINK_PEER = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_DESC_INUSE = 7,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo - Represents the attributes that
+ * define the packet information.
+ *
+ * @QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_PKTS: u32 attribute representing the
+ * number of MSDUs transmitted/received.
+ *
+ * @QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_BYTES: u64 attribute representing
+ * the number of bytes transmitted/received.
+ */
+enum qca_wlan_vendor_attr_telemetry_pktinfo {
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_INVALID = 0,
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_PKTS = 1,
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_BYTES = 2,
+
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_MAX =
+ QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_ingress_stats - Represents the attributes
+ * that define the types of TX ingress statistics for each TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_FROM_STK: Nested attribute
+ * representing the attributes that define the information of ingress MSDUs
+ * received from stack. The types of packet information are defined in
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW: Nested attribute
+ * representing the attributes that define the information of ingress MSDUs
+ * enqueued to hardware. The types of packet information are defined in
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW_FAST: Nested attribute
+ * representing the attributes that define the information of ingress MSDUs
+ * enqueued to hardware in fast path. The types of packet information are
+ * defined in enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCAP_TYPE: Nested attribute
+ * representing the attributes that define the count of ingress MSDUs with
+ * respective encapsulation types. The types of encapsulation are defined
+ * in enum qca_wlan_vendor_attr_telemetry_ingress_encap_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCRYPT_TYPE: Nested attribute
+ * representing the attributes that define the count of ingress MSDUs with
+ * respective encryption types. The types of encryption are defined in
+ * enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DESC_TYPE: Nested attribute
+ * representing the attributes that define the count of ingress MSDUs with
+ * respective descriptor type. The types of descriptor are defined in
+ * enum qca_wlan_vendor_attr_telemetry_ingress_desc_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DROP_TYPE: Nested attribute
+ * representing the attributes that define the count of ingress MSDUs with
+ * respective drop types. The types of drops are defined in
+ * enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MCAST: u32 attribute representing
+ * the number of ingress multicast MSDUs.
+ */
+enum qca_wlan_vendor_attr_telemetry_ingress_stats {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_FROM_STK = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW_FAST = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCAP_TYPE = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCRYPT_TYPE = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DESC_TYPE = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DROP_TYPE = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MCAST = 8,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_ingress_encap_type - Represents the
+ * attributes that define the types of packet encapsulation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_RAW: u32 attribute representing the
+ * number of MSDUs with raw encapsulation type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_NWIFI: u32 attribute representing
+ * the number of MSDUs with native Wi-Fi encapsulation type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_ETH: u32 attribute representing
+ * the number of MSDUs with ethernet encapsulation type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_802_3: u32 attribute representing
+ * the number of MSDUs with 802_3 encapsulation type.
+ */
+enum qca_wlan_vendor_attr_telemetry_ingress_encap_type {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_RAW = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_NWIFI = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_ETH = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_802_3 = 4,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type - Represents the
+ * attributes that define the types of packet encryption.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_NO_MIC: u32 attribute
+ * representing the number of MSDUs with encryption type TKIP_NO_MIC.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_MIC: u32 attribute representing
+ * the number of MSDUs with encryption type TKIP_MIC.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI: u32 attribute representing
+ * the number of MSDUs with encryption type WAPI.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_128: u32 attribute representing
+ * the number of MSDUs with encryption type CCMP_128.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_OPEN: u32 attribute representing
+ * the number of MSDUs with encryption type open.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_256: u32 attribute representing
+ * the number of MSDUs with encryption type CCMP_256.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_128: u32 attribute representing
+ * the number of MSDUs with encryption type GCMP-128.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_256: u32 attribute
+ * representing the number of MSDUs with encryption type GCMP-256.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI_GCM_SM4: u32 attribute
+ * representing the number of MSDUs with encryption type WAPI_GCM_SM4.
+ */
+enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_NO_MIC = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_MIC = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_128 = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_OPEN = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_256 = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_128 = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_256 = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI_GCM_SM4 = 9,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_ingress_desc_type - Represents the
+ * attributes that define the ingress MSDUs descriptor types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_BUF: u32 attribute representing
+ * the number of ingress MSDUs with buffer descriptor type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_EXT_DESC: u32 attribute
+ * representing the number of ingress MSDUs with extended descriptor type.
+ */
+enum qca_wlan_vendor_attr_telemetry_ingress_desc_type {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_BUF = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_EXT_DESC = 2,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types - Represents
+ * the attributes that define the ingress enqueue drop error types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MISC: u32 attribute representing
+ * the number of ingress MSDUs dropped due to miscellaneous reason.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_VIF_TYPE_MON: u32 attribute
+ * representing the number of ingress MSDUs dropped due to monitor vif type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_LINK: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid link id.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid arvif.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MGMT_FRAME: u32 attribute
+ * representing the number of ingress MSDUs dropped as it is Management frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TX_LIMIT: u32 attribute
+ * representing the number of ingress MSDUs dropped due to maximum TX limit
+ * reached.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid pdev.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PEER: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid peer.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_CACHE_FLUSH: u32 attribute
+ * representing the number of ingress MSDUs dropped due to cache flush.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_NON_DATA_FRAME: u32 attribute
+ * representing the number of ingress MSDUs dropped due to non Data frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_SW_DESC_NA: u32 attribute
+ * representing the number of ingress MSDUs dropped due to null software
+ * descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_RAW: u32 attribute
+ * representing the number of ingress MSDUs dropped due to raw encapsulation
+ * type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_802_3: u32 attribute
+ * representing the number of ingress MSDUs dropped due to 802_3 encapsulation
+ * type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_DMA_ERR: u32 attribute representing
+ * the number of ingress MSDUs dropped due to DMA error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_EXT_DESC_NA: u32 attribute
+ * representing the number of ingress MSDUs dropped due to null extended
+ * descriptor.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_HTT_MDATA_ERR: u32 attribute
+ * representing the number of ingress MSDUs dropped due to HTT metadata error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_NA: u32 attribute
+ * representing the number of ingress MSDUs dropped due to null descriptor
+ * error.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_RETRY: u32 attribute
+ * representing the number of ingress MSDUs dropped due to TCL descriptor retry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF_FAST: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid arvif in
+ * fast path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV_FAST: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid pdev in fast
+ * path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TXLIMIT_FAST: u32 attribute
+ * representing the number of ingress MSDUs dropped due to maximum TX limit
+ * reached in fast path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ENCAP_FAST: u32 attribute
+ * representing the number of ingress MSDUs dropped due to invalid
+ * encapsulation in fast path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_BRIDGE_VDEV: u32 attribute
+ * representing the number of ingress MSDUs dropped due to bridge vdev.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ARSTA_NA: u32 attribute representing
+ * the number of ingress MSDUs dropped due to arsta being NULL.
+ */
+enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MISC = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_VIF_TYPE_MON = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_LINK = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MGMT_FRAME = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TX_LIMIT = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PEER = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_CACHE_FLUSH = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_NON_DATA_FRAME = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_SW_DESC_NA = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_RAW = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_802_3 = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_DMA_ERR = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_EXT_DESC_NA = 15,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_HTT_MDATA_ERR = 16,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_NA = 17,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_RETRY = 18,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF_FAST = 19,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV_FAST = 20,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TXLIMIT_FAST = 21,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ENCAP_FAST = 22,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_BRIDGE_VDEV = 23,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ARSTA_NA = 24,
+
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ERR_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats - Represents the
+ * attributes that define the various per packet TX statistics per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_COMP_PKT: Nested attribute
+ * representing the attributes that define the information of TX MSDUs for
+ * which TX completion is received. The types of packet information are defined
+ * in enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_SUCCESS: Nested attribute
+ * representing the attributes that define the information of TX MSDUs which
+ * are successfully sent. The types of packet information are defined in
+ * enum qca_wlan_vendor_attr_telemetry_pktinfo.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_FAILED: u32 attribute representing
+ * the number of MSDUs for which TX is failed in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_WBM_REL_RSN: Nested attribute
+ * representing the attributes that define the types of TX WBM release reasons.
+ * The types of WBM release reasons are defined in
+ * enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TQM_REL_RSN: Nested attribute
+ * representing the attributes that define the types of TX TQM release reasons.
+ * The types of TQM release reasons are defined in
+ * enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_REL_SRC_NOT_TQM: u32 attribute
+ * representing the number of MSDUs for which release source is not TQM in TX
+ * completion status processing.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_RETRY_COUNT: u32 attribute
+ * representing the number of MSDUs successfully sent after one retry in per
+ * packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TOTAL_MSDU_RETRY: u32 attribute
+ * representing the total number of MSDUs retransmitted in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MULTI_RETRY_CNT: u32 attribute
+ * representing the number of MSDUs successfully sent after more than one retry
+ * in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_OFDMA: u32 attribute representing
+ * the number of TX OFDMA MSDUs transmitted in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AMSDU_CNT: u32 attribute
+ * representing the number of TX MSDUs transmitted in per packet path that
+ * are part of an A-MSDU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_NON_AMSDU_CNT: u32 attribute
+ * representing the number of TX MSDUs transmitted in per packet path with no
+ * MSDU level aggregation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVAL_LINK_ID: u32 attribute
+ * representing the number of TX MSDUs transmitted in per packet path for which
+ * the TX completion came with invalid link id.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MCAST: u32 attribute representing
+ * the number of TX multicast MSDUs transmitted in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_UCAST: u32 attribute representing
+ * the number of TX unicast MSDUs transmitted in per packet path.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_BCAST: u32 attribute representing
+ * the number of TX broadcast MSDUs transmitted in per packet path.
+ */
+enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_COMP_PKT = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_SUCCESS = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_FAILED = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_WBM_REL_RSN = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TQM_REL_RSN = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_REL_SRC_NOT_TQM = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_RETRY_COUNT = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TOTAL_MSDU_RETRY = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MULTI_RETRY_CNT = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_OFDMA = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AMSDU_CNT = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_NON_AMSDU_CNT = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVAL_LINK_ID = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MCAST = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_UCAST = 15,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_BCAST = 16,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status - Represents the
+ * attributes that define the types of TX completion status per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_OK: u32 attribute
+ * representing the number of MSDUs for which TX completion status is OK.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_DROP: u32 attribute
+ * representing the number of MSDUs which were dropped while transmitting. The
+ * TX completion status for these frames will be with reason 'drop'.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_TTL: u32 attribute
+ * representing the number of MSDUs for which TTL timeout occurred while
+ * transmitting. The TX completion status for these frames will be with reason
+ * 'TTL'.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_REINJ: u32 attribute
+ * representing the number of MSDUs which are reinjected. The TX completion
+ * status for these frames will be with reason 'REINJECT'.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INSPECT: u32 attribute
+ * representing the number of MSDUs which are under inspection. The TX
+ * completion status for these frames will be with reason 'INSPECT'.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_MEC_NOTIFY: u32 attribute
+ * representing the number of MSDUs with mec notify error. The TX completion
+ * status for these frames will be with reason 'MEC_NOTIFY'.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_VDEVID_MISMATCH: u32 attribute
+ * representing the number of MSDUs for which vdev id mismatch occurred. The TX
+ * completion status for these frames will be with reason 'VDEVID_MISMATCH'.
+ */
+enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_OK = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_DROP = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_TTL = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_REINJ = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INSPECT = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_MEC_NOTIFY = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_VDEVID_MISMATCH = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types: Represents the
+ * attributes that define the types of TQM release reason per TCL ring.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_FRAME_ACKED: u32 attribute
+ * representing the number of MSDUs for which ACK of BA is received.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_MPDU: u32 attribute
+ * representing the number of MSDUs for which remove command of type
+ * “Remove_mpdus” is initiated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_TX: u32 attribute
+ * representing the number of MSDUs for which remove command of type
+ * "Remove_transmitted_mpdus" is initiated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_NOTX: u32 attribute
+ * representing the number of MSDUs for which remove command of type
+ * "Remove_untransmitted_mpdus" is initiated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_AGED: u32 attribute
+ * representing the number of MSDUs for which remove command of type
+ * “Remove_aged_mpdus” or “Remove_aged_msdus” is initiated.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN1: u32 attribute
+ * representing the number of MSDUs for which remove command is given where
+ * firmware indicated that remove reason is fw_reason1.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN2: u32 attribute
+ * representing the number of MSDUs for which remove command is given where
+ * firmware indicated that remove reason is fw_reason2.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN3: u32 attribute
+ * representing the number of MSDUs for which remove command is given where
+ * firmware indicated that remove reason is fw_reason3.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_QUE_DISABLE: u32 attribute
+ * representing the number of MSDUs for which remove command is given where
+ * firmware indicated that remove reason is disabled queue.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_TILL_NONMATCH: u32 attribute
+ * representing the number of MSDUs for which remove command is given from
+ * firmware to remove all MPDU until the first non-match.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_THRESHOLD: u32 attribute
+ * representing the number of MSDUs dropped due to drop threshold criteria.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_LINK_DESC_NA: u32 attribute
+ * representing the number of MSDUs dropped due to link desc not available.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_OR_INVAL_MSDU: u32 attribute
+ * representing the number of MSDUs dropped due to drop bit set or null flow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MCAST_DROP: u32 attribute
+ * representing the number of MSDUs dropped due to multicast drop set for VDEV.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_VDEV_MISMATCH: u32 attribute
+ * representing the number of MSDUs dropped due to vdev Id mismatch.
+ */
+enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types {
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_FRAME_ACKED = 1,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_MPDU = 2,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_TX = 3,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_NOTX = 4,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_AGED = 5,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN1 = 6,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN2 = 7,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN3 = 8,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_QUE_DISABLE = 9,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_TILL_NONMATCH = 10,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_THRESHOLD = 11,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_LINK_DESC_NA = 12,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_OR_INVAL_MSDU = 13,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MCAST_DROP = 14,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_VDEV_MISMATCH = 15,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MAX =
+ QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_link_state_change_reason - Defines the different types
+ * of reasons for the link state change. Will be used with
+ * QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON attribute.
+ *
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_VDEV_UP: The link state change reported
+ * first time after the STA connection/interface up.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_RSSI: The link state change is due
+ * to low RSSI.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BMISS: The link state change is due to
+ * beacon miss.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_QUALITY: The link state change is
+ * due to low link quality.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_CAPACITY: The link state change is due
+ * to congestion of the links.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_ULL_MODE: The link state change is due
+ * to ultra low latency mode.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_T2LM: The link state change is due to
+ * TID_TO_LINK mapping change.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_FORCED: The link state change is due to
+ * user forced enable/disable.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BT_STATUS: The link state change is due
+ * to BT status.
+ * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_UNKNOWN: The link state change is due
+ * to unknown reason.
+ */
+enum qca_wlan_vendor_link_state_change_reason {
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_VDEV_UP = 0,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_RSSI = 1,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BMISS = 2,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_QUALITY = 3,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_CAPACITY = 4,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_ULL_MODE = 5,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_T2LM = 6,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_FORCED = 7,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BT_STATUS = 8,
+ QCA_WLAN_VENDOR_STATE_CHANGE_REASON_UNKNOWN = 9,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_link_state_change - Attributes used by
+ * QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_PEER_MLD_MAC: 6 byte MAC address.
+ * Indicates the associated AP MLD MAC address for the non-AP MLD interface.
+ * Indicates the peer MLD MAC address for the AP MLD interface
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON: u32 attribute. Indicates the
+ * reason for the link state change triggering the event.
+ * See enum qca_wlan_vendor_link_state_change_reason for the possible
+ * reasons.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_ACTIVE_LINK_BITMAP: u32 attribute.
+ * Indicates the link ID bitmap of the current active links among the affliated
+ * MLO links.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INACTIVE_LINK_BITMAP: u32 attribute.
+ * Indicates the link ID bitmap of the current inactive links among the
+ * affiliated MLO links.
+ */
+enum qca_wlan_vendor_attr_link_state_change {
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_PEER_MLD_MAC = 1,
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON = 2,
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_ACTIVE_LINK_BITMAP = 3,
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INACTIVE_LINK_BITMAP = 4,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_MAX =
+ QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_latency_report: Represents the possible values for
+ * %QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE.
+ *
+ * @QCA_WLAN_DAR_LATENCY_REPORT_HISTOGRAM: Indicates the Latency Statistics
+ * List field contains, or is requested to contain, histogram(s) of latency
+ * measurements.
+ *
+ * @QCA_WLAN_DAR_LATENCY_REPORT_PERCENTILE: Indicates the Latency Statistics
+ * List field contains, or is requested to contain, set(s) of percentile values
+ * of latency.
+ *
+ * @QCA_WLAN_DAR_LATENCY_REPORT_NO_PREF: Indicates the request has no preference
+ * as to the contents of the Latency Statistics List field; this value is
+ * reserved when the attribute is included in a DAR Response or DAR Report frame
+ */
+enum qca_wlan_dar_latency_report {
+ QCA_WLAN_DAR_LATENCY_REPORT_HISTOGRAM = 0,
+ QCA_WLAN_DAR_LATENCY_REPORT_PERCENTILE = 1,
+ QCA_WLAN_DAR_LATENCY_REPORT_NO_PREF = 2,
+};
+
+/*
+ * enum qca_wlan_dar_report_granularity: Represents the possible values for
+ * %QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY,
+ * %QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY,
+ * %QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY.
+ *
+ * @QCA_WLAN_DAR_REPORT_GRANULARITY_TID: Indicates the DAR report field
+ * contains, or is requested to contain, values with granularity at the
+ * TID level.
+ *
+ * @QCA_WLAN_DAR_REPORT_GRANULARITY_AC: Indicates the DAR report field
+ * contains, or is requested to contain, values with granularity at the AC
+ * level.
+ *
+ * @QCA_WLAN_DAR_REPORT_GRANULARITY_AGGREGATED_AC: Indicates the DAR field
+ * contains, or is requested to contain, values with granularity aggregated
+ * across all ACs.
+ */
+enum qca_wlan_dar_report_granularity {
+ QCA_WLAN_DAR_REPORT_GRANULARITY_TID = 0,
+ QCA_WLAN_DAR_REPORT_GRANULARITY_AC = 1,
+ QCA_WLAN_DAR_REPORT_GRANULARITY_AGGREGATED_AC = 2,
+};
+
+/*
+ * enum qca_wlan_dar_link_granularity: Represents the possible values
+ * for %QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY,
+ * %QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY,
+ * %QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY,
+ * %QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY,
+ * %QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY.
+ *
+ * @QCA_WLAN_DAR_LINK_GRANULARITY_DEVICE_LEVEL: Indicates the Latency
+ * Statistics List field contains, or is requested to contain, values at the
+ * device level. If the DAR responder is an MLD, this means at the MLD level.
+ *
+ * @QCA_WLAN_DAR_LINK_GRANULARITY_PER_LINK: Indicates the Latency
+ * Statistics List field contains, or is requested to contain, values at the
+ * link level (for an MLD). This value is only used if the DAR responder is an
+ * MLD.
+ */
+enum qca_wlan_dar_link_granularity {
+ QCA_WLAN_DAR_LINK_GRANULARITY_DEVICE_LEVEL = 0,
+ QCA_WLAN_DAR_LINK_GRANULARITY_PER_LINK = 1,
+};
+
+/*
+ * enum qca_wlan_dar_stats_latency_attr - Represents the possible latency
+ * statistics attributes for configuring
+ * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG.
+ *
+ * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE: Optional u8 attribute,
+ * indicates the type of report. Values are defined in
+ * enum qca_wlan_dar_latency_report.
+ *
+ * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY: Optional u8 attribute,
+ * indicates the report granularity. Values are defined in enum
+ * qca_wlan_dar_report_granularity.
+ *
+ * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY: Optional u8 attribute,
+ * indicates the link granularity. Values are defined in enum
+ * qca_wlan_dar_link_granularity.
+ *
+ * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_THRESHOLDS: An array of threshold values
+ * used to trigger reporting of latency statistics. Indexed first by link
+ * identifier. The number of threshold values is determined by: A * B * C, where
+ * - A is the Hamming weight of the Report Granularity Bitmap if Report
+ * Granularity is 0 or 1; otherwise A is 1.
+ * - B is the Hamming weight of the Link Granularity Bitmap if Link Granularity
+ * is 1; otherwise B is 1.
+ */
+enum qca_wlan_dar_stats_latency_attr {
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE = 1,
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY = 2,
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY = 3,
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_THRESHOLDS = 4,
+
+ /* keep last */
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_MAX =
+ QCA_WLAN_DAR_STATS_LATENCY_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_radio_counter_fields - Represents the possible subfields in
+ * the radio counter attribute for
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_TRANSMIT_POWER_FIELD: Indicates that transmit
+ * power field is included in the radio counter attribute.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD: Indicates that
+ * observed cu fraction field is included in the radio counter attribute.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD: Indicates that MPDU
+ * count statistics field is included in the radio counter attribute.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD: Indicates that RTS
+ * statistics field is included in the radio counter attribute.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD: Indicates that FCS failure
+ * field is included in the radio counter attribute.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_THRESHOLD_FIELDS_PRESENT: Indicates the presence
+ * of threshold fields in the radio counter attribute. This bit is set to 0 if
+ * the attribute is in a DAR Report frame.
+ */
+enum qca_wlan_dar_radio_counter_fields {
+ QCA_WLAN_DAR_RADIO_COUNTERS_TRANSMIT_POWER_FIELD = BIT(0),
+ QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD = BIT(1),
+ QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD = BIT(2),
+ QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD = BIT(3),
+ QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD = BIT(4),
+ QCA_WLAN_DAR_RADIO_COUNTERS_THRESHOLD_FIELDS_PRESENT = BIT(5),
+};
+
+/*
+ * enum qca_wlan_dar_observed_cu_fractional_fields_attr - Represents the
+ * possible parameters inside the attribute
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL
+ *
+ * @QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY: Optional u8
+ * attribute, indicates the granularity level of the Observed CU Fraction List
+ * field. Values are defined in enum qca_wlan_dar_link_granularity.
+ *
+ * @QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_THRESHOLDS: An array of
+ * possible 8-bit values, indexed by the link identifier. List of thresholds
+ * each of which is channel utilization fraction normalized to 255. If the
+ * granularity is at device level only one value will be included in this list.
+ */
+enum qca_wlan_dar_observed_cu_fractional_fields_attr {
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY = 1,
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_THRESHOLDS = 2,
+
+ /* keep last */
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_MAX =
+ QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_mpdu_count_stats_field_attr - Represents the possible
+ * radio statistics attribute configuration for
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS.
+ *
+ * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY: u8 attribute,
+ * Indicates the granularity level of the MPDU count statistics attribute field.
+ * values are defined in enum qca_wlan_dar_report_granularity.
+ *
+ * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY: u8 attribute,
+ * Indicates the granularity level of the MPDU count stats field.
+ * Values are defined in enum qca_wlan_dar_link_granularity.
+ *
+ * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_THRESHOLDS: An array of unsigned 8-bit
+ * values. Indexed by the link identifier. List of thresholds each of which is
+ * a one-octet percentile MPDU Retry Rate, equal to {100 * MPDU Retry Count /
+ * Total MPDU Count}, rounded to the nearest integer. If the Total MPDU Count
+ * for a measurement is zero, then the threshold is considered to not have been
+ * exceeded for that measurement. If the granularity is at device level only one
+ * value will be included in this list.
+ */
+enum qca_wlan_dar_mpdu_count_stats_field_attr {
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY = 1,
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY = 2,
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_THRESHOLDS = 3,
+
+ /* keep last */
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_AFTER_LAST,
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_MAX =
+ QCA_WLAN_DAR_MPDU_COUNT_STATS_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_fcs_failure_fields_attr - Represents the possible
+ * parameters inside the attribute
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE.
+ *
+ * @QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY: u8 attribute,
+ * Indicates the granularity level of the FCS failure link field.
+ * Values are defined in enum qca_wlan_dar_link_granularity.
+ */
+enum qca_wlan_dar_fcs_failure_fields_attr {
+ QCA_WLAN_DAR_FCS_FAILURE_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY = 1,
+
+ /* keep last */
+ QCA_WLAN_DAR_FCS_FAILURE_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_FCS_FAILURE_ATTR_MAX =
+ QCA_WLAN_DAR_FCS_FAILURE_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_rts_stats_field_attr - Represents the possible
+ * radio stats attribute configuration for
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS.
+ *
+ * @QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY: Optional u8 attribute,
+ * Indicates the granularity level of the RTS statistics attribute field.
+ * Values are defined in enum qca_wlan_dar_report_granularity.
+ *
+ * @QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY: Optional u8 attribute,
+ * Indicates the granularity level of the RTS stats field.
+ * Values are defined in enum qca_wlan_dar_link_granularity.
+ *
+ * @QCA_WLAN_DAR_RTS_STATS_ATTR_THRESHOLDS: An array of unsigned 8-bit values.
+ * Indexed by the link identifier. List of thresholds each of which is a
+ * one-octet percentile RTS Failure Rate, equal to {100 * RTS Failure Count /
+ * Total RTS Count}, rounded to the nearest integer. If the Total RTS Count for
+ * a measurement is zero, then the threshold is considered to not have been
+ * exceeded for that measurement. If the granularity is at device level only one
+ * value will be included in this list.
+ */
+enum qca_wlan_dar_rts_stats_field_attr {
+ QCA_WLAN_DAR_RTS_STATS_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY = 1,
+ QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY = 2,
+ QCA_WLAN_DAR_RTS_STATS_ATTR_THRESHOLDS = 3,
+
+ /* keep last */
+ QCA_WLAN_DAR_RTS_STATS_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_RTS_STATS_ATTR_MAX =
+ QCA_WLAN_DAR_RTS_STATS_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_radio_counters_attr - Represents the possible
+ * radio counters attribute configuration for
+ * %QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP: Mandatory u16
+ * attribute, indicates the Radio Counter fields that are requested to the peer
+ * in the DAR request frame.
+ * Values are defined in enum qca_wlan_dar_radio_counter_fields.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL:
+ * Optional nested attribute to configure the parameters that will be included
+ * in observed channel utilization fractional field.
+ * This attribute is included only when
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD bit is set in the
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP.
+ * The attributes used inside this nested attribute is defined in enum
+ * qca_wlan_dar_observed_cu_fractional_fields_attr.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS: Optional nested attribute
+ * to configure the parameters that will be included in MPDU Count statistics
+ * field.
+ * This attribute is included only when
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD bit is set in the
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP.
+ * The attribute used inside this nested attribute is defined in enum
+ * qca_wlan_dar_mpdu_count_stats_field_attr.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS: Optional nested attribute to
+ * configure the parameters that will be included in RTS statistics field.
+ * This attribute is included only when
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD bit is set in the radio
+ * counters presence field bitmap.
+ * The attribute used inside this nested attribute is defined in enum
+ * qca_wlan_dar_rts_stats_field_attr.
+ *
+ * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE: Optional nested attribute to
+ * configure the parameters that will be included in FCS failure statistics
+ * field.
+ * This attribute is included only when
+ * %QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD bit is set in the radio
+ * counters presence field bitmap.
+ * The attribute used inside this nested attribute is defined in enum
+ * qca_wlan_dar_fcs_failure_fields_attr.
+ */
+enum qca_wlan_dar_radio_counters_attr {
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP = 1,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL = 2,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS = 3,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS = 4,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE = 5,
+
+ /* keep last */
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MAX =
+ QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_tuple_codes_attr - Represents the possible control plane
+ * events tuple code parameters in a request for
+ * %QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES.
+ *
+ * @QCA_WLAN_DAR_TUPLE_CODES_ATTR_CATEGORY_CODE: Optional u8 attribute.
+ * Indicates the category of the control plane event as defined in Wi-Fi QoS
+ * Management Specification Version 3.0 + WorkingR4, Table 32. Control Plane
+ * Event Codes.
+ * @QCA_WLAN_DAR_TUPLE_CODES_ATTR_SUBCATEGORY_CODE: Optional u8 attribute
+ * Indicates the subcategory of the control plane event as defined in Wi-Fi QoS
+ * Management Specification Version 3.0 + WorkingR4, Table 32. Control Plane
+ * Event Codes.
+ */
+enum qca_wlan_dar_tuple_codes_attr {
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_CATEGORY_CODE = 1,
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_SUBCATEGORY_CODE = 2,
+
+ /* keep last */
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_MAX =
+ QCA_WLAN_DAR_TUPLE_CODES_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_control_plane_events_attr - Represents the possible control
+ * plane events attribute configuration for
+ * %QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG.
+ *
+ * @QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT: u8 attribute.
+ * This attribute specifiess the number of control plane event tuples.
+ * @QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES: Optional array of nested
+ * attributes specified by enum qca_wlan_dar_tuple_codes_attr. The size of the
+ * array specified by %QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT
+ * attribute.
+ */
+enum qca_wlan_dar_control_plane_events_attr {
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_INVALID = 0,
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT = 1,
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES = 2,
+
+ /* keep last */
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_AFTER_LAST,
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_MAX =
+ QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_dar_stats_types - Represents the type of the DAR statistics
+ * attribute for %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP.
+ *
+ * @QCA_WLAN_DAR_STATS_TYPE_LATENCY: Indicates that latency statistic attribute
+ * is requested in DAR request frame and DAR session terminate in the DAR
+ * response attribute.
+ * @QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS: Indicates that radio counters
+ * statistic attribute is requested in DAR request frame and DAR session
+ * terminate in the DAR response attribute.
+ * @QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE: Indicates that control plane
+ * statistic attribute is requested in DAR request frame and DAR session
+ * terminate in the DAR response attribute.
+ */
+enum qca_wlan_dar_stats_types {
+ QCA_WLAN_DAR_STATS_TYPE_LATENCY = BIT(0),
+ QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS = BIT(1),
+ QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE = BIT(2),
+};
+
+/*
+ * enum qca_wlan_dar_report_method - Specifies the report method for the
+ * DAR Request attribute %QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD.
+ *
+ * @QCA_WLAN_DAR_REPORT_PERIODIC: Specifies that the report should be sent at
+ * regular intervals based on the defined periodicity, supporting continuous
+ * monitoring.
+ * @QCA_WLAN_DAR_REPORT_ON_DEMAND: Specifies that the report should be sent
+ * immediately or after the measurement duration, upon request by the initiator.
+ * @QCA_WLAN_DAR_REPORT_TRIGGERED: Specifies that the report should be sent when
+ * a predefined trigger condition is met, enabling event-driven reporting.
+ */
+enum qca_wlan_dar_report_method {
+ QCA_WLAN_DAR_REPORT_PERIODIC = 0,
+ QCA_WLAN_DAR_REPORT_ON_DEMAND = 1,
+ QCA_WLAN_DAR_REPORT_TRIGGERED = 2,
+};
+
+/*
+ * enum qca_wlan_dar_op_type - Specifies the type of DAR operation for
+ * %QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE attribute.
+ *
+ * @QCA_WLAN_DAR_OP_TYPE_REQUEST_ADD: Triggers the driver to fill the request
+ * type as add in the DAR request attribute of the QoS management element sent
+ * in the DAR request frame.
+ * @QCA_WLAN_DAR_OP_TYPE_REQUEST_REMOVE: Triggers the driver to fill the
+ * request type as remove in the DAR request attribute of the QoS Management
+ * element sent in the DAR request frame.
+ * @QCA_WLAN_DAR_OP_TYPE_RESPONSE_TERMINATE: Triggers the driver to fill the
+ * status code as terminate DAR session in the DAR response attribute of the
+ * QoS management element sent in the DAR response frame.
+ */
+enum qca_wlan_dar_op_type {
+ QCA_WLAN_DAR_OP_TYPE_REQUEST_ADD = 0,
+ QCA_WLAN_DAR_OP_TYPE_REQUEST_REMOVE = 1,
+ QCA_WLAN_DAR_OP_TYPE_RESPONSE_TERMINATE = 2,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_dar: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_DAR.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE: Mandatory u8 attribute to specify the type
+ * of DAR operation in request/response attributes. Values are defined in enum
+ * qca_wlan_dar_op_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP: Mandatory u8 attribute. Bitmap of
+ * various DAR request attribute types. Refer the enum qca_wlan_dar_stats_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD: Optional u8 attribute. This
+ * attribute specifies the reporting method of the DAR Request attribute. It can
+ * be either periodic, on-demand, or triggered. Refer enum
+ * qca_wlan_dar_report_method.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENT_DURATION: Optional u16 attribute.
+ * This attribute specifies the requested measurement period in units of ms.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENTS: Optional u16 attribute. This
+ * attribute indicates the non-zero value for the requested number of
+ * measurements.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG: Optional nested attribute
+ * to configure latency statistic attribute. This attribute is included only
+ * when %QCA_WLAN_DAR_STATS_TYPE_LATENCY is set in
+ * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP.
+ * The attributes used inside this nested attribute are defined in enum
+ * qca_wlan_dar_stats_latency_attr.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG: Optional nested attribute to
+ * configure radio counters attribute. This attribute is included only when
+ * %QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS is set in
+ * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP.
+ * The attributes used inside this nested attribute is defined in enum
+ * qca_wlan_dar_radio_counters_attr.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG: Optional nested
+ * attribute to configure. This attribute is included only when
+ * %QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE is set in the
+ * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP.
+ * The attributes used inside this nested attribute is defined in enum
+ * qca_wlan_dar_control_plane_events_attr.
+ */
+enum qca_wlan_vendor_attr_dar {
+ QCA_WLAN_VENDOR_ATTR_DAR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP = 2,
+ QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD = 3,
+ QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENT_DURATION = 4,
+ QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENTS = 5,
+ QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG = 6,
+ QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG = 7,
+ QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DAR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DAR_MAX =
+ QCA_WLAN_VENDOR_ATTR_DAR_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_vendor_attr_feature_config_capability - Represents the attributes
+ * sent nested under the attribute
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_NSS_MASK - Optional u8
+ * attribute. Represents the bit mask of allowed NSS capabilities for the AP.
+ * Bit index starts from the least significant bit and Nth bit in the bitmask
+ * represents the (N + 1) NSS capability.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HT - Flag attribute. The AP
+ * must advertise HT Capability in its Beacon and Probe Response frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_VHT - Flag attribute. The AP
+ * must advertise VHT Capability in its Beacon and Probe Response frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HE - Flag attribute. The AP
+ * must advertise HE Capability in its Beacon and Probe Response frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_EHT - Flag attribute. The AP
+ * must advertise EHT Capability in its Beacon and Probe Response frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_BAND_MASK - Optional u32
+ * attribute. Carries bitmask value of bits from enum qca_set_band. This
+ * represents the allowed operating bands for the AP.
+ */
+enum qca_vendor_attr_feature_config_capability {
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_NSS_MASK = 1,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HT = 2,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_VHT = 3,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HE = 4,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_EHT = 5,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_BAND_MASK = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_MAX =
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_feature_config_data - This enum is used by
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. The attributes in this enum
+ * can be used to represent a configuration data entry to be matched against the
+ * contents advertised by the AP in its Beacon and Probe Response frames. All
+ * the specified attributes in the configuration data entry must be matched to
+ * perform the action specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI: Optional attribute
+ * of u8 array. The length of this attribute can be 3 or 5 bytes. Represents
+ * the vendor OUI to be matched against the OUI field in the Vendor Specific
+ * elements advertised by the AP in its Beacon and Probe Response frames.
+ * Userspace can optionally configure
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA and
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK along with
+ * this attribute to match the Vendor Specific element data after the OUI field.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA: Optional attribute
+ * of u8 array. Represents Vendor Specific element data to be matched after the
+ * OUI field in Vendor Specific elements when the OUI field matched the OUI
+ * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI. If this
+ * is present, userspace must configure the data mask also using
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK: Optional
+ * attribute of u8 array. Represents the data mask buffer for the data specified
+ * in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA. This is mandatory
+ * when %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA is present. The
+ * length of this attribute must be same as the length of
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID: Optional attribute.
+ * Represents the 6-byte MAC address to be matched with AP's BSSID. If this
+ * is present, userspace must configure the BSSID mask also using
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK: Optional attribute.
+ * Represents the 6-byte MAC address mask for the BSSID specified with
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID. This is mandatory when
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY: Optional nested
+ * attribute. Represents AP capabilities to be matched. The AP must support all
+ * the nested capabilities specified in this attribute to perform the action
+ * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. The attributes
+ * used with this are defined in enum qca_vendor_attr_feature_config_capability.
+ */
+enum qca_wlan_vendor_attr_feature_config_data {
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI = 1,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA = 2,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK = 3,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID = 4,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK = 5,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_MAX =
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_feature_config_action - Represents the values of the
+ * attribute %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS - Enable dynamic SMPS
+ * (Spatial Multiplex Power Save) only when contents advertised by AP in its
+ * Beacon and Probe Response frames matches with at least one of the
+ * configuration data entry from the array of configuration data entries
+ * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. Otherwise, the
+ * driver must disable DSMPS for the AP. When this action specified in the
+ * subcommand request, existing configuration data of the previous subcommand
+ * requests with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS will be
+ * cleared since the driver anyway needs to disable DSMPS for all the APs which
+ * do not meet enable criteria. If the default driver configuration itself
+ * enables DSMPS already and the subcommand request issued to add configuration
+ * data entries with this action the driver will enable the DSMPS only for the
+ * APs which meet the enable criteria and disable DSMPS for all other APs after
+ * the configuration.
+ *
+ * Interaction with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI:
+ * If %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI is also
+ * configured and the connected AP matches the adaptive action’s
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST, the adaptive logic may
+ * disable DSMPS when the AP RSSI is below the firmware-provided threshold, even
+ * if this static action would otherwise enable DSMPS for that AP.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS - Disable dynamic SMPS
+ * only when contents advertised by the AP in its Beacon and Probe Response
+ * frames matches at least one of the configuration data entry from the array of
+ * the configuration data entries specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. Otherwise, the driver must
+ * enable DSMPS for the AP. When this action specified in the subcommand
+ * request, existing configuration data entries of the previous subcommand
+ * requests with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS will be
+ * cleared since the driver anyway needs to enable DSMPS for all the APs which
+ * do not meet disable criteria. If the default driver configuration itself
+ * disables DSMPS already and the subcommand request issued to add configuration
+ * data entries with this action the driver will disable the DSMPS only for
+ * the APs which meet the disable criteria and enable DSMPS for all other APs
+ * after the configuration.
+ *
+ * Interaction with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI:
+ * If an AP is present in the
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS list, DSMPS remains
+ * disabled for that AP regardless of any
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI or RSSI
+ * changes.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI - Enable
+ * Dynamic Spatial Multiplexing Power Save (DSMPS) adaptively based on AP RSSI.
+ * This action enables DSMPS only when the Access Point (AP) signal strength
+ * (RSSI) exceeds a predefined threshold after a connection is established. The
+ * driver activates or deactivates DSMPS dynamically during the association,
+ * based on whether the AP’s RSSI is above or below the threshold. The RSSI
+ * threshold is determined by the default configuration set by the firmware.
+ * DSMPS is activated or deactivated only if the information advertised in the
+ * AP’s Beacon and Probe Response frames matches at least one entry from the
+ * configuration data list specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. The DSMPS state is
+ * continuously evaluated during the association. If the AP's RSSI crosses the
+ * threshold (either above or below), the driver will enable or disable DSMPS
+ * accordingly, as long as the AP matches the configuration data list.
+ *
+ * Interaction with other actions:
+ * - %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI can co-exist
+ * with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS and
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS.
+ * - When AP matches with the configuration data of the both
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS and
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI, DSMPS can be
+ * disabled due to signal strength being below the RSSI threshold even if the
+ * AP matches the enable list.
+ * - The %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI action
+ * takes effect only for APs not matching with the configuration data of
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS. For APs in the
+ * disable list, DSMPS remains disabled regardless of RSSI.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2:
+ * Enable connections using more than two spatial streams (RX or TX) only if
+ * the AP’s Beacon and Probe Response frames include information that matches
+ * at least one entry from the configuration data list specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST.
+ * If no match is found, the driver must restrict the connection to two spatial
+ * streams, even if the AP supports more than two spatial streams.
+ *
+ * Interaction with other actions:
+ * - If a new configuration with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2
+ * is specified, any existing configuration with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 will be cleared.
+ * - If neither %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 nor
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 is configured, the
+ * driver follows its default NSS negotiation logic.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2:
+ * Restrict connections to a maximum of two spatial streams (RX or TX) if the
+ * AP’s Beacon and Probe Response frames include information that matches at
+ * least one entry from the configuration data list specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST.
+ * If no match is found, the driver may allow more than two spatial streams,
+ * provided the AP supports them.
+ *
+ * Interaction with other actions:
+ * - If a new configuration with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2
+ * is specified, any existing configuration with
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 will be cleared.
+ * - If neither %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 nor
+ * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 is configured, the
+ * driver follows its default NSS negotiation logic.
+ */
+
+enum qca_wlan_vendor_feature_config_action {
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS = 0,
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS = 1,
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI = 2,
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 = 3,
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_feature_config_data_op - Represents values of the
+ * attribute %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_ADD - This operation appends the
+ * configuration data entries specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST to the existing configuration
+ * data entries for the action specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ * The existing configuration data entries may have been set through previous
+ * subcommand requests for the action specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_CLEAR - This operation clears the
+ * existing configuration data entries of the action specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. The existing configuration data
+ * entries may have been set through previous subcommand requests for the action
+ * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ */
+enum qca_wlan_vendor_feature_config_data_op {
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_ADD = 0,
+ QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_CLEAR = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_feature_config - This enum is used by
+ * %QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION: Mandatory u32 attribute.
+ * Possible values for this attribute are defined in the
+ * enum qca_wlan_vendor_feature_config_action. This represents the action to be
+ * performed by the driver when the contents advertised by the AP in its Beacon
+ * and Probe Response frames matches at least one of the configuration data
+ * entries from the list of the configuration data entries specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST: Array of nested attributes.
+ * Each nested attribute set in the array represents a configuration data entry
+ * to be matched against the contents advertised by the AP in its Beacon and
+ * Probe Response frames. The driver needs to perform the action indicated in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION when the AP matches at least one
+ * of the configuration data entries specified in the list of configuration data
+ * entries. The attributes in enum qca_wlan_vendor_attr_feature_config_data can
+ * be used to represent a configuration data entry to be matched against the
+ * contents advertised by the AP in its Beacon and Probe Response frames. All
+ * the specified attributes in a single configuration data entry must be matched
+ * to perform the action specified in
+ * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION.
+ * This is a mandatory attribute when %QCA_WLAN_VENDOR_FEATURE_CONFIG_INFO_ADD
+ * is indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INFO_OP. This attribute
+ * shall not be present when %QCA_WLAN_VENDOR_FEATURE_CONFIG_INFO_CLEAR is
+ * indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INFO_OP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP: Mandatory u8 attribute. This
+ * indicates the operation to be performed on the configuration data entries
+ * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST for the action
+ * indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. Possible values for
+ * this attribute is defined in enum qca_wlan_vendor_feature_config_data_op.
+ */
+enum qca_wlan_vendor_attr_feature_config {
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION = 1,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST = 2,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_ops - Defines attributes to be used
+ * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_RADIO_INDEX: Mandatory u32 attribute.
+ * An attribute that represents the index of the radio for which ATF
+ * configuration is received. Radio index is fetched from
+ * NL80211_WIPHY_RADIO_ATTR_INDEX, which is part of the
+ * NL80211_ATTR_WIPHY_RADIOS.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_ENABLED: u8 attribute.
+ * An attribute used to enable or disable ATF offload.
+ * 0 - Disable.
+ * 1 - Enable.
+ * Note: The ATF offload must be enabled first by setting this to 1.
+ * Only if this attribute is enabled, the remaining ATF configuration
+ * attributes will be applied. If ATF offload is disabled (value 0),
+ * all other configuration attributes will be ignored.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG: Nested attribute.
+ * This attribute is used to send SSID group configuration information.
+ * Each SSID group must contain at least one SSID, and each group can have
+ * its own distinct configuration.
+ * The configuration for each SSID group is provided as a nested attribute.
+ * These nested attributes are defined in
+ * enum qca_wlan_vendor_attr_atf_offload_ssid_group_config.
+
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG: Nested attribute.
+ * This attribute is used to send configuration for each WMM Access
+ * Category (AC) within an SSID group. The attributes defined in
+ * enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config are nested in
+ * this attribute.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG: Nested attribute.
+ * Indicates the airtime configuration of each peer associated to the radio.
+ * The attributes defined in enum qca_wlan_vendor_attr_atf_offload_peer_config
+ * are nested in this attribute.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STATS_ENABLED: u8 attribute
+ * Indicates whether ATF offload statistics are enabled or disabled.
+ * 0 - Disable.
+ * 1 - Enable.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STRICT_SCHEDULING_ENABLED: u8 attribute.
+ * Indicates the ATF strict scheduling to be enabled or disabled.
+ * 0 - Disable.
+ * 1 - Enable.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VO_DEDICATED_TIME_CONFIG: u16 attribute.
+ * An attribute used to dedicate time slot for Voice (VO) within every
+ * one second window. The value is between 0 to 1000 milliseconds.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VI_DEDICATED_TIME_CONFIG: u16 attribute.
+ * An attribute used to dedicate time slot for Video (VI) within every one
+ * second window. The value is between 0 to 1000 milliseconds.
+ *
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY: Nested attribute
+ * representing the ATF scheduling policy of the interface. This attribute is
+ * applicable for an SSID, allowing the firmware to apply the scheduling policy
+ * directly at the interface level. The attributes defined in enum
+ * qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy are nested in this
+ * attribute.
+ */
+enum qca_wlan_vendor_attr_atf_offload_ops {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_RADIO_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_ENABLED = 2,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG = 3,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG = 4,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG = 5,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STATS_ENABLED = 6,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STRICT_SCHEDULING_ENABLED = 7,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VO_DEDICATED_TIME_CONFIG = 8,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VI_DEDICATED_TIME_CONFIG = 9,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY = 10,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_MAX =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_ssid_group_config -
+ * Defines attributes to be used with vendor attribute
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INDEX: Mandatory
+ * u8 attribute. Indicates the unique index of the SSID group.
+ * These indexes are assigned based on the order in which the SSID
+ * groups are configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_AIRTIME_CONFIGURED:
+ * Mandatory u16 attribute. Indicates the percentage of airtime configured by
+ * the user for the SSID group. The value is represented as a fixed-point
+ * integer with one digit after the decimal point.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_POLICY: Mandatory
+ * u8 attribute. Indicates the scheduling policy of the SSID group.
+ * Attribute value range is 0 to 2.
+ * 0 - Fair scheduling, the SSID group can contribute its
+ * unused airtime with other SSID groups and can also borrow from them.
+ * 1 - Strict scheduling, the SSID group can contribute
+ * its unused airtime with other SSID groups but cannot be borrowed.
+ * 2 - Fair with upper bound, the SSID group can only contribute
+ * its unused airtime, but cannot borrow airtime from other SSID groups.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS:
+ * Mandatory u16 attribute. Indicates the number of peers that are associated
+ * but do not have airtime configured by user.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIGURED_PEERS:
+ * Mandatory u16 attribute. Indicates the number of peers that are associated
+ * and have airtime configured by user. Note: The total number of associated
+ * peers to the SSID group is the sum of configured and unconfigured peers.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS_AIRTIME:
+ * Mandatory u16 attribute. Indicates the percentage of total airtime allocated
+ * for unconfigured peers within the SSID group. The value is represented as
+ * a fixed-point integer with one digit after the decimal point.
+ */
+enum qca_wlan_vendor_attr_atf_offload_ssid_group_config {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_AIRTIME_CONFIGURED = 2,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_POLICY = 3,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS = 4,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIGURED_PEERS = 5,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS_AIRTIME = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_LAST,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_MAX =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config -
+ * Defines attributes to be used with vendor attribute
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_GROUP_INDEX: Mandatory u8 attribute.
+ * Indicates the unique index of the SSID group. These indexes are assigned
+ * based on the order in which the SSID groups are configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BE_AIRTIME: Mandatory u16 attribute.
+ * Indicates the airtime percentage configured for the Best Effort (BE) WMM
+ * Access Category of SSID group. The value is represented as a fixed-point
+ * integer with one digit after the decimal point.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BK_AIRTIME: Mandatory u16 attribute.
+ * Indicates the airtime percentage configured for the Background (BK) WMM
+ * Access Category of SSID group. The value is represented as a fixed-point
+ * integer with one digit after the decimal point.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VI_AIRTIME: Mandatory u16 attribute.
+ * Indicates the airtime percentage configured for the Video (VI) WMM Access
+ * Category of SSID group. The value is represented as a fixed-point integer
+ * with one digit after the decimal point.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VO_AIRTIME: Mandatory u16 attribute.
+ * Indicates the airtime percentage configured for the Voice (VO) WMM Access
+ * Category of SSID group. The value is represented as a fixed-point integer
+ * with one digit after the decimal point.
+ */
+enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_GROUP_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BE_AIRTIME = 2,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BK_AIRTIME = 3,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VI_AIRTIME = 4,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VO_AIRTIME = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_LAST,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_MAX =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_peer_config - Defines
+ * attributes to be used with vendor attribute
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_FULL_UPDATE: Mandatory NLA_FLAG
+ * attribute. When included, the configuration update applies to all currently
+ * connected peers. If not include, the update applies only to newly connected
+ * peers.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MORE: Mandatory NLA_FLAG
+ * attribute. Indicates that the current
+ * QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS command does not include
+ * configuration data for all connected peers. When this flag is included,
+ * it signals that additional command(s) will follow, each carrying the
+ * remaining peer configurations using the
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_PAYLOAD: Mandatory nested
+ * attribute. Indicates each peer's configuration associated with the radio.
+ * The attributes defined in enum qca_wlan_vendor_attr_atf_offload_peer are
+ * nested in this attribute.
+ */
+enum qca_wlan_vendor_attr_atf_offload_peer_config {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_FULL_UPDATE = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MORE = 2,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_PAYLOAD = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_LAST,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_peer - Defines
+ * attributes to be used with vendor attribute
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_PAYLOAD.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAC: MAC address mandatory attribute.
+ * Indicates the MAC address of the peer or link peer in case of MLO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_AIRTIME: Mandatory u16 attribute.
+ * Indicates the percentage of airtime configured for the peer. The airtime
+ * assigned to a peer is relative to the SSID group's total airtime allocation.
+ * The value is represented as a fixed-point integer with one digit after the
+ * decimal point.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_GROUP_INDEX: Mandatory u8 attribute.
+ * Indicates the index of the SSID group to which the peer belongs.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIGURED: Mandatory NLA_FLAG
+ * attribute. This flag is included if the peer has explicitly configured
+ * airtime by user.
+ */
+enum qca_wlan_vendor_attr_atf_offload_peer {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAC = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_AIRTIME = 2,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_GROUP_INDEX = 3,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIGURED = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAX,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_LAST =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAX - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy -
+ * Defines attributes to be used with vendor attribute
+ * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LINK_ID: u8 attribute.
+ * Mandatory attribute when interface is configured in Multi Link Operation
+ * (MLO). This attribute must not be included in non-MLO scenarios. This is the
+ * link ID of the interface in the MLO case. Possible values are 0 to 14.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHEDULING: Mandatory u8 attribute.
+ * An attribute used to define the scheduling policy.
+ * The accepted values for the attribute are
+ * 0 - fair, 1 - strict, 2 - fair with upper bound.
+ */
+enum qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy {
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LINK_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHEDULING = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LAST,
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_MAX =
+ QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_cca_stat - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_LINK_ID - u8 attribute. This is mandatory
+ * attribute in case of MLO connection and represents the link ID of the
+ * affiliated AP/link. For non-MLO connection, this attribute will not be
+ * present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME - u32 attribute, which represents
+ * total number of milliseconds the radio is monitored with wideband energy
+ * detection feature on the whole affiliated AP/link channel width accruing
+ * over time, corresponding to the respective channel.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_BUSY_TIME - u32 attribute, representing total
+ * number of milliseconds the the CCA register is busy accruing over time
+ * corresponding to the whole affiliated AP/link channel width where radio was
+ * awake for @QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME.
+ */
+enum qca_wlan_vendor_attr_cca_stat {
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_LINK_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME = 2,
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_BUSY_TIME = 3,
+
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_MAX =
+ QCA_WLAN_VENDOR_ATTR_CCA_STAT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_beacon_miss_stat - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_LINK_ID - u8 attribute. This is
+ * mandatory attribute in case of MLO connection and represents the link ID of
+ * the affiliated AP/link. For non-MLO connection, this attribute will not be
+ * present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_DATA - Mandatory byte array attribute.
+ * This attribute indicates the record of beacon miss data when the STA is
+ * operating on the affiliated AP/link channel. Each bit in the byte array
+ * corresponds to a TBTT slot: 0 means the beacon was received, 1 means it was
+ * missed. The least significant bit of the first byte of the byte array
+ * indicates the most recent TBTT slot's status. The length of this attribute
+ * depends on the maximum number of TBTT slot statuses that hardware is capable
+ * of reporting.
+ */
+enum qca_wlan_vendor_attr_beacon_miss_stat {
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_LINK_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_DATA = 2,
+
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_MAX =
+ QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dcs - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID: 8-bit unsigned value for link ID.
+ * Specifies which link to set/get in a multi-link setup.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE: 8-bit unsigned value for DCS command type
+ * 0: GET – Retrieve the current DCS configuration.
+ * Userspace must provide QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE.
+ * QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID is also required in multi-link AP
+ * scenarios.
+ * The driver will return the following attributes:
+ * QCA_WLAN_VENDOR_ATTR_DCS_ENABLE
+ * QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD
+ * QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY
+ * QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD
+ * QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD
+ * QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD
+ * QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW
+ * QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD
+ * QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU
+ * 1: SET – Update the DCS configuration.
+ * Userspaxce must provide QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE.
+ * QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID is also required in multi-link AP
+ * scenarios.
+ * One or more of the above attributes must be included with new
+ * values to apply the configuration update.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_ENABLE: 16-bit bitmap to set/get enable/disable DCS
+ * bit 0: Enable Continuous Wave Interference Management (CW IM)
+ * bit 1: Enable WLAN Interference Management (WLAN IM)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD: 32-bit unsigned
+ * value to set/get interference detection threshold. This attribute specifies
+ * the number of interference events required to trigger a channel switch.
+ * Higher values decrease sensitivity, making DCS less likely to switch.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY: 32-bit unsigned value to set/get
+ * the PHY error penalty. This value specifies the amount of channel time
+ * (in microseconds) counted as wasted for each PHY error when estimating
+ * interference.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD: 32-bit unsigned value to set/get
+ * the PHY error count threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD: 32-bit unsigned value to
+ * set/get radar error count threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD: 32-bit unsigned value to set/get
+ * TX error count threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW: 32-bit unsigned
+ * value to set/get the interference detection sampling window. The unit is a
+ * count of sampling intervals, where each interval corresponds to one second.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD: 8-bit unsigned
+ * value to set/get the co-channel interference threshold level, interpreted as
+ * a percentage of channel time affected by same-channel interference.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU: 8-bit unsigned value to set/get the maximum
+ * channel utilization percentage allowed. If the combined TX and RX channel
+ * utilization exceeds this configured maximum CU, treats the condition as WLAN
+ * interference.
+ */
+enum qca_wlan_vendor_attr_dcs {
+ QCA_WLAN_VENDOR_ATTR_DCS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_DCS_ENABLE = 3,
+ QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD = 4,
+ QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY = 5,
+ QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD = 6,
+ QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD = 7,
+ QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD = 8,
+ QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW = 9,
+ QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD = 10,
+ QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU = 11,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_DCS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_DCS_MAX =
+ QCA_WLAN_VENDOR_ATTR_DCS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_qsh_stats - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_QSH_STATS_SCAN_COUNT: 32-bit unsigned value
+ * representing the Wi-Fi scan count from the sensor. This attribute is
+ * mandatory. It's a response-only attribute.
+ */
+enum qca_wlan_vendor_attr_qsh_stats {
+ QCA_WLAN_VENDOR_ATTR_QSH_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_QSH_STATS_SCAN_COUNT = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_QSH_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_QSH_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_QSH_STATS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_host_txrx_stats_attr - Defines attributes to be used
+ * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_PKTS: 32-bit unsigned value
+ * for total TX packets successfully transmitted to the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_DROPPED: 32-bit unsigned
+ * value for total TX packets dropped by the WLAN host driver (e.g., due
+ * to queue exhaustion).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_PKTS: 32-bit unsigned value
+ * for total RX packets successfully delivered to the network stack.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_DROPPED: 32-bit unsigned
+ * value for total RX packets dropped by the WLAN host driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_IPA_EXCEPTION_RX_DROPPED:
+ * 64-bit unsigned value for IPA exception RX drops. Counts packets
+ * routed from IPA to WLAN driver that were dropped.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_PAD: Attribute used for padding
+ * for 64-bit alignment.
+ */
+
+enum qca_wlan_host_txrx_stats_attr {
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_PKTS = 1,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_DROPPED = 2,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_PKTS = 3,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_DROPPED = 4,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_IPA_EXCEPTION_RX_DROPPED = 5,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_PAD = 6,
+
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_LAST,
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_esp_param - Parameters for ESP configuration
+ *
+ * Attributes used to configure (set command) or report (get command)
+ * Estimated Service Parameters (ESP). ESP describes predicted service
+ * characteristics such as airtime availability, PPDU duration, and Block Ack
+ * window size per access category and it is advertised in the Estimated Service
+ * Parameters Inbound element (see IEEE Std 802.11-2024, 9.4.2.172).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_ENABLE: u8. Enable (1) or disable (0)
+ * the advertisement of the Estimated Service Parameters Inbound element
+ * in Beacon and Probe Response frames.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BE: u8. Predicted percentage of
+ * airtime available for AC_BE. This is the exact 8-bit value used in the ESP
+ * Information field as defined in IEEE Std 802.11-2024, 9.4.2.172. The
+ * value is linearly scaled: 0 represents 0% airtime, 255 represents 100%
+ * airtime.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BE: u8. BE Access Category "target"
+ * duration for PPDUs that carry Data MPDUs, i.e., how long the AP expects a
+ * typical data transmission PPDU to last for the BE AC. Encoded in unts of 50
+ * microseconds (actual_duration_us = value × 50) as defined in IEEE Std
+ * 802.11-2024, 9.4.2.172.
+ * Range: 0..255 units (0..12.75 ms). Example: value 16 -> 800 microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BE: u8. BA Window Size subfield
+ * for AC_BE as defined in IEEE Std 802.11-2024, Table 9-334 (BA Window Size
+ * subfield encoding).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BK: u8. Airtime for AC_BK.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BK: u8. PPDU duration for AC_BK.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BK: u8. BA window for AC_BK.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VI: u8. Airtime for AC_VI.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VI: u8. PPDU duration for AC_VI.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VI: u8. BA window for AC_VI.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VO: u8. Airtime for AC_VO.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VO: u8. PPDU duration for AC_VO.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VO: u8. BA window for AC_VO.
+ */
+enum qca_wlan_vendor_attr_config_esp_param {
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_ENABLE = 1,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BE = 2,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BE = 3,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BE = 4,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BK = 5,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BK = 6,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BK = 7,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VI = 8,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VI = 9,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VI = 10,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VO = 11,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VO = 12,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VO = 13,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_ipa_ring_stats_attr - Defines attributes to be used
+ * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ID: 32-bit unsigned value
+ * for the ring identifier (ring_id). Distinguishes between different
+ * rings (e.g., REO, TCL).
+ * In the request, this specifies which ring to query.
+ * In the response, this identifies the ring the statistics belong to.
+ * Values are defined in enum qca_ipa_ring_stats_id.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_HP_IDX: 32-bit unsigned value
+ * for the head pointer index (entry index) of the ring.
+ * (Response only)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_TP_IDX: 32-bit unsigned value
+ * for the tail pointer index (entry index) of the ring.
+ * (Response only)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_FREE_ENTRIES: 32-bit unsigned
+ * value for the number of free entries available in the ring.
+ * (Response only)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ENTRIES: 32-bit unsigned
+ * value for the total number of entries in the ring (capacity).
+ * (Response only)
+ */
+enum qca_wlan_ipa_ring_stats_attr {
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_HP_IDX = 2,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_TP_IDX = 3,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_FREE_ENTRIES = 4,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ENTRIES = 5,
+
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_LAST,
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_LAST - 1,
+};
+
+/**
+ * enum qca_ipa_ring_stats_id - IPA statistics ring type
+ *
+ * @QCA_WLAN_VENDOR_IPA_RING_STATS_ID_REO: Ring ID for REO (Rx) ring.
+ * @QCA_WLAN_VENDOR_IPA_RING_STATS_ID_TCL: Ring ID for TCL (Tx) ring.
+ *
+ * These values are used to distinguish which IPA path statistics
+ * are being queried or reported.
+ */
+enum qca_ipa_ring_stats_id {
+ QCA_WLAN_VENDOR_IPA_RING_STATS_ID_REO = 0,
+ QCA_WLAN_VENDOR_IPA_RING_STATS_ID_TCL = 1,
+
+ QCA_WLAN_VENDOR_IPA_RING_STATS_ID_MAX,
+};
+
+/**
+ * enum qca_wlan_vendor_external_auth_action - Action to perform with
+ * external authentication request. Used by
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION.
+ *
+ * @QCA_WLAN_VENDOR_EXTERNAL_AUTH_START: Start the authentication.
+ * @QCA_WLAN_VENDOR_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+ *
+ */
+enum qca_wlan_vendor_external_auth_action {
+ QCA_WLAN_VENDOR_EXTERNAL_AUTH_START = 0,
+ QCA_WLAN_VENDOR_EXTERNAL_AUTH_ABORT = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_external_auth - This enum is used by
+ * %QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID: Mandatory binary attribute,
+ * 1..32 octets. This indicates the SSID of the AP and is used by both the
+ * authentication request event and authentication response commands.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID: Mandatory 6-byte MAC address
+ * attribute. It specifies the BSSID of the AP with which the
+ * authentication has to happen. Used by both the authentication request
+ * event and authentication response command.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR: Optional 6-byte attribute
+ * for the authentication request event interface. This specifies the MLD
+ * MAC address of the AP MLD. The driver indicates this to enable MLO
+ * during the authentication offload to supplicant. The driver shall look
+ * at %NL80211_ATTR_MLO_SUPPORT flag capability in NL80211_CMD_CONNECT to
+ * know whether the supplicant supports enabling MLO during the
+ * authentication offload. Supplicant should use the address of the
+ * interface (on which the authentication request event is reported) as
+ * own MLD MAC address.
+ *
+ * User space should enable MLO during the authentication only when it
+ * receives the AP MLD MAC address in authentication offload request. User
+ * space shouldn't enable MLO when the authentication offload request
+ * doesn't indicate the AP MLD MAC address even if the AP is MLO capable.
+ * The supplicant and driver should use MLD MAC addresses in the RA, TA,
+ * and BSSID fields of Authentication frames sent or received via nl80211.
+ * The driver translates the MLD MAC addresses to/from link addresses based
+ * on the link chosen for the authentication.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION: Mandatory u32 attribute for
+ * authentication request event. This indicates the operation to be
+ * performed with external authentication request event. Possible values
+ * for this attribute are defined in
+ * enum qca_wlan_vendor_external_auth_action.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO: Mandatory u32 attribute for
+ * authentication request event. This indicates the authentication
+ * algorithm to be used. Possible values are defined in
+ * enum nl80211_auth_type.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM: Mandatory u32 attribute for the
+ * authentication request event. This specifies AKM suite selector for the
+ * respective authentication, selected by the driver. Possible values are
+ * defined in IEEE Std 802.11-2024, 9.4.2.23.3 (AKM suites) (e.g.,
+ * 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER: Optional u32 attribute
+ * for the authentication request event. This specifies the pairwise cipher
+ * suite for the respective authentication, selected by the driver.
+ * Possible values are defined in IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher
+ * suites) (e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER: Optional u32 attribute for
+ * the authentication request event. This specifes the group cipher suite
+ * of the respective authentication, selected by the driver. Possible
+ * values are defined in IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher suites)
+ * (e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER: Optional u32
+ * attribute for the authentication request event. This specifies
+ * the group management cipher suite of the respective authentication,
+ * selected by the driver. Possible values are defined in
+ * IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher suites) (e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE: Mandatory u16 attribute for
+ * the authentication response command. The supplicant indicates the
+ * status of the external authentication operation with this attribute to
+ * the driver. Use %WLAN_STATUS_SUCCESS for successful authentication, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if the supplicant cannot give the real
+ * status code for failures.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID: Optional byte array attribute with
+ * a size of 16 bytes for the authentication response command. The
+ * supplicant indicates the PMKID generated during the external
+ * authentication operation with this attribute to the driver, which needs
+ * to be included in the RSNE of (Re)Association Request frame by the
+ * driver.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB: Optional u16 attribute
+ * for the authentication request event interface. This specifies the
+ * RSN Capabilities field (IEEE Std 802.11-2024, 9.4.2.23.4) that is to
+ * be included in the RSNE of the Authentication frames.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA: Optional binary attribute
+ * for the authentication request event interface. This specifies the
+ * entire element data to be included in the RSNXE of the Authentication
+ * frames.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK: Optional binary attribute for
+ * the authentication response command interface. This attribute is
+ * present only when the NL80211_AUTHTYPE_IEEE8021X authentication
+ * algorithm is used with (re)association frame encryption.
+ *
+ * In SME-in-driver mode, the (re)association exchange is entirely handled
+ * by the driver. The standard requires that the (Re)Association Request
+ * frame includes a Message Integrity Code (MIC) calculated using the Key
+ * Confirmation Key (KCK). This attribute is used to pass the KCK from the
+ * supplicant to the driver. With the KCK provided, the driver can
+ * calculate the MIC and append it to the (Re)Association Request frame as
+ * mandated by IEEE P802.11bi/D4.0, 12.16.6.2/12.16.6.3.
+ */
+enum qca_wlan_vendor_attr_external_auth {
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID = 1,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID = 2,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR = 3,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION = 4,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO = 5,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM = 6,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER = 7,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER = 8,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER = 9,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE = 10,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID = 11,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB = 12,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA = 13,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK = 14,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_MAX =
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c
index a65da613404a..f82a9e4fcedf 100644
--- a/contrib/wpa/src/common/sae.c
+++ b/contrib/wpa/src/common/sae.c
@@ -114,6 +114,8 @@ void sae_clear_temp_data(struct sae_data *sae)
wpabuf_free(tmp->own_rejected_groups);
wpabuf_free(tmp->peer_rejected_groups);
os_free(tmp->pw_id);
+ os_free(tmp->parsed_pw_id);
+ os_free(tmp->dec_pw_id);
bin_clear_free(tmp, sizeof(*tmp));
sae->tmp = NULL;
}
@@ -121,12 +123,16 @@ void sae_clear_temp_data(struct sae_data *sae)
void sae_clear_data(struct sae_data *sae)
{
+ unsigned int no_pw_id;
+
if (sae == NULL)
return;
sae_clear_temp_data(sae);
crypto_bignum_deinit(sae->peer_commit_scalar, 0);
crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0);
+ no_pw_id = sae->no_pw_id;
os_memset(sae, 0, sizeof(*sae));
+ sae->no_pw_id = no_pw_id;
}
@@ -361,8 +367,11 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const_time_select_bin(found, stub_password, password,
password_len, tmp_password);
if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
- addr, len, pwd_seed) < 0)
+ addr, len, pwd_seed) < 0) {
+ wpa_printf(MSG_INFO,
+ "SAE: hmac_sha256_vector() failed - cannot derive PWE");
break;
+ }
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
prime, qr_bin, qnr_bin, x_cand_bin);
@@ -823,7 +832,8 @@ fail:
static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier, u8 *pwd_seed)
+ const u8 *identifier, size_t identifier_len,
+ u8 *pwd_seed)
{
const u8 *addr[2];
size_t len[2];
@@ -837,10 +847,10 @@ static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
if (identifier) {
- wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
- identifier);
+ wpa_hexdump_ascii(MSG_DEBUG, "SAE: password identifier",
+ identifier, identifier_len);
addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
+ len[num_elem] = identifier_len;
num_elem++;
}
if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len,
@@ -865,7 +875,7 @@ static struct crypto_ec_point *
sae_derive_pt_ecc(struct crypto_ec *ec, int group,
const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier)
+ const u8 *identifier, size_t identifier_len)
{
u8 pwd_seed[64];
u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2];
@@ -884,7 +894,7 @@ sae_derive_pt_ecc(struct crypto_ec *ec, int group,
pwd_value_len = prime_len + (prime_len + 1) / 2;
if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
- identifier, pwd_seed) < 0)
+ identifier, identifier_len, pwd_seed) < 0)
goto fail;
/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len)
@@ -965,7 +975,7 @@ static struct crypto_bignum *
sae_derive_pt_ffc(const struct dh_group *dh, int group,
const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier)
+ const u8 *identifier, size_t identifier_len)
{
size_t hash_len, prime_len, pwd_value_len;
struct crypto_bignum *prime, *order;
@@ -989,7 +999,7 @@ sae_derive_pt_ffc(const struct dh_group *dh, int group,
goto fail;
if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
- identifier, pwd_seed) < 0)
+ identifier, identifier_len, pwd_seed) < 0)
goto fail;
/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */
@@ -1042,7 +1052,7 @@ fail:
static struct sae_pt *
sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier)
+ const u8 *identifier, size_t identifier_len)
{
struct sae_pt *pt;
@@ -1055,6 +1065,12 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
if (!pt)
return NULL;
+ if (identifier) {
+ pt->password_id = wpabuf_alloc_copy(identifier, identifier_len);
+ if (!pt->password_id)
+ goto fail;
+ }
+
#ifdef CONFIG_SAE_PK
os_memcpy(pt->ssid, ssid, ssid_len);
pt->ssid_len = ssid_len;
@@ -1064,7 +1080,7 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
if (pt->ec) {
pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len,
password, password_len,
- identifier);
+ identifier, identifier_len);
if (!pt->ecc_pt) {
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
goto fail;
@@ -1080,7 +1096,8 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
}
pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len,
- password, password_len, identifier);
+ password, password_len, identifier,
+ identifier_len);
if (!pt->ffc_pt) {
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
goto fail;
@@ -1093,19 +1110,21 @@ fail:
}
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+ const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier)
+ const u8 *identifier, size_t identifier_len)
{
struct sae_pt *pt = NULL, *last = NULL, *tmp;
- int default_groups[] = { 19, 0 };
+ const int default_groups[] = { 19, 0 };
int i;
if (!groups)
groups = default_groups;
for (i = 0; groups[i] > 0; i++) {
tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password,
- password_len, identifier);
+ password_len, identifier,
+ identifier_len);
if (!tmp)
continue;
@@ -1268,6 +1287,7 @@ void sae_deinit_pt(struct sae_pt *pt)
crypto_ec_point_deinit(pt->ecc_pt, 1);
crypto_bignum_deinit(pt->ffc_pt, 1);
crypto_ec_deinit(pt->ec);
+ wpabuf_free(pt->password_id);
prev = pt;
pt = pt->next;
os_free(prev);
@@ -1661,17 +1681,23 @@ fail:
int sae_process_commit(struct sae_data *sae)
{
u8 k[SAE_MAX_PRIME_LEN];
+ int ret = 0;
+
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
(sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
sae_derive_keys(sae, k) < 0)
- return -1;
- return 0;
+ ret = -1;
+
+ forced_memzero(k, SAE_MAX_PRIME_LEN);
+
+ return ret;
}
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token, const char *identifier)
+ const struct wpabuf *token, const u8 *identifier,
+ size_t identifier_len)
{
u8 *pos;
@@ -1713,11 +1739,11 @@ int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
if (identifier) {
/* Password Identifier element */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
- wpabuf_put_u8(buf, 1 + os_strlen(identifier));
+ wpabuf_put_u8(buf, 1 + identifier_len);
wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
- wpabuf_put_str(buf, identifier);
- wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
- identifier);
+ wpabuf_put_data(buf, identifier, identifier_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "SAE: own Password Identifier",
+ identifier, identifier_len);
}
if (sae->h2e && sae->tmp->own_rejected_groups) {
@@ -1877,14 +1903,14 @@ static void sae_parse_token_container(struct sae_data *sae,
const u8 *pos, const u8 *end,
const u8 **token, size_t *token_len)
{
- wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
- pos, end - pos);
if (!sae_is_token_container_elem(pos, end))
return;
- *token = pos + 3;
- *token_len = pos[1] - 1;
+ if (token)
+ *token = pos + 3;
+ if (token_len)
+ *token_len = pos[1] - 1;
wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)",
- *token, *token_len);
+ pos + 3, pos[1] - 1);
}
@@ -2045,14 +2071,12 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
}
-static int sae_parse_password_identifier(struct sae_data *sae,
+static int sae_parse_password_identifier(struct sae_data *sae, bool h2e,
const u8 **pos, const u8 *end)
{
const u8 *epos;
u8 len;
- wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
- *pos, end - *pos);
if (!sae_is_password_id_elem(*pos, end)) {
if (sae->tmp->pw_id) {
wpa_printf(MSG_DEBUG,
@@ -2060,8 +2084,9 @@ static int sae_parse_password_identifier(struct sae_data *sae,
sae->tmp->pw_id);
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
- os_free(sae->tmp->pw_id);
- sae->tmp->pw_id = NULL;
+ os_free(sae->tmp->parsed_pw_id);
+ sae->tmp->parsed_pw_id = NULL;
+ sae->tmp->parsed_pw_id_len = 0;
return WLAN_STATUS_SUCCESS; /* No Password Identifier */
}
@@ -2073,8 +2098,20 @@ static int sae_parse_password_identifier(struct sae_data *sae,
epos++; /* skip ext ID */
len--;
+ if (!h2e) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Password Identifier included, but H2E is not used");
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+
+ if (sae->no_pw_id) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Password Identifier included, but none has been enabled");
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ }
+
if (sae->tmp->pw_id &&
- (len != os_strlen(sae->tmp->pw_id) ||
+ (len != sae->tmp->pw_id_len ||
os_memcmp(sae->tmp->pw_id, epos, len) != 0)) {
wpa_printf(MSG_DEBUG,
"SAE: The included Password Identifier does not match the expected one (%s)",
@@ -2082,14 +2119,17 @@ static int sae_parse_password_identifier(struct sae_data *sae,
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
- os_free(sae->tmp->pw_id);
- sae->tmp->pw_id = os_malloc(len + 1);
- if (!sae->tmp->pw_id)
+ os_free(sae->tmp->parsed_pw_id);
+ sae->tmp->parsed_pw_id = os_malloc(len + 1);
+ if (!sae->tmp->parsed_pw_id) {
+ sae->tmp->parsed_pw_id_len = 0;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- os_memcpy(sae->tmp->pw_id, epos, len);
- sae->tmp->pw_id[len] = '\0';
+ }
+ os_memcpy(sae->tmp->parsed_pw_id, epos, len);
+ sae->tmp->parsed_pw_id_len = len;
+ sae->tmp->parsed_pw_id[len] = '\0';
wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
- sae->tmp->pw_id, len);
+ sae->tmp->parsed_pw_id, len);
*pos = epos + len;
return WLAN_STATUS_SUCCESS;
}
@@ -2101,8 +2141,6 @@ static int sae_parse_rejected_groups(struct sae_data *sae,
const u8 *epos;
u8 len;
- wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
- *pos, end - *pos);
if (!sae_is_rejected_groups_elem(*pos, end)) {
wpabuf_free(sae->tmp->peer_rejected_groups);
sae->tmp->peer_rejected_groups = NULL;
@@ -2141,8 +2179,6 @@ static int sae_parse_akm_suite_selector(struct sae_data *sae,
const u8 *epos;
u8 len;
- wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
- *pos, end - *pos);
if (!sae_is_akm_suite_selector_elem(*pos, end))
return WLAN_STATUS_SUCCESS;
@@ -2195,8 +2231,13 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
if (ie_offset)
*ie_offset = pos - data;
+ if (end > pos)
+ wpa_hexdump(MSG_DEBUG,
+ "SAE: Possible elements at the end of the frame",
+ pos, end - pos);
+
/* Optional Password Identifier element */
- res = sae_parse_password_identifier(sae, &pos, end);
+ res = sae_parse_password_identifier(sae, h2e, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
@@ -2215,11 +2256,9 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
sae_parse_token_container(sae, pos, end, token, token_len);
/* Conditional AKM Suite Selector element */
- if (h2e) {
- res = sae_parse_akm_suite_selector(sae, &pos, end);
- if (res != WLAN_STATUS_SUCCESS)
- return res;
- }
+ res = sae_parse_akm_suite_selector(sae, &pos, end);
+ if (res != WLAN_STATUS_SUCCESS)
+ return res;
if (sae->own_akm_suite_selector &&
sae->own_akm_suite_selector != sae->peer_akm_suite_selector) {
@@ -2239,6 +2278,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
}
+ if (wpa_key_mgmt_sae_ext_key(sae->akmp) && !h2e) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Tried to use EXT-KEY AKM without H2E");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
/*
* Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
* the values we sent which would be evidence of a reflection attack.
diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h
index a353aa8da357..afde296bfe76 100644
--- a/contrib/wpa/src/common/sae.h
+++ b/contrib/wpa/src/common/sae.h
@@ -58,12 +58,19 @@ struct sae_temporary_data {
struct crypto_bignum *prime_buf;
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
- char *pw_id;
+ u8 *pw_id;
+ size_t pw_id_len;
+ u8 *parsed_pw_id;
+ size_t parsed_pw_id_len;
+ char *dec_pw_id;
+ size_t dec_pw_id_len;
+ unsigned int pw_id_counter;
int vlan_id;
u8 bssid[ETH_ALEN];
struct wpabuf *own_rejected_groups;
struct wpabuf *peer_rejected_groups;
unsigned int own_addr_higher:1;
+ unsigned int try_other_password:1;
#ifdef CONFIG_SAE_PK
u8 kek[SAE_MAX_HASH_LEN];
@@ -84,6 +91,8 @@ struct sae_temporary_data {
#endif /* CONFIG_SAE_PK */
struct os_reltime disabled_until;
+
+ const void *used_pw;
};
struct sae_pt {
@@ -94,6 +103,7 @@ struct sae_pt {
const struct dh_group *dh;
struct crypto_bignum *ffc_pt;
+ struct wpabuf *password_id;
#ifdef CONFIG_SAE_PK
u8 ssid[32];
size_t ssid_len;
@@ -120,6 +130,7 @@ struct sae_data {
u16 rc; /* protocol instance variable: Rc (received send-confirm) */
unsigned int h2e:1;
unsigned int pk:1;
+ unsigned int no_pw_id:1;
struct sae_temporary_data *tmp;
};
@@ -135,7 +146,8 @@ int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
int *rejected_groups, const struct sae_pk *pk);
int sae_process_commit(struct sae_data *sae);
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
- const struct wpabuf *token, const char *identifier);
+ const struct wpabuf *token, const u8 *identifier,
+ size_t identifier_len);
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
int h2e, int *ie_offset);
@@ -146,9 +158,10 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
const char * sae_state_txt(enum sae_state state);
size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+ const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
- const char *identifier);
+ const u8 *identifier, size_t identifier_len);
struct crypto_ec_point *
sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2);
diff --git a/contrib/wpa/src/common/sae_pk.c b/contrib/wpa/src/common/sae_pk.c
index df79e5f2c3b0..88e16b3cf97b 100644
--- a/contrib/wpa/src/common/sae_pk.c
+++ b/contrib/wpa/src/common/sae_pk.c
@@ -384,6 +384,12 @@ int sae_pk_set_password(struct sae_data *sae, const char *password)
tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5;
wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu",
tmp->sec, tmp->lambda, tmp->fingerprint_bits);
+ if (tmp->fingerprint_bits > 8 * SAE_MAX_HASH_LEN) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: fingerprint_bits exceeds SAE_MAX_HASH_LEN");
+ bin_clear_free(pw, pw_len);
+ return -1;
+ }
/* Construct Fingerprint from PasswordBase by prefixing with Sec zero
* octets and skipping the Sec_1b bits */
diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h
index 5b735984983a..3d3c2ae63b7f 100644
--- a/contrib/wpa/src/common/version.h
+++ b/contrib/wpa/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.11" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.12" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c
index 1cfdd9ad7e2f..959da3663266 100644
--- a/contrib/wpa/src/common/wpa_common.c
+++ b/contrib/wpa/src/common/wpa_common.c
@@ -19,6 +19,7 @@
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
#include "defs.h"
+#include "common/nan_defs.h"
#include "wpa_common.h"
@@ -62,7 +63,7 @@ static unsigned int wpa_kck2_len(int akmp)
#endif /* CONFIG_IEEE80211R */
-static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
+unsigned int wpa_kek_len(int akmp, size_t pmk_len)
{
switch (akmp) {
case WPA_KEY_MGMT_FILS_SHA384:
@@ -102,7 +103,21 @@ static unsigned int wpa_kek2_len(int akmp)
#endif /* CONFIG_IEEE80211R */
-unsigned int wpa_mic_len(int akmp, size_t pmk_len)
+static int rsn_mic_len_hash(size_t pmk_len, enum rsn_hash_alg hash)
+{
+ switch (hash) {
+ case RSN_HASH_SHA256:
+ return 16;
+ case RSN_HASH_SHA384:
+ return 24;
+ case RSN_HASH_SHA512:
+ return 32;
+ default:
+ return pmk_len / 2;
+ }
+}
+
+unsigned int wpa_mic_len(int akmp, size_t pmk_len, enum rsn_hash_alg hash)
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -115,18 +130,116 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len)
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 0;
case WPA_KEY_MGMT_DPP:
- return pmk_len / 2;
+ return rsn_mic_len_hash(pmk_len, hash);
case WPA_KEY_MGMT_OWE:
- return pmk_len / 2;
+ return rsn_mic_len_hash(pmk_len, hash);
case WPA_KEY_MGMT_SAE_EXT_KEY:
case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
- return pmk_len / 2;
+ return rsn_mic_len_hash(pmk_len, hash);
default:
return 16;
}
}
+int rsn_cipher_suite_to_wpa_cipher(u32 cipher)
+{
+ switch (cipher) {
+ case RSN_CIPHER_SUITE_NONE:
+ return WPA_CIPHER_NONE;
+ case RSN_CIPHER_SUITE_TKIP:
+ return WPA_CIPHER_TKIP;
+ case RSN_CIPHER_SUITE_CCMP:
+ return WPA_CIPHER_CCMP;
+ case RSN_CIPHER_SUITE_AES_128_CMAC:
+ return WPA_CIPHER_AES_128_CMAC;
+ case RSN_CIPHER_SUITE_GCMP:
+ return WPA_CIPHER_GCMP;
+ case RSN_CIPHER_SUITE_CCMP_256:
+ return WPA_CIPHER_CCMP_256;
+ case RSN_CIPHER_SUITE_GCMP_256:
+ return WPA_CIPHER_GCMP_256;
+ case RSN_CIPHER_SUITE_BIP_GMAC_128:
+ return WPA_CIPHER_BIP_GMAC_128;
+ case RSN_CIPHER_SUITE_BIP_GMAC_256:
+ return WPA_CIPHER_BIP_GMAC_256;
+ case RSN_CIPHER_SUITE_BIP_CMAC_256:
+ return WPA_CIPHER_BIP_CMAC_256;
+ case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
+ return WPA_CIPHER_GTK_NOT_USED;
+ default:
+ return 0;
+ }
+}
+
+
+int rsn_key_mgmt_to_wpa_akm(u32 akm_suite)
+{
+ switch (akm_suite) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ return WPA_KEY_MGMT_IEEE8021X;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ return WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_IEEE80211R
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ return WPA_KEY_MGMT_FT_IEEE8021X;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ return WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_SHA384
+ case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
+ return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ case RSN_AUTH_KEY_MGMT_802_1X_SHA384:
+ return WPA_KEY_MGMT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+ case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
+ return WPA_KEY_MGMT_IEEE8021X_SHA256;
+ case RSN_AUTH_KEY_MGMT_PSK_SHA256:
+ return WPA_KEY_MGMT_PSK_SHA256;
+#ifdef CONFIG_SAE
+ case RSN_AUTH_KEY_MGMT_SAE:
+ return WPA_KEY_MGMT_SAE;
+ case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY:
+ return WPA_KEY_MGMT_SAE_EXT_KEY;
+ case RSN_AUTH_KEY_MGMT_FT_SAE:
+ return WPA_KEY_MGMT_FT_SAE;
+ case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY:
+ return WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+#endif /* CONFIG_SAE */
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ return WPA_KEY_MGMT_FILS_SHA256;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ return WPA_KEY_MGMT_FILS_SHA384;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ return WPA_KEY_MGMT_FT_FILS_SHA256;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ return WPA_KEY_MGMT_FT_FILS_SHA384;
+#ifdef CONFIG_OWE
+ case RSN_AUTH_KEY_MGMT_OWE:
+ return WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ case RSN_AUTH_KEY_MGMT_DPP:
+ return WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_PASN
+ case RSN_AUTH_KEY_MGMT_PASN:
+ return WPA_KEY_MGMT_PASN;
+#endif /* CONFIG_PASN */
+ case RSN_AUTH_KEY_MGMT_EPPKE:
+ return WPA_KEY_MGMT_EPPKE;
+ default:
+ return 0;
+ }
+}
+
+
/**
* wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
* @akmp: WPA_KEY_MGMT_* used in key derivation
@@ -134,8 +247,7 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len)
*/
int wpa_use_akm_defined(int akmp)
{
- return akmp == WPA_KEY_MGMT_OSEN ||
- akmp == WPA_KEY_MGMT_OWE ||
+ return akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
@@ -152,8 +264,7 @@ int wpa_use_akm_defined(int akmp)
*/
int wpa_use_cmac(int akmp)
{
- return akmp == WPA_KEY_MGMT_OSEN ||
- akmp == WPA_KEY_MGMT_OWE ||
+ return akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
@@ -174,8 +285,7 @@ int wpa_use_cmac(int akmp)
*/
int wpa_use_aes_key_wrap(int akmp)
{
- return akmp == WPA_KEY_MGMT_OSEN ||
- akmp == WPA_KEY_MGMT_OWE ||
+ return akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
wpa_key_mgmt_ft(akmp) ||
@@ -185,11 +295,68 @@ int wpa_use_aes_key_wrap(int akmp)
}
+#if defined(CONFIG_SAE) || defined(CONFIG_OWE) || defined(CONFIG_DPP)
+static int rsn_eapol_key_mic_hash(const u8 *key, size_t key_len, int akmp,
+ enum rsn_hash_alg hash_alg, int ver,
+ const u8 *buf, size_t len, u8 *mic)
+{
+ u8 hash[SHA512_MAC_LEN];
+ size_t mic_len;
+
+ if (hash_alg == RSN_HASH_NOT_SPECIFIED) {
+ if (key_len == 128 / 8)
+ hash_alg = RSN_HASH_SHA256;
+ else if (key_len == 192 / 8)
+ hash_alg = RSN_HASH_SHA384;
+ else if (key_len == 256 / 8)
+ hash_alg = RSN_HASH_SHA512;
+ else
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - %s)",
+ hash_alg == RSN_HASH_SHA512 ? 512 :
+ hash_alg == RSN_HASH_SHA384 ? 384 : 256,
+ wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN));
+ wpa_hexdump(MSG_DEBUG, "RSN: KCK", key, key_len);
+
+ if (hash_alg == RSN_HASH_SHA256) {
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+ mic_len = 16;
+#ifdef CONFIG_SHA384
+ } else if (hash_alg == RSN_HASH_SHA384) {
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+ mic_len = 24;
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ } else if (hash_alg == RSN_HASH_SHA512) {
+ if (hmac_sha512(key, key_len, buf, len, hash))
+ return -1;
+ mic_len = 32;
+#endif /* CONFIG_SHA512 */
+ } else {
+ wpa_printf(MSG_INFO,
+ "RSN: Unsupported KCK length: %u (hash_alg=%d)",
+ (unsigned int) key_len, hash_alg);
+ return -1;
+ }
+
+ os_memcpy(mic, hash, mic_len);
+ wpa_hexdump(MSG_DEBUG, "RSN: EAPOL-Key MIC", mic, mic_len);
+ return 0;
+}
+#endif /* CONFIG_SAE || CONFIG_OWE || CONFIG_DPP */
+
+
/**
* wpa_eapol_key_mic - Calculate EAPOL-Key MIC
* @key: EAPOL-Key Key Confirmation Key (KCK)
* @key_len: KCK length in octets
* @akmp: WPA_KEY_MGMT_* used in key derivation
+ * @hash_alg: Hash algorithm
* @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
* @buf: Pointer to the beginning of the EAPOL header (version field)
* @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
@@ -205,7 +372,8 @@ int wpa_use_aes_key_wrap(int akmp)
* happened during final editing of the standard and the correct behavior is
* defined in the last draft (IEEE 802.11i/D10).
*/
-int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp,
+ enum rsn_hash_alg hash_alg, int ver,
const u8 *buf, size_t len, u8 *mic)
{
u8 hash[SHA512_MAC_LEN];
@@ -241,37 +409,11 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
return omac1_aes_128(key, buf, len, mic);
case WPA_KEY_MGMT_SAE_EXT_KEY:
case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
-#ifdef CONFIG_SHA384
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
-#endif /* CONFIG_SHA384 */
-#ifdef CONFIG_SHA512
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
-#endif /* CONFIG_SHA512 */
- } else {
- wpa_printf(MSG_INFO,
- "SAE: Unsupported KCK length: %u",
- (unsigned int) key_len);
+ if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg,
+ ver, buf, len, mic) < 0)
return -1;
- }
- os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_SAE */
-#ifdef CONFIG_HS20
- case WPA_KEY_MGMT_OSEN:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
- return omac1_aes_128(key, buf, len, mic);
-#endif /* CONFIG_HS20 */
#ifdef CONFIG_SUITEB
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
wpa_printf(MSG_DEBUG,
@@ -292,48 +434,16 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_OWE
case WPA_KEY_MGMT_OWE:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
- } else {
- wpa_printf(MSG_INFO,
- "OWE: Unsupported KCK length: %u",
- (unsigned int) key_len);
+ if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg,
+ ver, buf, len, mic) < 0)
return -1;
- }
- os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
case WPA_KEY_MGMT_DPP:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
- } else {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported KCK length: %u",
- (unsigned int) key_len);
+ if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg,
+ ver, buf, len, mic) < 0)
return -1;
- }
- os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_DPP */
#ifdef CONFIG_SHA384
@@ -378,6 +488,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
* @ptk: Buffer for pairwise transient key
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
+ * @z: Additional context data
+ * @z_len: Length of additional context data
* @kdk_len: The length in octets that should be derived for KDK
* Returns: 0 on success, -1 on failure
*
@@ -388,7 +500,9 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
* [ || Z.x ])
*
* The optional Z.x component is used only with DPP and that part is not defined
- * in IEEE 802.11.
+ * in IEEE 802.11. The additional context data is also used to carry the DHss
+ * (Diffie-Hellman shared secret) when IEEE 802.1X authentication in
+ * Authentication frames is used.
*/
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
@@ -467,6 +581,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA384;
#else /* CONFIG_SHA384 */
return -1;
#endif /* CONFIG_SHA384 */
@@ -475,6 +590,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA256;
#ifdef CONFIG_OWE
} else if (akmp == WPA_KEY_MGMT_OWE && (pmk_len == 32 ||
owe_ptk_workaround)) {
@@ -482,16 +598,24 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ if (owe_ptk_workaround && pmk_len == 64)
+ ptk->hash_alg = RSN_HASH_SHA512;
+ else if (owe_ptk_workaround && pmk_len == 48)
+ ptk->hash_alg = RSN_HASH_SHA384;
+ else
+ ptk->hash_alg = RSN_HASH_SHA256;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA384;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA512;
} else if (akmp == WPA_KEY_MGMT_OWE) {
wpa_printf(MSG_INFO, "OWE: Unknown PMK length %u",
(unsigned int) pmk_len);
@@ -503,16 +627,19 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA256;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA384;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA512;
} else if (akmp == WPA_KEY_MGMT_DPP) {
wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
(unsigned int) pmk_len);
@@ -526,6 +653,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA256;
#ifdef CONFIG_SHA384
} else if (pmk_len == 48) {
wpa_printf(MSG_DEBUG,
@@ -533,6 +661,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA384;
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
} else if (pmk_len == 64) {
@@ -541,6 +670,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA512;
#endif /* CONFIG_SHA512 */
} else {
wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u",
@@ -553,6 +683,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
return -1;
+ ptk->hash_alg = RSN_HASH_SHA1;
}
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
@@ -582,6 +713,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
ptk->kek2_len = 0;
ptk->kck2_len = 0;
+ ptk->ptk_len = ptk_len;
os_memset(tmp, 0, sizeof(tmp));
os_memset(data, 0, data_len);
return 0;
@@ -593,7 +725,7 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
size_t dh_ss_len, u8 *pmk, size_t *pmk_len)
{
- u8 nonces[2 * FILS_NONCE_LEN];
+ u8 nonces[2 * NONCE_LEN];
const u8 *addr[2];
size_t len[2];
size_t num_elem;
@@ -610,12 +742,12 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
return -1;
wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len);
- wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len);
- os_memcpy(nonces, snonce, FILS_NONCE_LEN);
- os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN);
+ os_memcpy(nonces, snonce, NONCE_LEN);
+ os_memcpy(&nonces[NONCE_LEN], anonce, NONCE_LEN);
addr[0] = rmsk;
len[0] = rmsk_len;
num_elem = 1;
@@ -625,10 +757,10 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
num_elem++;
}
if (wpa_key_mgmt_sha384(akmp))
- res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+ res = hmac_sha384_vector(nonces, 2 * NONCE_LEN, num_elem,
addr, len, pmk);
else
- res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+ res = hmac_sha256_vector(nonces, 2 * NONCE_LEN, num_elem,
addr, len, pmk);
if (res == 0)
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
@@ -691,7 +823,7 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
* KDK = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits + FILS-FT_bits,
* KDK_bits)
*/
- data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
+ data_len = 2 * ETH_ALEN + 2 * NONCE_LEN + dhss_len;
data = os_malloc(data_len);
if (!data)
goto err;
@@ -700,10 +832,10 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
pos += ETH_ALEN;
os_memcpy(pos, aa, ETH_ALEN);
pos += ETH_ALEN;
- os_memcpy(pos, snonce, FILS_NONCE_LEN);
- pos += FILS_NONCE_LEN;
- os_memcpy(pos, anonce, FILS_NONCE_LEN);
- pos += FILS_NONCE_LEN;
+ os_memcpy(pos, snonce, NONCE_LEN);
+ pos += NONCE_LEN;
+ os_memcpy(pos, anonce, NONCE_LEN);
+ pos += NONCE_LEN;
if (dhss)
os_memcpy(pos, dhss, dhss_len);
@@ -757,8 +889,8 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
" AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
- wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN);
if (dhss)
wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
@@ -814,8 +946,8 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
" AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
- wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
@@ -825,9 +957,9 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
* [ || gSTA || gAP ])
*/
addr[0] = snonce;
- len[0] = FILS_NONCE_LEN;
+ len[0] = NONCE_LEN;
addr[1] = anonce;
- len[1] = FILS_NONCE_LEN;
+ len[1] = NONCE_LEN;
addr[2] = sta_addr;
len[2] = ETH_ALEN;
addr[3] = bssid;
@@ -1428,27 +1560,66 @@ void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse)
* @akmp: Authentication and key management protocol
* @cipher: The cipher suite
*
- * According to IEEE P802.11az/D2.7, 12.12.7, the hash algorithm to use is the
- * hash algorithm defined for the Base AKM (see Table 9-151 (AKM suite
+ * According to IEEE Std 802.11-2024, 12.13.8 (PTKSA derivation with PASN
+ * authentication), the hash algorithm to use is the
+ * hash algorithm defined for the Base AKM (see Table 9-190 (AKM suite
* selectors)). When there is no Base AKM, the hash algorithm is selected based
* on the pairwise cipher suite provided in the RSNE by the AP in the second
* PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers
* 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used.
*/
-bool pasn_use_sha384(int akmp, int cipher)
+static bool pasn_use_sha384(int akmp, int cipher)
{
- return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
- cipher == WPA_CIPHER_GCMP_256)) ||
+ return ((akmp == WPA_KEY_MGMT_PASN || akmp == WPA_KEY_MGMT_EPPKE) &&
+ (cipher == WPA_CIPHER_CCMP_256 ||
+ cipher == WPA_CIPHER_GCMP_256)) ||
wpa_key_mgmt_sha384(akmp);
}
/**
- * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc.
+ * pasn_select_hash_alg - Select hash algorithm for PTK derivation
+ * @akmp: Authentication and key management protocol
+ * @cipher: The cipher suite
+ * @pmk_len: PMK length in octets
+ *
+ * According to IEEE Std 802.11-2024, Table 9-190 (AKM suite selectors), AKMs
+ * 00-0F-AC:24 and 00-0F-AC:25 have the length of the PMK, the length
+ * of the SAE key confirmation key, SAE-KCK, and PTK-KCK, and the length of
+ * PTK-KEK depending on the hash algorithm specified in 12.4.2 (see 12.7.1.3
+ * and 12.7.3), i.e, the hash algorithm depends on the prime length associated
+ * with the selected group per Table 12-1 (Hash algorithm based on length of
+ * prime).
+ */
+static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher,
+ size_t pmk_len)
+{
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae_ext_key(akmp)) {
+ if (pmk_len == 64)
+ return RSN_HASH_SHA512;
+ if (pmk_len == 48)
+ return RSN_HASH_SHA384;
+ }
+#endif /* CONFIG_SAE */
+
+ if (pasn_use_sha384(akmp, cipher))
+ return RSN_HASH_SHA384;
+
+ return RSN_HASH_SHA256;
+}
+
+
+/**
+ * pasn_pmk_to_ptk - Calculate PASN/EPPKE PTK from PMK, addresses, etc.
* @pmk: Pairwise master key
* @pmk_len: Length of PMK
- * @spa: Suppplicant address
- * @bssid: AP BSSID
+ * @spa: For EPPKE authentication, non-AP MLD MAC address is used for MLO. For
+ * PASN authentication or EPPKE authentication for non-MLO, non-AP STA link
+ * MAC address is used.
+ * @bssid: For EPPKE authentication, AP MLD MAC address is used for MLO. For
+ * PASN authentication or EPPKE authentication for non-MLO, AP BSSID is
+ * used.
* @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key
* exchange encoded as an octet string
* @dhss_len: The length of dhss in octets
@@ -1456,19 +1627,26 @@ bool pasn_use_sha384(int akmp, int cipher)
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @kdk_len: the length in octets that should be derived for HTLK. Can be zero.
+ * @kek_len: The length in octets that should be derived for KEK. Can be zero.
+ * @alg: Output variable for indicating the selected hash algorithm
+ * @is_eppke: EPPKE authentication
* Returns: 0 on success, -1 on failure
*/
int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
const u8 *spa, const u8 *bssid,
const u8 *dhss, size_t dhss_len,
struct wpa_ptk *ptk, int akmp, int cipher,
- size_t kdk_len)
+ size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg,
+ bool is_eppke)
{
- u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN];
+ u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
+ WPA_KDK_MAX_LEN];
+ const u8 *pos;
u8 *data;
size_t data_len, ptk_len;
int ret = -1;
- const char *label = "PASN PTK Derivation";
+ const char *label = is_eppke ? "EPPKE PTK Derivation" :
+ "PASN PTK Derivation";
if (!pmk || !pmk_len) {
wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation");
@@ -1481,6 +1659,12 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
}
/*
+ * Use "EPPKE PTK Derivation" instead of “PASN PTK Derivation” for
+ * EPPKE Authentication per IEEE P802.11bi/D4.0, 12.16.9.3.4 (PTKSA
+ * derivation and MIC computation with EPPKE authentication). For EPPKE
+ * MLO, the non-AP MLD MAC address is used instead of the SPA and the
+ * AP MLD MAC address instead of the BSSID.
+ *
* PASN-PTK = KDF(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss)
*
* KCK = L(PASN-PTK, 0, 256)
@@ -1499,7 +1683,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
ptk->kck_len = WPA_PASN_KCK_LEN;
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
- ptk->kek_len = 0;
+ ptk->kek_len = kek_len;
ptk->kek2_len = 0;
ptk->kck2_len = 0;
@@ -1510,22 +1694,42 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
goto err;
}
- ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len;
+ ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len + ptk->kek_len;
if (ptk_len > sizeof(tmp))
goto err;
- if (pasn_use_sha384(akmp, cipher)) {
+ *alg = pasn_select_hash_alg(akmp, cipher, pmk_len);
+
+ switch (*alg) {
+ case RSN_HASH_SHA512:
+#ifdef CONFIG_SHA512
+ wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA512");
+
+ if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp,
+ ptk_len) < 0)
+ goto err;
+ break;
+#endif
+ case RSN_HASH_SHA384:
+#ifdef CONFIG_SHA384
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384");
if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
goto err;
- } else {
+ break;
+#endif
+ case RSN_HASH_SHA256:
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256");
if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
goto err;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "PASN: Unsupported hash algorithm %d",
+ *alg);
+ goto err;
}
wpa_printf(MSG_DEBUG,
@@ -1538,17 +1742,27 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN);
wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN);
+ pos = &tmp[WPA_PASN_KCK_LEN];
- os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len);
+ if (kek_len) {
+ os_memcpy(ptk->kek, pos, kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "PASN: KEK:",
+ ptk->kek, ptk->kek_len);
+ pos += kek_len;
+ }
+
+ os_memcpy(ptk->tk, pos, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len);
+ pos += ptk->tk_len;
if (kdk_len) {
- os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len,
- ptk->kdk_len);
+ os_memcpy(ptk->kdk, pos, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:",
ptk->kdk, ptk->kdk_len);
}
+ ptk->ptk_len = ptk_len;
+ ptk->hash_alg = *alg;
forced_memzero(tmp, sizeof(tmp));
ret = 0;
err:
@@ -1559,13 +1773,19 @@ err:
/*
* pasn_mic_len - Returns the MIC length for PASN authentication
+ * @alg: Selected hash algorithm from pasn_pmk_to_ptk()
*/
-u8 pasn_mic_len(int akmp, int cipher)
+size_t pasn_mic_len(enum rsn_hash_alg alg)
{
- if (pasn_use_sha384(akmp, cipher))
+ switch (alg) {
+ case RSN_HASH_SHA512:
+ return 32;
+ case RSN_HASH_SHA384:
return 24;
-
- return 16;
+ case RSN_HASH_SHA256:
+ default:
+ return 16;
+ }
}
@@ -1625,15 +1845,126 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher)
}
+#ifdef CONFIG_IEEE8021X_AUTH
+
+int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa,
+ const u8 *aa, const u8 *snonce, const u8 *anonce,
+ int akmp, int cipher, const u8 *dhss,
+ size_t dhss_len, struct wpa_ptk *ptk,
+ size_t kdk_len)
+{
+ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+ spa, aa, snonce, anonce, ptk, akmp,
+ cipher, dhss, dhss_len, kdk_len);
+}
+
+
+/**
+ * wpa_auth_8021x_mic - Calculate IEEE 802.1X in Authentication frames MIC
+ * @akmp: Negotiated key management protocol
+ * @kck: The key confirmation key from 802.1X exchange
+ * @kck_len: KCK length in octets
+ * @addr1: Authenticator address
+ * @addr2: Supplicant address
+ * @data: This should hold the RSNE + RSNXE.
+ * For a MIC included by the AP with EAP-Success and the second
+ * Authentication frame if a PMKSA was identified via a PMKID included in
+ * the first Authentication frame, this holds the Beacon frame RSNE+RSNXE.
+ * For a MIC included by the non-AP STA in the (Re)Association Request
+ * frame, this holds the RSNE and RSNXE sent by the non-AP STA.
+ * @data_len: The length of data
+ * @frame: For a MIC included by the AP in EAP-Success and the second
+ * Authentication frame if a PMKSA was identified via a PMKID included in
+ * the first Authentication frame, this holds the body of the
+ * Authentication frame including the MIC element with the octets in the
+ * MIC field of the MIC element set to 0, else NULL
+ * @frame_len: The length of frame
+ * @mic: Buffer to hold the MIC on success. Must be large enough to handle the
+ * maximal MIC length.
+ * Returns: 0 on success, -1 on failure
+ *
+ * HMAC-HASH (PTK-KCK, AA || SPA || RSNE || RSNXE || Frame Data)
+ */
+int wpa_auth_8021x_mic(int akmp, const u8 *kck, size_t kck_len, const u8 *addr1,
+ const u8 *addr2, const u8 *data, size_t data_len,
+ const u8 *frame, size_t frame_len, u8 *mic)
+{
+ size_t mic_len;
+ u8 *buf;
+ int ret = -1;
+ size_t buf_len = 2 * ETH_ALEN + data_len + frame_len;
+
+ if (!kck) {
+ wpa_printf(MSG_INFO, "802.1X: No KCK for MIC calculation");
+ return -1;
+ }
+
+ if (!data || !data_len) {
+ wpa_printf(MSG_INFO,
+ "802.1X: Invalid data for MIC calculation");
+ return -1;
+ }
+
+ buf = os_zalloc(buf_len);
+ if (!buf)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "802.1X MIC calculation");
+ wpa_printf(MSG_DEBUG, "addr1: " MACSTR, MAC2STR(addr1));
+ wpa_printf(MSG_DEBUG, "addr2: " MACSTR, MAC2STR(addr2));
+
+ os_memcpy(buf, addr1, ETH_ALEN);
+ os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "MIC: data", data, data_len);
+ os_memcpy(buf + 2 * ETH_ALEN, data, data_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "MIC: KCK", kck, kck_len);
+
+ if (frame) {
+ wpa_hexdump_key(MSG_MSGDUMP, "802.1X: MIC: frame",
+ frame, frame_len);
+ os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len);
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "MIC: buf", buf, buf_len);
+ if (wpa_key_mgmt_sha384(akmp)) {
+ wpa_printf(MSG_DEBUG, "MIC: HMAC-SHA384");
+ mic_len = 24;
+
+ if (hmac_sha384(kck, kck_len, buf, buf_len, mic) < 0)
+ goto out;
+ } else {
+ wpa_printf(MSG_DEBUG, "MIC: HMAC-SHA256");
+ mic_len = 16;
+
+ if (hmac_sha256(kck, kck_len, buf, buf_len, mic) < 0)
+ goto out;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "802.1X: Calculated MIC", mic, mic_len);
+ ret = 0;
+out:
+ bin_clear_free(buf, buf_len);
+ return ret;
+}
+
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+
/**
* pasn_mic - Calculate PASN MIC
+ * @alg: Selected hash algorithm from pasn_pmk_to_ptk()
* @kck: The key confirmation key for the PASN PTKSA
- * @akmp: Negotiated AKM
- * @cipher: Negotiated pairwise cipher
- * @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the
- * BSSID
- * @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant
- * address
+ * @kck_len: KCK length in octets
+ * @addr1: For the 2nd PASN/EPPKE frame supplicant address is used for non-MLO;
+ * for MLO, 2nd EPPKE authentication to use non-AP MLD MAC address.
+ * For the 3rd PASN/EPPKE frame BSSID is used for non-MLO; for MLO, 3rd
+ * EPPKE authentication to use AP MLD MAC address as per
+ * @addr2: For the 2nd PASN/EPPKE frame BSSID is used for non-MLO; for MLO, 2nd
+ * EPPKE authentication to use AP MLD MAC address. For the 3rd PASN/EPPKE
+ * frame supplicant address is used for non-MLO; for MLO, 3rd EPPKE
+ * Authentication frame to use non-AP MLD MAC address.
* @data: For calculating the MIC for the 2nd PASN frame, this should hold the
* Beacon frame RSNE + RSNXE. For calculating the MIC for the 3rd PASN
* frame, this should hold the hash of the body of the PASN 1st frame.
@@ -1645,21 +1976,29 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher)
* maximal MIC length
* Returns: 0 on success, -1 on failure
*/
-int pasn_mic(const u8 *kck, int akmp, int cipher,
+int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len,
const u8 *addr1, const u8 *addr2,
const u8 *data, size_t data_len,
const u8 *frame, size_t frame_len, u8 *mic)
{
u8 *buf;
- u8 hash[SHA384_MAC_LEN];
+ u8 hash[SHA512_MAC_LEN];
size_t buf_len = 2 * ETH_ALEN + data_len + frame_len;
int ret = -1;
+ size_t mic_len;
if (!kck) {
wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation");
return -1;
}
+ if (kck_len != WPA_PASN_KCK_LEN) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Unexpected KCK length %zu for MIC calculation",
+ kck_len);
+ return -1;
+ }
+
if (!data || !data_len) {
wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation");
return -1;
@@ -1683,27 +2022,40 @@ int pasn_mic(const u8 *kck, int akmp, int cipher,
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame", frame, frame_len);
os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len);
- wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, WPA_PASN_KCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, kck_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf", buf, buf_len);
- if (pasn_use_sha384(akmp, cipher)) {
- wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384");
+ mic_len = pasn_mic_len(alg);
- if (hmac_sha384(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
+ switch (alg) {
+#ifdef CONFIG_SHA512
+ case RSN_HASH_SHA512:
+ wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA512");
+ if (hmac_sha512(kck, kck_len, buf, buf_len, hash))
goto err;
-
- os_memcpy(mic, hash, 24);
- wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 24);
- } else {
+ break;
+#endif /* CONFIG_SHA512 */
+#ifdef CONFIG_SHA384
+ case RSN_HASH_SHA384:
+ wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384");
+ if (hmac_sha384(kck, kck_len, buf, buf_len, hash))
+ goto err;
+ break;
+#endif /* CONFIG_SHA384 */
+ case RSN_HASH_SHA256:
wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256");
-
- if (hmac_sha256(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
+ if (hmac_sha256(kck, kck_len, buf, buf_len, hash))
goto err;
-
- os_memcpy(mic, hash, 16);
- wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 16);
+ break;
+ default:
+ wpa_printf(MSG_ERROR,
+ "PASN: Unsupported alg=%d for MIC calculation", alg);
+ goto err;
}
+ os_memcpy(mic, hash, mic_len);
+ wpa_hexdump_key(MSG_DEBUG, "PASN: MIC", mic, mic_len);
+
ret = 0;
err:
bin_clear_free(buf, buf_len);
@@ -1713,23 +2065,33 @@ err:
/**
* pasn_auth_frame_hash - Computes a hash of an Authentication frame body
- * @akmp: Negotiated AKM
- * @cipher: Negotiated pairwise cipher
+ * @alg: Selected hash algorithm from pasn_pmk_to_ptk()
* @data: Pointer to the Authentication frame body
* @len: Length of the Authentication frame body
* @hash: On return would hold the computed hash. Should be big enough to handle
- * SHA384.
+ * SHA512.
* Returns: 0 on success, -1 on failure
*/
-int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
+int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len,
u8 *hash)
{
- if (pasn_use_sha384(akmp, cipher)) {
+ switch (alg) {
+#ifdef CONFIG_SHA512
+ case RSN_HASH_SHA512:
+ wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-512");
+ return sha512_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA512 */
+#ifdef CONFIG_SHA384
+ case RSN_HASH_SHA384:
wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-384");
return sha384_vector(1, &data, &len, hash);
- } else {
+#endif /* CONFIG_SHA384 */
+ case RSN_HASH_SHA256:
wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-256");
return sha256_vector(1, &data, &len, hash);
+ default:
+ wpa_printf(MSG_ERROR, "PASN: Unsupported alg=%d", alg);
+ return -1;
}
}
@@ -1738,93 +2100,13 @@ int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
static int rsn_selector_to_bitfield(const u8 *s)
{
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
- return WPA_CIPHER_NONE;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
- return WPA_CIPHER_TKIP;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
- return WPA_CIPHER_CCMP;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
- return WPA_CIPHER_AES_128_CMAC;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
- return WPA_CIPHER_GCMP;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
- return WPA_CIPHER_CCMP_256;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
- return WPA_CIPHER_GCMP_256;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
- return WPA_CIPHER_BIP_GMAC_128;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
- return WPA_CIPHER_BIP_GMAC_256;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
- return WPA_CIPHER_BIP_CMAC_256;
- if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
- return WPA_CIPHER_GTK_NOT_USED;
- return 0;
+ return rsn_cipher_suite_to_wpa_cipher(RSN_SELECTOR_GET(s));
}
static int rsn_key_mgmt_to_bitfield(const u8 *s)
{
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
- return WPA_KEY_MGMT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
- return WPA_KEY_MGMT_PSK;
-#ifdef CONFIG_IEEE80211R
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
- return WPA_KEY_MGMT_FT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
- return WPA_KEY_MGMT_FT_PSK;
-#ifdef CONFIG_SHA384
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384)
- return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
-#endif /* CONFIG_SHA384 */
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_SHA384
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA384)
- return WPA_KEY_MGMT_IEEE8021X_SHA384;
-#endif /* CONFIG_SHA384 */
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
- return WPA_KEY_MGMT_IEEE8021X_SHA256;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
- return WPA_KEY_MGMT_PSK_SHA256;
-#ifdef CONFIG_SAE
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
- return WPA_KEY_MGMT_SAE;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
- return WPA_KEY_MGMT_SAE_EXT_KEY;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
- return WPA_KEY_MGMT_FT_SAE;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
- return WPA_KEY_MGMT_FT_SAE_EXT_KEY;
-#endif /* CONFIG_SAE */
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
- return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
- return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
- return WPA_KEY_MGMT_FILS_SHA256;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
- return WPA_KEY_MGMT_FILS_SHA384;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
- return WPA_KEY_MGMT_FT_FILS_SHA256;
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
- return WPA_KEY_MGMT_FT_FILS_SHA384;
-#ifdef CONFIG_OWE
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
- return WPA_KEY_MGMT_OWE;
-#endif /* CONFIG_OWE */
-#ifdef CONFIG_DPP
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
- return WPA_KEY_MGMT_DPP;
-#endif /* CONFIG_DPP */
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
- return WPA_KEY_MGMT_OSEN;
-#ifdef CONFIG_PASN
- if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN)
- return WPA_KEY_MGMT_PASN;
-#endif /* CONFIG_PASN */
- return 0;
+ return rsn_key_mgmt_to_wpa_akm(RSN_SELECTOR_GET(s));
}
@@ -1880,16 +2162,14 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
return -1;
}
- if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
- rsn_ie[1] == rsn_ie_len - 2 &&
- WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
- pos = rsn_ie + 6;
- left = rsn_ie_len - 6;
-
- data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
- data->has_group = 1;
- data->key_mgmt = WPA_KEY_MGMT_OSEN;
- data->proto = WPA_PROTO_OSEN;
+ if (rsn_ie_len >= 2 + 4 + 2 && rsn_ie[1] >= 4 + 2 &&
+ rsn_ie[1] == rsn_ie_len - 2 &&
+ (WPA_GET_BE32(&rsn_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE ||
+ WPA_GET_BE32(&rsn_ie[2]) ==
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE) &&
+ WPA_GET_LE16(&rsn_ie[2 + 4]) == RSN_VERSION) {
+ pos = rsn_ie + 2 + 4 + 2;
+ left = rsn_ie_len - 2 - 4 - 2;
} else {
const struct rsn_ie_hdr *hdr;
@@ -2689,6 +2969,70 @@ int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_PMKSA_PRIVACY
+/**
+ * rsn_pmkid_privacy - Calculate a new PMKID for PMKSA caching privacy
+ * @pmkid_anonce: Authenticator nonce (ANonce)
+ * @pmkid_snonce: Supplicant nonce (SNonce)
+ * @akmp: Negotiated key management protocol
+ * @pmk_len: PMK length in bytes
+ * @pmkid: Buffer for returning PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE P802.11bi/D4.0, 12.16.7.2 (PMKID privacy)
+ * PMKID = Truncate-128(Hash("PMK Name" || PMKIDANonce || PMKIDSNonce))
+ */
+int rsn_pmkid_privacy(const u8 *pmkid_anonce, const u8 *pmkid_snonce,
+ int akmp, size_t pmk_len, u8 *pmkid)
+{
+ const char *label = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, NONCE_LEN, NONCE_LEN };
+ unsigned char hash[SHA512_MAC_LEN];
+
+ wpa_hexdump(MSG_DEBUG, "PMKID privacy: PMKIDANonce",
+ pmkid_anonce, NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "PMKID privacy: PMKIDSNonce",
+ pmkid_snonce, NONCE_LEN);
+
+ addr[0] = (const u8 *) label;
+ addr[1] = pmkid_anonce;
+ addr[2] = pmkid_snonce;
+
+ if (0) {
+#if defined(CONFIG_FILS) || defined(CONFIG_SHA384)
+ } else if (wpa_key_mgmt_sha384(akmp)) {
+ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using SHA-384");
+ if (sha384_vector(3, addr, len, hash) < 0)
+ return -1;
+#endif /* CONFIG_FILS || CONFIG_SHA384 */
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae_ext_key(akmp)) {
+ if (pmk_len == 64) {
+ if (sha512_vector(3, addr, len, hash) < 0)
+ return -1;
+ } else if (pmk_len == 48) {
+ if (sha384_vector(3, addr, len, hash) < 0)
+ return -1;
+ } else {
+ if (sha256_vector(3, addr, len, hash) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SAE */
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using SHA-256");
+ if (sha256_vector(3, addr, len, hash) < 0)
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PMKID privacy: new PMKID", hash, PMKID_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+
+ return 0;
+}
+#endif /* CONFIG_PMKSA_PRIVACY */
+
+
/**
* wpa_cipher_txt - Convert cipher suite to a text string
* @cipher: Cipher suite (WPA_CIPHER_* enum)
@@ -2780,8 +3124,6 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
return "FT-SAE";
case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
return "FT-SAE-EXT-KEY";
- case WPA_KEY_MGMT_OSEN:
- return "OSEN";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
return "WPA2-EAP-SUITE-B";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
@@ -2802,6 +3144,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
return "PASN";
case WPA_KEY_MGMT_IEEE8021X_SHA384:
return "WPA2-EAP-SHA384";
+ case WPA_KEY_MGMT_EPPKE:
+ return "EPPKE";
default:
return "UNKNOWN";
}
@@ -2828,8 +3172,6 @@ u32 wpa_akm_to_suite(int akm)
return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
if (akm & WPA_KEY_MGMT_CCKM)
return RSN_AUTH_KEY_MGMT_CCKM;
- if (akm & WPA_KEY_MGMT_OSEN)
- return RSN_AUTH_KEY_MGMT_OSEN;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
@@ -2854,6 +3196,12 @@ u32 wpa_akm_to_suite(int akm)
return RSN_AUTH_KEY_MGMT_OWE;
if (akm & WPA_KEY_MGMT_DPP)
return RSN_AUTH_KEY_MGMT_DPP;
+#ifdef CONFIG_PASN
+ if (akm & WPA_KEY_MGMT_PASN)
+ return RSN_AUTH_KEY_MGMT_PASN;
+#endif /* CONFIG_PASN */
+ if (akm & WPA_KEY_MGMT_EPPKE)
+ return RSN_AUTH_KEY_MGMT_EPPKE;
return 0;
}
@@ -2895,6 +3243,38 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
}
+/**
+ * wpa_compare_rsn_ie_params - Compare RSN IE parameters
+ * @rsne1: Pointer to first RSNE (with element ID and length)
+ * @rsne1_len: Length of the first RSNE
+ * @rsnee2: Pointer to second RSNE (with element ID and length)
+ * @rsne2_len: Length of the second RSNE
+ * Returns: 0 if parameters match, -1 otherwise
+ *
+ * Compare the security parameters (proto, ciphers, key_mgmt)
+ * of two RSN IEs.
+ */
+int wpa_compare_rsne_params(const u8 *rsne1, size_t rsne1_len,
+ const u8 *rsne2, size_t rsne2_len)
+{
+ struct wpa_ie_data rsn1, rsn2;
+
+ if (!rsne1 || !rsne2)
+ return -1;
+
+ if (wpa_parse_wpa_ie_rsn(rsne1, rsne1_len, &rsn1) < 0 ||
+ wpa_parse_wpa_ie_rsn(rsne2, rsne2_len, &rsn2) < 0)
+ return -1;
+
+ if (rsn1.proto == rsn2.proto &&
+ rsn1.pairwise_cipher == rsn2.pairwise_cipher &&
+ rsn1.key_mgmt == rsn2.key_mgmt)
+ return 0;
+
+ return -1;
+}
+
+
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace)
{
u8 *start, *end, *rpos, *rend;
@@ -3162,10 +3542,10 @@ int wpa_cipher_put_suites(u8 *start, int ciphers)
int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
{
- if (ciphers & WPA_CIPHER_CCMP_256)
- return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_GCMP_256)
return WPA_CIPHER_GCMP_256;
+ if (ciphers & WPA_CIPHER_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_CCMP)
return WPA_CIPHER_CCMP;
if (ciphers & WPA_CIPHER_GCMP)
@@ -3180,10 +3560,10 @@ int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
int wpa_pick_group_cipher(int ciphers)
{
- if (ciphers & WPA_CIPHER_CCMP_256)
- return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_GCMP_256)
return WPA_CIPHER_GCMP_256;
+ if (ciphers & WPA_CIPHER_CCMP_256)
+ return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_CCMP)
return WPA_CIPHER_CCMP;
if (ciphers & WPA_CIPHER_GCMP)
@@ -3440,7 +3820,7 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
const u8 *p;
size_t left;
u8 link_id;
- char title[50];
+ char title[100];
int ret;
if (len == 0)
@@ -3462,12 +3842,6 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
return 0;
}
- if (selector == OSEN_IE_VENDOR_TYPE) {
- ie->osen = pos;
- ie->osen_len = dlen;
- return 0;
- }
-
if (left >= PMKID_LEN && selector == RSN_KEY_DATA_PMKID) {
ie->pmkid = p;
wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, dlen);
@@ -3510,6 +3884,15 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
return 0;
}
+ if (left > 2 && selector == RSN_KEY_DATA_SAE_PW_IDS) {
+ ie->sae_pw_ids = p;
+ ie->sae_pw_ids_len = left;
+ wpa_hexdump_key(MSG_DEBUG,
+ "RSN: SAE Password Identifiers in EAPOL-Key",
+ pos, dlen);
+ return 0;
+ }
+
if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) {
ie->ip_addr_req = p;
wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
@@ -3621,6 +4004,78 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
return 0;
}
+ if (left >= 1 && selector == WFA_KEY_DATA_RSN_OVERRIDE_LINK) {
+ link_id = p[0];
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ return 2;
+
+ ie->rsn_override_link[link_id] = p;
+ ie->rsn_override_link_len[link_id] = left;
+ ret = os_snprintf(title, sizeof(title),
+ "RSN: Link ID %u - RSN Override Link KDE in EAPOL-Key",
+ link_id);
+ if (!os_snprintf_error(sizeof(title), ret))
+ wpa_hexdump(MSG_DEBUG, title, pos, dlen);
+ return 0;
+ }
+
+ if (selector == RSNE_OVERRIDE_IE_VENDOR_TYPE) {
+ ie->rsne_override = pos;
+ ie->rsne_override_len = dlen;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: RSNE Override element in EAPOL-Key",
+ ie->rsne_override, ie->rsne_override_len);
+ return 0;
+ }
+
+ if (selector == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) {
+ ie->rsne_override_2 = pos;
+ ie->rsne_override_2_len = dlen;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: RSNE Override 2 element in EAPOL-Key",
+ ie->rsne_override_2, ie->rsne_override_2_len);
+ return 0;
+ }
+
+ if (selector == RSNXE_OVERRIDE_IE_VENDOR_TYPE) {
+ ie->rsnxe_override = pos;
+ ie->rsnxe_override_len = dlen;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: RSNXE Override element in EAPOL-Key",
+ ie->rsnxe_override, ie->rsnxe_override_len);
+ return 0;
+ }
+
+ if (selector == RSN_SELECTION_IE_VENDOR_TYPE) {
+ ie->rsn_selection = p;
+ ie->rsn_selection_len = left;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: RSN Selection element in EAPOL-Key",
+ ie->rsn_selection, ie->rsn_selection_len);
+ return 0;
+ }
+
+#if defined(CONFIG_NAN) || defined(CONFIG_PASN)
+ if (left >= sizeof(struct nan_nik_kde) &&
+ selector == NAN_KEY_DATA_NIK) {
+ ie->nan_nik = p;
+ ie->nan_nik_len = left;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: NAN NIK KDE in EAPOL-Key",
+ ie->nan_nik, ie->nan_nik_len);
+ return 0;
+ }
+
+ if (left >= sizeof(struct nan_key_lifetime_kde) &&
+ selector == NAN_KEY_DATA_LIFETIME) {
+ ie->nan_key_lifetime = p;
+ ie->nan_key_lifetime_len = left;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: NAN Key Lifetime KDE in EAPOL-Key",
+ ie->nan_key_lifetime, ie->nan_key_lifetime_len);
+ return 0;
+ }
+#endif /* CONFIG_NAN || CONFIG_PASN */
+
return 2;
}
@@ -3779,7 +4234,7 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
/*
* wpa_pasn_build_auth_header - Add the MAC header and initialize Authentication
- * frame for PASN
+ * frame for PASN/EPPKE
*
* @buf: Buffer in which the header will be added
* @bssid: The BSSID of the AP
@@ -3787,15 +4242,17 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
* @dst: Destination address
* @trans_seq: Authentication transaction sequence number
* @status: Authentication status
+ * @is_eppke: EPPKE authentication
*/
void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
const u8 *src, const u8 *dst,
- u8 trans_seq, u16 status)
+ u8 trans_seq, u16 status, bool is_eppke)
{
struct ieee80211_mgmt *auth;
+ u16 auth_alg = is_eppke ? WLAN_AUTH_EPPKE : WLAN_AUTH_PASN;
- wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u",
- trans_seq);
+ wpa_printf(MSG_DEBUG, "%s: Add authentication header trans_seq=%u",
+ is_eppke ? "EPPKE" : "PASN", trans_seq);
auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.auth.variable));
@@ -3808,7 +4265,7 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
os_memcpy(auth->bssid, bssid, ETH_ALEN);
auth->seq_ctrl = 0;
- auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN);
+ auth->u.auth.auth_alg = host_to_le16(auth_alg);
auth->u.auth.auth_transaction = host_to_le16(trans_seq);
auth->u.auth.status_code = host_to_le16(status);
}
@@ -3862,6 +4319,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
case WPA_KEY_MGMT_PASN:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
break;
+#ifdef CONFIG_ENC_ASSOC
+ case WPA_KEY_MGMT_EPPKE:
+ /* EPPKE without base AKM: EPPKE AKMP is used as the selected
+ * AKMP per IEEE P802.11bi/D4.0, 12.16.9.1. */
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE);
+ break;
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
@@ -4037,7 +4501,7 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
data_len -= len - 1;
while (data_len) {
- if (wpabuf_tailroom(buf) < 1 + data_len)
+ if (wpabuf_tailroom(buf) < 2 + data_len)
return -1;
wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
len = data_len > 255 ? 255 : data_len;
@@ -4052,23 +4516,29 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
/*
- * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE
+ * wpa_pasn_validate_rsne - Validate PASN/EPPKE specific data of RSNE
* @data: Parsed representation of an RSNE
+ * @is_eppke: EPPKE Authentication
* Returns -1 for invalid data; otherwise 0
*/
-int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke)
{
- u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+ u16 capab = WPA_CAPABILITY_MFPC;
+
+ if (!is_eppke)
+ capab |= WPA_CAPABILITY_MFPR;
if (data->proto != WPA_PROTO_RSN)
return -1;
if ((data->capabilities & capab) != capab) {
- wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities");
+ wpa_printf(MSG_DEBUG, "%s: Invalid RSNE capabilities",
+ is_eppke ? "EPPKE" : "PASN");
return -1;
}
- if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+ if (!data->has_group ||
+ (!is_eppke && data->group_cipher != WPA_CIPHER_GTK_NOT_USED)) {
wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher");
return -1;
}
@@ -4098,13 +4568,25 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
#endif /* CONFIG_IEEE80211R */
case WPA_KEY_MGMT_PASN:
break;
+#ifdef CONFIG_ENC_ASSOC
+ case WPA_KEY_MGMT_EPPKE:
+ /* EPPKE AKMP is used when EPPKE is performed without a base
+ * AKM (no mutual authentication). This is only valid for EPPKE
+ * authentication per IEEE P802.11bi/D4.0, 12.16.9.1. */
+ if (!is_eppke) {
+ wpa_printf(MSG_INFO,
+ "PASN: EPPKE AKM not allowed for PASN");
+ return -1;
+ }
+ break;
+#endif /* CONFIG_ENC_ASSOC */
default:
- wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x",
- data->key_mgmt);
+ wpa_printf(MSG_ERROR, "%s: invalid key_mgmt: 0x%0x",
+ is_eppke ? "EPPKE" : "PASN", data->key_mgmt);
return -1;
}
- if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+ if (!is_eppke && (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED)) {
wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher");
return -1;
}
@@ -4217,23 +4699,54 @@ int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
}
-void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab)
+void wpa_pasn_add_rsnxe(struct wpabuf *buf, u64 capab)
{
size_t flen;
- flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return; /* no supported extended RSN capabilities */
+
+ /* Determine how many octets are needed to represent capab */
+ if (capab & 0xFF00000000000000ULL)
+ flen = 8;
+ else if (capab & 0x00FF000000000000ULL)
+ flen = 7;
+ else if (capab & 0x0000FF0000000000ULL)
+ flen = 6;
+ else if (capab & 0x000000FF00000000ULL)
+ flen = 5;
+ else if (capab & 0xFF000000)
+ flen = 4;
+ else if (capab & 0x00FF0000)
+ flen = 3;
+ else if (capab & 0x0000FF00)
+ flen = 2;
+ else
+ flen = 1;
+
if (wpabuf_tailroom(buf) < 2 + flen)
return;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
wpabuf_put_u8(buf, WLAN_EID_RSNX);
wpabuf_put_u8(buf, flen);
- wpabuf_put_u8(buf, capab & 0x00ff);
- capab >>= 8;
- if (capab)
- wpabuf_put_u8(buf, capab);
+
+ /* Write the little endian capability field octet-by-octet */
+ wpabuf_put_u8(buf, capab & 0x000000FF);
+ if (flen > 1)
+ wpabuf_put_u8(buf, (capab >> 8) & 0x000000FF);
+ if (flen > 2)
+ wpabuf_put_u8(buf, (capab >> 16) & 0x000000FF);
+ if (flen > 3)
+ wpabuf_put_u8(buf, (capab >> 24) & 0x000000FF);
+ if (flen > 4)
+ wpabuf_put_u8(buf, (capab >> 32) & 0x000000FF);
+ if (flen > 5)
+ wpabuf_put_u8(buf, (capab >> 40) & 0x000000FF);
+ if (flen > 6)
+ wpabuf_put_u8(buf, (capab >> 48) & 0x000000FF);
+ if (flen > 7)
+ wpabuf_put_u8(buf, (capab >> 56) & 0x000000FF);
}
@@ -4252,7 +4765,7 @@ int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len)
if (!len || !extra_ies || !buf)
return 0;
- if (wpabuf_tailroom(buf) < sizeof(len))
+ if (wpabuf_tailroom(buf) < len)
return -1;
wpabuf_put_data(buf, extra_ies, len);
@@ -4260,3 +4773,43 @@ int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len)
}
#endif /* CONFIG_PASN */
+
+
+void rsn_set_snonce_cookie(u8 *snonce)
+{
+ u8 *pos;
+
+ pos = snonce + WPA_NONCE_LEN - 6;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ WPA_PUT_BE24(pos, 0x000029);
+}
+
+
+bool rsn_is_snonce_cookie(const u8 *snonce)
+{
+ const u8 *pos;
+
+ pos = snonce + WPA_NONCE_LEN - 6;
+ return WPA_GET_BE24(pos) == OUI_WFA &&
+ WPA_GET_BE24(pos + 3) == 0x000029;
+}
+
+
+void wpa_add_supported_groups(struct wpabuf *buf, const int *groups)
+{
+ unsigned int count, i;
+
+ if (!buf || !groups)
+ return;
+
+ count = int_array_len(groups);
+ if (wpabuf_tailroom(buf) < 2 + 1 + count * 2)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + count * 2);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_SUPPORTED_GROUPS);
+ for (i = 0; i < count; i++)
+ wpabuf_put_le16(buf, groups[i]);
+}
diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h
index 1e3136843437..51bd3886f38f 100644
--- a/contrib/wpa/src/common/wpa_common.h
+++ b/contrib/wpa/src/common/wpa_common.h
@@ -23,11 +23,20 @@
#define WPA_GMK_LEN 32
#define WPA_GTK_MAX_LEN 32
#define WPA_PASN_PMK_LEN 32
-#define WPA_PASN_MAX_MIC_LEN 24
+#define WPA_PASN_MAX_MIC_LEN 32
#define WPA_MAX_RSNXE_LEN 4
+#define WPA_1X_MAX_MIC_LEN 32
#define OWE_DH_GROUP 19
+enum rsn_hash_alg {
+ RSN_HASH_NOT_SPECIFIED,
+ RSN_HASH_SHA1,
+ RSN_HASH_SHA256,
+ RSN_HASH_SHA384,
+ RSN_HASH_SHA512,
+};
+
#ifdef CONFIG_NO_TKIP
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_NONE | \
@@ -92,9 +101,9 @@ WPA_CIPHER_BIP_CMAC_256)
#define RSN_AUTH_KEY_MGMT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 23)
#define RSN_AUTH_KEY_MGMT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 24)
#define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
+#define RSN_AUTH_KEY_MGMT_EPPKE RSN_SELECTOR(0x00, 0x0f, 0xac, 29)
#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
-#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
@@ -139,11 +148,13 @@ WPA_CIPHER_BIP_CMAC_256)
#define RSN_KEY_DATA_MLO_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
#define RSN_KEY_DATA_MLO_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
#define RSN_KEY_DATA_MLO_LINK RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
+#define RSN_KEY_DATA_SAE_PW_IDS RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
#define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20)
#define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21)
+#define WFA_KEY_DATA_RSN_OVERRIDE_LINK RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x2d)
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
@@ -246,7 +257,6 @@ struct wpa_eapol_key {
#define FILS_ICK_MAX_LEN 48
#define FILS_FT_MAX_LEN 48
#define WPA_PASN_KCK_LEN 32
-#define WPA_PASN_MIC_MAX_LEN 24
#define WPA_LTF_KEYSEED_MAX_LEN 48
/**
@@ -267,8 +277,12 @@ struct wpa_ptk {
size_t kck2_len;
size_t kek2_len;
size_t kdk_len;
+ size_t ptk_len;
size_t ltf_keyseed_len;
+ enum rsn_hash_alg hash_alg;
int installed; /* 1 if key has already been installed to driver */
+ bool installed_rx; /* whether TK has been installed as the next TK
+ * for temporary RX-only use in the driver */
};
struct wpa_gtk {
@@ -346,6 +360,13 @@ struct rsn_error_kde {
be16 error_type;
} STRUCT_PACKED;
+#define WPA_GTK_KDE_PREFIX_LEN 2
+struct wpa_gtk_kde {
+ u8 keyid;
+ u8 reserved;
+ u8 gtk[WPA_GTK_MAX_LEN];
+} STRUCT_PACKED;
+
#define WPA_IGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN)
struct wpa_igtk_kde {
u8 keyid[2];
@@ -458,7 +479,8 @@ struct rsn_rdie {
#endif /* _MSC_VER */
-int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp,
+ enum rsn_hash_alg hash, int ver,
const u8 *buf, size_t len, u8 *mic);
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
@@ -553,6 +575,8 @@ static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
return -1;
}
#endif /* CONFIG_SUITEB192 */
+int rsn_pmkid_privacy(const u8 *pmkid_anonce, const u8 *pmkid_snonce,
+ int akmp, size_t pmk_len, u8 *pmkid);
const char * wpa_cipher_txt(int cipher);
const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
@@ -560,6 +584,8 @@ u32 wpa_akm_to_suite(int akm);
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len);
+int wpa_compare_rsne_params(const u8 *rsne1, size_t rsne1_len,
+ const u8 *rsne2, size_t rsne2_len);
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace);
struct wpa_ft_ies {
@@ -611,7 +637,7 @@ struct wpa_ft_ies {
struct wpabuf *fte_buf;
};
-/* IEEE P802.11az/D2.6 - 9.4.2.303 PASN Parameters element */
+/* IEEE Std 802.11-2024 - 9.4.2.305 PASN Parameters element */
#define WPA_PASN_CTRL_COMEBACK_INFO_PRESENT BIT(0)
#define WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT BIT(1)
@@ -643,6 +669,14 @@ struct wpa_pasn_params_data {
#define WPA_PASN_PUBKEY_COMPRESSED_1 0x03
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
+/* WPA3 specification - RSN Selection element */
+enum rsn_selection_variant {
+ RSN_SELECTION_RSNE = 0,
+ RSN_SELECTION_RSNE_OVERRIDE = 1,
+ RSN_SELECTION_RSNE_OVERRIDE_2 = 2,
+};
+
+
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
int key_mgmt, bool reassoc_resp);
void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse);
@@ -661,6 +695,8 @@ struct wpa_eapol_ie_parse {
size_t igtk_len;
const u8 *bigtk;
size_t bigtk_len;
+ const u8 *sae_pw_ids;
+ size_t sae_pw_ids_len;
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
@@ -704,6 +740,18 @@ struct wpa_eapol_ie_parse {
u16 aid;
const u8 *wmm;
size_t wmm_len;
+ const u8 *rsn_selection;
+ size_t rsn_selection_len;
+ const u8 *rsne_override;
+ size_t rsne_override_len;
+ const u8 *rsne_override_2;
+ size_t rsne_override_2_len;
+ const u8 *rsnxe_override;
+ size_t rsnxe_override_len;
+ const u8 *nan_nik;
+ size_t nan_nik_len;
+ const u8 *nan_key_lifetime;
+ size_t nan_key_lifetime_len;
u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
size_t mlo_gtk_len[MAX_NUM_MLD_LINKS];
@@ -716,6 +764,8 @@ struct wpa_eapol_ie_parse {
u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
const u8 *mlo_link[MAX_NUM_MLD_LINKS];
size_t mlo_link_len[MAX_NUM_MLD_LINKS];
+ const u8 *rsn_override_link[MAX_NUM_MLD_LINKS];
+ size_t rsn_override_link_len[MAX_NUM_MLD_LINKS];
};
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
@@ -740,34 +790,39 @@ int wpa_pick_group_cipher(int ciphers);
int wpa_parse_cipher(const char *value);
int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
-unsigned int wpa_mic_len(int akmp, size_t pmk_len);
+unsigned int wpa_mic_len(int akmp, size_t pmk_len, enum rsn_hash_alg hash);
+unsigned int wpa_kek_len(int akmp, size_t pmk_len);
int wpa_use_akm_defined(int akmp);
int wpa_use_cmac(int akmp);
int wpa_use_aes_key_wrap(int akmp);
int fils_domain_name_hash(const char *domain, u8 *hash);
-bool pasn_use_sha384(int akmp, int cipher);
int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
const u8 *spa, const u8 *bssid,
const u8 *dhss, size_t dhss_len,
struct wpa_ptk *ptk, int akmp, int cipher,
- size_t kdk_len);
+ size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg,
+ bool is_eppke);
+
+size_t pasn_mic_len(enum rsn_hash_alg alg);
-u8 pasn_mic_len(int akmp, int cipher);
+int wpa_auth_8021x_mic(int akmp, const u8 *kck, size_t kck_len, const u8 *addr1,
+ const u8 *addr2, const u8 *data, size_t data_len,
+ const u8 *frame, size_t frame_len, u8 *mic);
-int pasn_mic(const u8 *kck, int akmp, int cipher,
+int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len,
const u8 *addr1, const u8 *addr2,
const u8 *data, size_t data_len,
const u8 *frame, size_t frame_len, u8 *mic);
int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher);
-int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
+int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len,
u8 *hash);
void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
const u8 *src, const u8 *dst,
- u8 trans_seq, u16 status);
+ u8 trans_seq, u16 status, bool is_eppke);
int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid,
int akmp, int cipher);
@@ -780,11 +835,24 @@ void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
struct wpabuf *wrapped_data_buf);
-int wpa_pasn_validate_rsne(const struct wpa_ie_data *data);
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke);
int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
struct wpa_pasn_params_data *pasn_params);
-void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab);
+void wpa_pasn_add_rsnxe(struct wpabuf *buf, u64 capab);
int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len);
+void rsn_set_snonce_cookie(u8 *snonce);
+bool rsn_is_snonce_cookie(const u8 *snonce);
+
+int rsn_cipher_suite_to_wpa_cipher(u32 cipher);
+int rsn_key_mgmt_to_wpa_akm(u32 akm_suite);
+
+int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa,
+ const u8 *aa, const u8 *snonce, const u8 *anonce,
+ int akmp, int cipher, const u8 *dhss,
+ size_t dhss_len, struct wpa_ptk *ptk,
+ size_t kdk_len);
+void wpa_add_supported_groups(struct wpabuf *buf, const int *groups);
+
#endif /* WPA_COMMON_H */
diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h
index f6142501e440..ae93e1e4ef88 100644
--- a/contrib/wpa/src/common/wpa_ctrl.h
+++ b/contrib/wpa/src/common/wpa_ctrl.h
@@ -13,6 +13,8 @@
extern "C" {
#endif
+#define WPA_CTRL_IFACE_LINK_NAME "link"
+
/* wpa_supplicant control interface - fixed message prefixes */
/** Interactive request for identity/password/pin */
@@ -116,6 +118,10 @@ extern "C" {
#define WPA_EVENT_T2LM_UPDATE "CTRL-EVENT-T2LM-UPDATE "
/** MLO link reconfiguration event */
#define WPA_EVENT_LINK_RECONFIG "CTRL-EVENT-LINK-RECONFIG "
+/** MLO link STA removed through link reconfiguration */
+#define WPA_EVENT_LINK_STA_REMOVED "CTRL-EVENT-LINK-STA-REMOVED "
+/** MLO link STA added through link reconfiguration */
+#define WPA_EVENT_LINK_STA_ADDED "CTRL-EVENT-LINK-STA-ADDED "
/** IP subnet status change notification
*
@@ -204,6 +210,7 @@ extern "C" {
#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET "
#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
+#define DPP_EVENT_CONFOBJ_IDPASS "DPP-CONFOBJ-IDPASS "
#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
@@ -225,6 +232,7 @@ extern "C" {
#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
+#define DPP_EVENT_ENROLLEE_CAPABILITY "DPP-ENROLLEE-CAPABILITY "
#define DPP_EVENT_CSR "DPP-CSR "
#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
#define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED "
@@ -232,12 +240,37 @@ extern "C" {
#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT "
#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER "
-/* Wi-Fi Aware (NAN USD) events */
+/* Wi-Fi Aware (NAN) events */
#define NAN_DISCOVERY_RESULT "NAN-DISCOVERY-RESULT "
#define NAN_REPLIED "NAN-REPLIED "
#define NAN_PUBLISH_TERMINATED "NAN-PUBLISH-TERMINATED "
#define NAN_SUBSCRIBE_TERMINATED "NAN-SUBSCRIBE-TERMINATED "
#define NAN_RECEIVE "NAN-RECEIVE "
+#define NAN_TRANSMIT_STATUS "NAN-TRANSMIT-STATUS "
+#define NAN_CLUSTER_JOIN "NAN-CLUSTER-JOIN "
+#define NAN_NDP_REQUEST "NAN-NDP-REQUEST "
+#define NAN_NDP_COUNTER_REQUEST "NAN-NDP-COUNTER-REQUEST "
+#define NAN_NDP_CONNECTED "NAN-NDP-CONNECTED "
+#define NAN_NDP_DISCONNECTED "NAN-NDP-DISCONNECTED "
+#define NAN_BOOTSTRAP_REQUEST "NAN-BOOTSTRAP-REQUEST "
+#define NAN_BOOTSTRAP_SUCCESS "NAN-BOOTSTRAP-SUCCESS "
+#define NAN_BOOTSTRAP_FAILURE "NAN-BOOTSTRAP-FAILURE "
+#define NAN_NIK_RECEIVED "NAN-NIK-RECEIVED "
+#define NAN_PAIRING_REQUEST "NAN-PAIRING-REQUEST "
+#define NAN_PEER_SCHEDULE_CHANGED "NAN-PEER-SCHEDULE-CHANGED "
+#define NAN_SCHEDULE_UPDATE_DONE "NAN-SCHEDULE-UPDATE-DONE "
+
+/* NAN Pairing status
+ * addr=<peer address> akmp=<SAE|PASN> cipher=<CCMP|GCMP-256>
+ * status=<success|failure> [nd_pmk=<hex>]
+ */
+#define NAN_PAIRING_STATUS "NAN-PAIRING-STATUS "
+
+/* NAN local schedule channel evacuation.
+ * parameters: map_id=<map_id> freq=<freq>
+ */
+#define NAN_CHAN_EVACUATION "NAN-CHAN-EVACUATION "
+#define NAN_STOPPED "NAN-STOPPED "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -304,6 +337,10 @@ extern "C" {
#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
+#define P2P_EVENT_BOOTSTRAP_REQUEST "P2P-BOOTSTRAP-REQUEST "
+#define P2P_EVENT_BOOTSTRAP_SUCCESS "P2P-BOOTSTRAP-SUCCESS "
+#define P2P_EVENT_BOOTSTRAP_FAILURE "P2P-BOOTSTRAP-FAILURE "
+
#define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
@@ -335,7 +372,6 @@ extern "C" {
/* parameters: <Venue Number> <Venue URL> */
#define RX_VENUE_URL "RX-VENUE-URL "
-#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
@@ -448,6 +484,11 @@ extern "C" {
/* Event triggered for received management frame */
#define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED "
+/* Event triggerred on AP receiving Wi-Fi Alliance Generational Capabilities
+ * indication.
+ * Parameters: <STA addr> <Generational Capabilities Indication body> */
+#define WFA_GEN_CAPAB_RX "WFA-GEN-CAPAB "
+
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
@@ -455,6 +496,37 @@ extern "C" {
/* PASN authentication status */
#define PASN_AUTH_STATUS "PASN-AUTH-STATUS "
+/* Proximity Ranging PASN negotiation started
+ * peer_addr=<MAC> role=<role> protocol=<protocol type>
+ */
+#define PR_PASN_NEGOTIATION_STARTED "PR-PASN-NEGOTIATION-STARTED "
+
+/* Result of PASN performed for Proximity Ranging
+ * <result> role=<role> protocol=<protocol type> opclass=<op class> channel=<op channel> cc=<country>
+ */
+#define PR_PASN_RESULT "PR-PASN-RESULT "
+#define PR_RANGING_TERMINATED "PR-RANGING-TERMINATED "
+
+/* Proximity Ranging parameters to use in ranging */
+#define PR_RANGING_PARAMS "PR-RANGING-PARAMS "
+
+/* Proximity Ranging measurement result */
+#define PR_EVENT_PEER_MEASUREMENT "PR-PEER-MEASUREMENT "
+
+/* Proximity Ranging measurement session complete */
+#define PR_EVENT_RANGING_COMPLETE "PR-RANGING-COMPLETE "
+
+/* Proximity Ranging capable peer discovered via USD
+ * peer_addr=<MAC> pasn_type=0x<hex> name=<string>
+ * edca=<0|1> edca_ista=<0|1> edca_rsta=<0|1>
+ * ntb=<0|1> ntb_ista=<0|1> ntb_rsta=<0|1>
+ * secure_ltf=<0|1> 6ghz=<0|1> freq=<MHz> dik_valid=<0|1>
+ */
+#define PR_PEER_FOUND "PR-PEER-FOUND "
+
+#define AFC_EVENT_RECEIVED "AFC-EVENT-RECEIVED "
+#define AFC_EVENT_COMPLETE "AFC-EVENT-COMPLETE "
+
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h
index 0ac8fc19476a..e6bc7bac3c3f 100644
--- a/contrib/wpa/src/crypto/crypto.h
+++ b/contrib/wpa/src/crypto/crypto.h
@@ -217,7 +217,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
enum crypto_cipher_alg {
CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
- CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC4
};
struct crypto_cipher;
diff --git a/contrib/wpa/src/crypto/crypto_gnutls.c b/contrib/wpa/src/crypto/crypto_gnutls.c
index a7a163f5cb2a..524d1fe07904 100644
--- a/contrib/wpa/src/crypto/crypto_gnutls.c
+++ b/contrib/wpa/src/crypto/crypto_gnutls.c
@@ -439,14 +439,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
break;
- case CRYPTO_CIPHER_ALG_RC2:
- if (key_len == 5)
- a = GCRY_CIPHER_RFC2268_40;
- else
- a = GCRY_CIPHER_RFC2268_128;
- res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
- gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
- break;
default:
os_free(ctx);
return NULL;
diff --git a/contrib/wpa/src/crypto/crypto_libtomcrypt.c b/contrib/wpa/src/crypto/crypto_libtomcrypt.c
index fd79c1a40337..b14e28260c28 100644
--- a/contrib/wpa/src/crypto/crypto_libtomcrypt.c
+++ b/contrib/wpa/src/crypto/crypto_libtomcrypt.c
@@ -315,9 +315,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
case CRYPTO_CIPHER_ALG_DES:
idx = find_cipher("des");
break;
- case CRYPTO_CIPHER_ALG_RC2:
- idx = find_cipher("rc2");
- break;
case CRYPTO_CIPHER_ALG_RC4:
idx = -1;
rc4 = 1;
diff --git a/contrib/wpa/src/crypto/crypto_linux.c b/contrib/wpa/src/crypto/crypto_linux.c
index 9278e279795f..0282743fd947 100644
--- a/contrib/wpa/src/crypto/crypto_linux.c
+++ b/contrib/wpa/src/crypto/crypto_linux.c
@@ -363,8 +363,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
}
if (*len < ctx->mac_len) {
- crypto_hash_deinit(ctx);
*len = ctx->mac_len;
+ crypto_hash_deinit(ctx);
return -1;
}
*len = ctx->mac_len;
@@ -580,7 +580,6 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip,
linux_af_alg_skcipher_deinit(skcipher);
return -1;
}
- os_free(skip_buf);
msg.msg_control = NULL;
msg.msg_controllen = 0;
@@ -588,9 +587,11 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip,
if (ret < 0) {
wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
__func__, strerror(errno));
+ os_free(skip_buf);
linux_af_alg_skcipher_deinit(skcipher);
return -1;
}
+ os_free(skip_buf);
linux_af_alg_skcipher_deinit(skcipher);
if ((size_t) ret < skip + data_len) {
diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c
index 2d8ff60aa92d..a6bfc7a7c5a2 100644
--- a/contrib/wpa/src/crypto/crypto_openssl.c
+++ b/contrib/wpa/src/crypto/crypto_openssl.c
@@ -186,8 +186,32 @@ static int EC_GROUP_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
#endif /* OpenSSL version < 1.1.1 */
+static void openssl_disable_fips(void)
+{
+#ifndef CONFIG_FIPS
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ static bool done = false;
+
+ if (done)
+ return;
+ done = true;
+
+ if (!EVP_default_properties_is_fips_enabled(NULL))
+ return; /* FIPS mode is not enabled */
+
+ if (!EVP_default_properties_enable_fips(NULL, 0))
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to disable FIPS mode");
+ else
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Disabled FIPS mode to enable non-FIPS-compliant algorithms and parameters");
+#endif /* OpenSSL version >= 3.0 */
+#endif /* !CONFIG_FIPS */
+}
+
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static OSSL_PROVIDER *openssl_legacy_provider = NULL;
+static OSSL_PROVIDER *openssl_default_provider = NULL;
#endif /* OpenSSL version >= 3.0 */
void openssl_load_legacy_provider(void)
@@ -212,6 +236,36 @@ static void openssl_unload_legacy_provider(void)
}
+static void openssl_load_default_provider_if_fips(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (openssl_default_provider)
+ return;
+
+ if (!OSSL_PROVIDER_available(NULL, "fips"))
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Load default provider to replace fips provider when needed");
+ openssl_default_provider = OSSL_PROVIDER_try_load(NULL, "default", 1);
+ if (!openssl_default_provider)
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Failed to load default provider");
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static void openssl_unload_default_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (openssl_default_provider) {
+ OSSL_PROVIDER_unload(openssl_default_provider);
+ openssl_default_provider = NULL;
+ }
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
#if OPENSSL_VERSION_NUMBER < 0x30000000L
static BIGNUM * get_group5_prime(void)
@@ -319,8 +373,16 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
#ifndef CONFIG_FIPS
+static void openssl_need_md5(void)
+{
+ openssl_disable_fips();
+ openssl_load_default_provider_if_fips();
+}
+
+
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
+ openssl_disable_fips();
openssl_load_legacy_provider();
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
@@ -369,7 +431,7 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip,
EVP_CIPHER_CTX *ctx;
int outl;
int res = -1;
- unsigned char skip_buf[16];
+ unsigned char skip_buf[16] = { 0 };
openssl_load_legacy_provider();
@@ -404,6 +466,7 @@ out:
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
+ openssl_need_md5();
return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
@@ -898,11 +961,11 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
cipher = EVP_des_cbc();
break;
#endif /* OPENSSL_NO_DES */
-#ifndef OPENSSL_NO_RC2
- case CRYPTO_CIPHER_ALG_RC2:
- cipher = EVP_rc2_ecb();
+#ifndef OPENSSL_NO_NULL
+ case CRYPTO_CIPHER_NULL:
+ cipher = EVP_enc_null();
break;
-#endif /* OPENSSL_NO_RC2 */
+#endif /* OPENSSL_NO_NULL */
default:
os_free(ctx);
return NULL;
@@ -1023,16 +1086,23 @@ err:
struct wpabuf *pubkey = NULL, *privkey = NULL;
BIGNUM *priv_bn = NULL;
EVP_PKEY_CTX *gctx;
+ const char *propquery = NULL;
*priv = NULL;
wpabuf_free(*publ);
*publ = NULL;
+ if (OSSL_PROVIDER_available(NULL, "fips")) {
+ openssl_disable_fips();
+ openssl_load_default_provider_if_fips();
+ propquery = "provider!=fips";
+ }
+
params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
"modp_1536", 0);
params[1] = OSSL_PARAM_construct_end();
- gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+ gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", propquery);
if (!gctx ||
EVP_PKEY_keygen_init(gctx) != 1 ||
EVP_PKEY_CTX_set_params(gctx, params) != 1 ||
@@ -1371,6 +1441,9 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
}
if (EVP_MAC_init(ctx->ctx, key, key_len, params) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_MAC_init(hmac,digest=%s) failed: %s",
+ a, ERR_error_string(ERR_get_error(), NULL));
EVP_MAC_CTX_free(ctx->ctx);
bin_clear_free(ctx, sizeof(*ctx));
ctx = NULL;
@@ -1527,13 +1600,30 @@ static int openssl_hmac_vector(char *digest, const u8 *key,
EVP_MAC_CTX *ctx;
size_t i, mlen;
int res;
+ const char *property_query = NULL;
if (TEST_FAIL())
return -1;
- hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
- if (!hmac)
+#ifndef CONFIG_FIPS
+ if (os_strcmp(digest, "MD5") == 0) {
+ openssl_need_md5();
+ property_query = "provider!=fips";
+ } else if (key_len < 14 && OSSL_PROVIDER_available(NULL, "fips")) {
+ /* Need to use non-FIPS provider in OpenSSL to handle cases
+ * where HMAC is used with salt that is less than 112 bits
+ * instead of the HMAC uses with an actual key. */
+ openssl_disable_fips();
+ openssl_load_default_provider_if_fips();
+ property_query = "provider!=fips";
+ }
+#endif /* CONFIG_FIPS */
+ hmac = EVP_MAC_fetch(NULL, "HMAC", property_query);
+ if (!hmac) {
+ wpa_printf(MSG_INFO, "OpenSSL: EVP_MAC_fetch(HMAC) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
return -1;
+ }
params[0] = OSSL_PARAM_construct_utf8_string("digest", digest, 0);
params[1] = OSSL_PARAM_construct_end();
@@ -1543,8 +1633,13 @@ static int openssl_hmac_vector(char *digest, const u8 *key,
if (!ctx)
return -1;
- if (EVP_MAC_init(ctx, key, key_len, params) != 1)
+ if (EVP_MAC_init(ctx, key, key_len, params) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_MAC_init(hmac,digest=%s,key_len=%zu) failed: %s",
+ digest, key_len,
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
+ }
for (i = 0; i < num_elem; i++) {
if (EVP_MAC_update(ctx, addr[i], len[i]) != 1)
@@ -1786,6 +1881,32 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
}
+#ifdef CONFIG_SHA256
+int pbkdf2_sha256(const char *passphrase, const u8 *salt, size_t salt_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (PKCS5_PBKDF2_HMAC(passphrase, os_strlen(passphrase), salt,
+ salt_len, iterations, EVP_sha256(), buflen,
+ buf) != 1)
+ return -1;
+ return 0;
+}
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+int pbkdf2_sha384(const char *passphrase, const u8 *salt, size_t salt_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (PKCS5_PBKDF2_HMAC(passphrase, os_strlen(passphrase), salt,
+ salt_len, iterations, EVP_sha384(), buflen,
+ buf) != 1)
+ return -1;
+ return 0;
+}
+#endif /* CONFIG_SHA384 */
+
+
int crypto_get_random(void *buf, size_t len)
{
if (RAND_bytes(buf, len) != 1)
@@ -1822,8 +1943,12 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
if (!emac || !cipher ||
!(ctx = EVP_MAC_CTX_new(emac)) ||
- EVP_MAC_init(ctx, key, key_len, params) != 1)
+ EVP_MAC_init(ctx, key, key_len, params) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_MAC_init(cmac,cipher=%s) failed: %s",
+ cipher, ERR_error_string(ERR_get_error(), NULL));
goto fail;
+ }
for (i = 0; i < num_elem; i++) {
if (!EVP_MAC_update(ctx, addr[i], len[i]))
@@ -2650,8 +2775,12 @@ struct crypto_ecdh * crypto_ecdh_init(int group)
goto fail;
ecdh->pkey = EVP_EC_gen(name);
- if (!ecdh->pkey)
+ if (!ecdh->pkey) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_EC_gen(group=%d) failed: %s",
+ group, ERR_error_string(ERR_get_error(), NULL));
goto fail;
+ }
done:
return ecdh;
@@ -3416,8 +3545,8 @@ struct crypto_ec_key * crypto_ec_key_gen(int group)
EVP_PKEY_CTX_set_params(ctx, params) != 1 ||
EVP_PKEY_generate(ctx, &pkey) != 1) {
wpa_printf(MSG_INFO,
- "OpenSSL: failed to generate EC keypair: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ "OpenSSL: Failed to generate EC keypair (group=%d): %s",
+ group, ERR_error_string(ERR_get_error(), NULL));
pkey = NULL;
}
@@ -3680,6 +3809,8 @@ struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
"type-specific", NULL);
if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL: OSSL_ENCODER failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
OSSL_ENCODER_CTX_free(ctx);
EVP_PKEY_free(copy);
return NULL;
@@ -4248,7 +4379,7 @@ fail:
}
-struct crypto_csr * crypto_csr_init()
+struct crypto_csr * crypto_csr_init(void)
{
return (struct crypto_csr *)X509_REQ_new();
}
@@ -4271,8 +4402,10 @@ struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
if (X509_REQ_verify((X509_REQ *)csr, pkey) != 1)
goto fail;
+ EVP_PKEY_free(pkey);
return (struct crypto_csr *)csr;
fail:
+ EVP_PKEY_free(pkey);
X509_REQ_free(csr);
return NULL;
}
@@ -4298,6 +4431,7 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
{
X509_NAME *n;
int nid;
+ int ret = -1;
switch (type) {
case CSR_NAME_CN:
@@ -4319,7 +4453,7 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
return -1;
}
- n = X509_REQ_get_subject_name((X509_REQ *) csr);
+ n = X509_NAME_new();
if (!n)
return -1;
@@ -4327,15 +4461,21 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
(unsigned char *) name,
os_strlen(name), -1, 0))
- return -1;
+ goto fail;
#else
if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
(const unsigned char *) name,
os_strlen(name), -1, 0))
- return -1;
+ goto fail;
#endif
- return 0;
+ if (X509_REQ_set_subject_name((X509_REQ *) csr, n) != 1)
+ goto fail;
+
+ ret = 0;
+fail:
+ X509_NAME_free(n);
+ return ret;
}
@@ -4365,8 +4505,8 @@ const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
size_t *len, int *type)
{
X509_ATTRIBUTE *attrib;
- ASN1_TYPE *attrib_type;
- ASN1_STRING *data;
+ const ASN1_TYPE *attrib_type;
+ const ASN1_STRING *data;
int loc;
int nid;
@@ -4793,8 +4933,12 @@ static int hpke_labeled_extract(struct hpke_context *ctx, bool kem,
if (!hctx)
return -1;
- if (EVP_MAC_init(hctx, salt, salt_len, params) != 1)
+ if (EVP_MAC_init(hctx, salt, salt_len, params) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
+ }
if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 ||
EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 ||
@@ -4902,8 +5046,12 @@ hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk,
if (!hctx)
goto fail;
- if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
+ if (EVP_MAC_init(hctx, prk, mdlen, params) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
+ }
if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1)
goto fail;
@@ -5581,4 +5729,5 @@ struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
void crypto_unload(void)
{
openssl_unload_legacy_provider();
+ openssl_unload_default_provider();
}
diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c
index 269174321bdf..d2541fde5173 100644
--- a/contrib/wpa/src/crypto/crypto_wolfssl.c
+++ b/contrib/wpa/src/crypto/crypto_wolfssl.c
@@ -18,6 +18,7 @@
#include <wolfssl/openssl/bn.h>
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/cmac.h>
#include <wolfssl/wolfcrypt/des3.h>
@@ -29,6 +30,7 @@
#include <wolfssl/wolfcrypt/md5.h>
#include <wolfssl/wolfcrypt/pkcs7.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/sha.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/sha512.h>
@@ -514,8 +516,10 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
{
int ret;
+ PRIVATE_KEY_UNLOCK();
ret = wc_PBKDF2(buf, (const byte *) passphrase, os_strlen(passphrase),
ssid, ssid_len, iterations, buflen, WC_SHA);
+ PRIVATE_KEY_LOCK();
if (ret != 0) {
if (ret == HMAC_MIN_KEYLEN_E) {
LOG_WOLF_ERROR_VA("wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
@@ -818,7 +822,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
}
break;
#endif /* NO_DES3 */
- case CRYPTO_CIPHER_ALG_RC2:
case CRYPTO_CIPHER_ALG_DES:
default:
os_free(ctx);
@@ -3412,7 +3415,7 @@ const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
{
if (!csr || !len || !type) {
LOG_INVALID_PARAMETERS();
- return NULL;;
+ return NULL;
}
switch (attr) {
@@ -3555,6 +3558,284 @@ 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)
+ return NULL;
+
+ 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)
+{
+ if (key) {
+ int err;
+
+ 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 *key_pem, size_t key_pem_len,
+ DerBuffer **key_der)
+{
+ struct DecodedCert cert;
+ DerBuffer *cert_der = NULL;
+ word32 der_key_sz = 0;
+ int err;
+
+ err = wc_PemToDer(key_pem, (long) key_pem_len, CERT_TYPE, &cert_der,
+ NULL, NULL, NULL);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+ goto fail;
+ }
+
+ wc_InitDecodedCert(&cert, cert_der->buffer, cert_der->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, &der_key_sz);
+ if (err != LENGTH_ONLY_E) {
+ LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err);
+ goto fail;
+ }
+
+ if (*key_der)
+ wc_FreeDer(key_der);
+ *key_der = NULL;
+
+ err = wc_AllocDer(key_der, der_key_sz, PUBLICKEY_TYPE, NULL);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_AllocDer, err);
+ goto fail;
+ }
+
+ err = wc_GetPubKeyDerFromCert(&cert, (*key_der)->buffer,
+ &(*key_der)->length);
+ if (err != 0) {
+ LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err);
+ goto fail;
+ }
+
+fail:
+ if (cert_der) {
+ wc_FreeDecodedCert(&cert);
+ wc_FreeDer(&cert_der);
+ }
+
+ /* caller is responsible for free'ing key_der */
+}
+
+
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
+{
+ struct crypto_rsa_key *ret = NULL;
+ unsigned char *key_pem = NULL;
+ size_t key_pem_len = 0;
+ DerBuffer *key_der = NULL;
+ int key_format = 0;
+ int err;
+ int success = 0;
+ word32 idx = 0;
+
+ key_pem = (unsigned char *) os_readfile(file, &key_pem_len);
+ if (!key_pem) {
+ LOG_WOLF_ERROR_FUNC_NULL(os_readfile);
+ goto fail;
+ }
+
+ err = wc_PemToDer(key_pem, (long) key_pem_len,
+ private_key ? PRIVATEKEY_TYPE : PUBLICKEY_TYPE,
+ &key_der, NULL, NULL, &key_format);
+ if (err != 0) {
+ if (private_key) {
+ LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+ goto fail;
+ }
+
+ /* 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(key_pem, key_pem_len, &key_der);
+ if (!key_der) {
+ LOG_WOLF_ERROR_FUNC(wc_PemToDer, err);
+ LOG_WOLF_ERROR_FUNC_NULL(read_rsa_key_from_x509);
+ goto fail;
+ }
+ }
+
+ if (private_key && key_format != RSAk) {
+ LOG_WOLF_ERROR("Private key is not RSA key");
+ goto fail;
+ }
+
+ /* No longer needed so let's free the memory early */
+ os_free(key_pem);
+ key_pem = 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(key_der->buffer, &idx, &ret->key,
+ key_der->length);
+ else
+ err = wc_RsaPublicKeyDecode(key_der->buffer, &idx, &ret->key,
+ key_der->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:
+ os_free(key_pem);
+ if (key_der)
+ wc_FreeDer(&key_der);
+ 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/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c
index d665a9983cf8..de7394a32a73 100644
--- a/contrib/wpa/src/crypto/sha256-prf.c
+++ b/contrib/wpa/src/crypto/sha256-prf.c
@@ -97,7 +97,7 @@ int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
* Mask out unused bits in the last octet if it does not use all the
* bits.
*/
- if (buf_len_bits % 8) {
+ if (pos > 0 && (buf_len_bits % 8)) {
u8 mask = 0xff << (8 - buf_len_bits % 8);
buf[pos - 1] &= mask;
}
diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h
index 8054bbe5c514..82702ae3e117 100644
--- a/contrib/wpa/src/crypto/sha256.h
+++ b/contrib/wpa/src/crypto/sha256.h
@@ -26,5 +26,7 @@ int tls_prf_sha256(const u8 *secret, size_t secret_len,
int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
const char *label, const u8 *seed, size_t seed_len,
u8 *out, size_t outlen);
+int pbkdf2_sha256(const char *passphrase, const u8 *salt, size_t salt_len,
+ int iterations, u8 *buf, size_t buflen);
#endif /* SHA256_H */
diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c
index 420e78c380cd..fdf33165506b 100644
--- a/contrib/wpa/src/crypto/sha384-prf.c
+++ b/contrib/wpa/src/crypto/sha384-prf.c
@@ -97,7 +97,7 @@ int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
* Mask out unused bits in the last octet if it does not use all the
* bits.
*/
- if (buf_len_bits % 8) {
+ if (pos > 0 && (buf_len_bits % 8)) {
u8 mask = 0xff << (8 - buf_len_bits % 8);
buf[pos - 1] &= mask;
}
diff --git a/contrib/wpa/src/crypto/sha384.h b/contrib/wpa/src/crypto/sha384.h
index d946907c675d..276cdd3770f4 100644
--- a/contrib/wpa/src/crypto/sha384.h
+++ b/contrib/wpa/src/crypto/sha384.h
@@ -26,5 +26,7 @@ int tls_prf_sha384(const u8 *secret, size_t secret_len,
int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
const char *label, const u8 *seed, size_t seed_len,
u8 *out, size_t outlen);
+int pbkdf2_sha384(const char *passphrase, const u8 *salt, size_t salt_len,
+ int iterations, u8 *buf, size_t buflen);
#endif /* SHA384_H */
diff --git a/contrib/wpa/src/crypto/sha512-prf.c b/contrib/wpa/src/crypto/sha512-prf.c
index e48cf5f05662..be458141dc54 100644
--- a/contrib/wpa/src/crypto/sha512-prf.c
+++ b/contrib/wpa/src/crypto/sha512-prf.c
@@ -97,7 +97,7 @@ int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
* Mask out unused bits in the last octet if it does not use all the
* bits.
*/
- if (buf_len_bits % 8) {
+ if (pos > 0 && (buf_len_bits % 8)) {
u8 mask = 0xff << (8 - buf_len_bits % 8);
buf[pos - 1] &= mask;
}
diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c
index e3f5b5a4227c..8ce939032e4a 100644
--- a/contrib/wpa/src/crypto/tls_gnutls.c
+++ b/contrib/wpa/src/crypto/tls_gnutls.c
@@ -62,6 +62,8 @@ struct tls_connection {
char *suffix_match;
char *domain_match;
unsigned int flags;
+
+ char *prio_str;
};
@@ -213,7 +215,9 @@ static int tls_gnutls_init_session(struct tls_global *global,
if (ret < 0)
goto fail;
- ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+ ret = gnutls_priority_set_direct(conn->session,
+ conn->prio_str ? conn->prio_str :
+ "NORMAL:-VERS-SSL3.0",
&err);
if (ret < 0) {
wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
@@ -285,6 +289,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
wpabuf_free(conn->pull_buf);
os_free(conn->suffix_match);
os_free(conn->domain_match);
+ os_free(conn->prio_str);
os_free(conn);
}
@@ -410,15 +415,18 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
TLS_CONN_DISABLE_TLSv1_1 |
- TLS_CONN_DISABLE_TLSv1_2)) {
+ TLS_CONN_DISABLE_TLSv1_2 |
+ TLS_CONN_DISABLE_TLSv1_3)) {
os_snprintf(prio_buf, sizeof(prio_buf),
- "NORMAL:-VERS-SSL3.0%s%s%s",
+ "NORMAL:-VERS-SSL3.0%s%s%s%s",
params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
":-VERS-TLS1.0" : "",
params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
":-VERS-TLS1.1" : "",
params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
- ":-VERS-TLS1.2" : "");
+ ":-VERS-TLS1.2" : "",
+ params->flags & TLS_CONN_DISABLE_TLSv1_3 ?
+ ":-VERS-TLS1.3" : "");
prio = prio_buf;
}
@@ -459,6 +467,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
err);
return -1;
}
+ os_free(conn->prio_str);
+ conn->prio_str = os_strdup(prio);
}
if (params->openssl_ecdh_curves) {
@@ -1513,7 +1523,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
conn->global->event_cb(conn->global->cb_ctx,
TLS_ALERT, &ev);
}
- /* continue */
+ /* fallthrough */
default:
wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
"-> %s", __func__, gnutls_strerror(ret));
diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c
index 17283f99817a..b14b1236031f 100644
--- a/contrib/wpa/src/crypto/tls_openssl.c
+++ b/contrib/wpa/src/crypto/tls_openssl.c
@@ -7,6 +7,10 @@
*/
#include "includes.h"
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+#include <sys/stat.h>
+#include <limits.h>
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
#ifdef CONFIG_TESTING_OPTIONS
#include <fcntl.h>
#endif /* CONFIG_TESTING_OPTIONS */
@@ -33,6 +37,8 @@
#include <openssl/core_names.h>
#include <openssl/decoder.h>
#include <openssl/param_build.h>
+#include <openssl/store.h>
+#include <openssl/provider.h>
#else /* OpenSSL version >= 3.0 */
#ifndef OPENSSL_NO_DSA
#include <openssl/dsa.h>
@@ -244,8 +250,8 @@ struct tls_connection {
BIO *ssl_in, *ssl_out;
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
ENGINE *engine; /* functional reference to the engine */
- EVP_PKEY *private_key; /* the private key if using engine */
#endif /* OPENSSL_NO_ENGINE */
+ EVP_PKEY *private_key; /* the private key if using engine/provider */
char *subject_match, *altsubject_match, *suffix_match, *domain_match;
char *check_cert_subject;
int read_alerts, write_alerts, failed;
@@ -357,6 +363,161 @@ static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl)
}
+#ifndef ANDROID
+#ifdef OPENSSL_NO_ENGINE
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static OSSL_PROVIDER *openssl_pkcs11_provider = NULL;
+#endif /* OpenSSL version >= 3.0 */
+
+static void openssl_load_pkcs11_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (openssl_pkcs11_provider)
+ return;
+
+ openssl_pkcs11_provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1);
+ if (!openssl_pkcs11_provider)
+ wpa_printf(MSG_WARNING, "PKCS11 provider not present");
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static void openssl_unload_pkcs11_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (openssl_pkcs11_provider) {
+ OSSL_PROVIDER_unload(openssl_pkcs11_provider);
+ openssl_pkcs11_provider = NULL;
+ }
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static bool openssl_can_use_provider(const char *engine_id, const char *req)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (!os_strcmp(engine_id, "pkcs11") && openssl_pkcs11_provider)
+ return true;
+
+ wpa_printf(MSG_ERROR,
+ "Cannot find OpenSSL provider for '%s' (missing '%s')",
+ req, engine_id);
+#endif /* OpenSSL version >= 3.0 */
+ return false;
+}
+
+
+static EVP_PKEY * provider_load_key(const char *uri)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_STORE_CTX *store;
+ OSSL_STORE_INFO *info;
+ EVP_PKEY *key = NULL;
+
+ if (!uri) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "Invalid NULL uri for key");
+ goto err_key;
+ }
+
+ store = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL);
+ if (!store) {
+ wpa_printf(MSG_DEBUG, "Bad uri for private key:%s", uri);
+
+ tls_show_errors(MSG_ERROR, __func__,
+ "Failed to open key store");
+ goto err_key;
+ }
+
+ if (os_strncmp(uri, "pkcs11:", 7) &&
+ os_strstr(uri, "type=private") == NULL) {
+ /* This is a workaround for OpenSSL < 3.2.0 where the code fails
+ * to correctly source public keys unless explicitly requested
+ * via an expect hint. */
+ if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PUBKEY) != 1) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "Failed to expect Public Key File");
+ goto err_store;
+ }
+ }
+
+ while (!OSSL_STORE_eof(store)) {
+ info = OSSL_STORE_load(store);
+ if (!info) {
+ if (OSSL_STORE_error(store))
+ break;
+ continue;
+ }
+ if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_PKEY)
+ key = OSSL_STORE_INFO_get1_PKEY(info);
+
+ OSSL_STORE_INFO_free(info);
+ if (key)
+ break;
+ }
+
+err_store:
+ OSSL_STORE_close(store);
+err_key:
+ if (!key)
+ wpa_printf(MSG_ERROR, "OpenSSL: Failed to load key from URI");
+
+ return key;
+#else /* OpenSSL version >= 3.0 */
+ return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static X509 * provider_load_cert(const char *cert_id)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_STORE_CTX *store;
+ OSSL_STORE_INFO *info;
+ X509 *cert = NULL;
+
+ if (!cert_id) {
+ tls_show_errors(MSG_ERROR, __func__, "Invalid NULL uri");
+ goto err_cert;
+ }
+
+ store = OSSL_STORE_open(cert_id, NULL, NULL, NULL, NULL);
+ if (!store) {
+ tls_show_errors(MSG_ERROR, __func__, "Failed to open store");
+ goto err_cert;
+ }
+
+ while (!OSSL_STORE_eof(store)) {
+ info = OSSL_STORE_load(store);
+ if (!info) {
+ if (OSSL_STORE_error(store))
+ break;
+ continue;
+ }
+ if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_CERT)
+ cert = OSSL_STORE_INFO_get1_CERT(info);
+
+ OSSL_STORE_INFO_free(info);
+ if (cert)
+ break;
+ }
+ OSSL_STORE_close(store);
+
+err_cert:
+ if (!cert)
+ tls_show_errors(MSG_ERROR, __func__,
+ "Failed to load cert from URI");
+ return cert;
+#else /* OpenSSL version >= 3.0 */
+ return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+#endif /* OPENSSL_NO_ENGINE */
+#endif /* !ANDROID */
+
+
#ifdef CONFIG_NATIVE_WINDOWS
/* Windows CryptoAPI and access to certificate stores */
@@ -871,6 +1032,79 @@ static int tls_engine_load_dynamic_generic(const char *pre[],
}
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+
+#define TRUSTED_PATH "/usr/lib/"
+
+/**
+ * tls_engine_path_trusted - Verify engine .so path is trusted for loading
+ * @path: Engine/module path supplied via configuration or D-Bus
+ * @real_path: Buffer for the canonical path (PATH_MAX bytes)
+ * Returns: 0 if trusted (real_path filled), -1 otherwise
+ *
+ * The PKCS#11/OpenSC engine and module shared object paths are loaded with
+ * dlopen() within this process, which may be running with elevated
+ * privileges. Verify that the supplied path cannot be used to load an
+ * attacker-controlled shared library by requiring, in addition to being
+ * located under the trusted path, that the target is a regular file owned by
+ * root, not writable by group or others, and that every ancestor directory up
+ * to the file is itself a directory, owned by root and not writable by group
+ * or others (otherwise a root-owned file could be swapped by whoever controls
+ * such a directory). lstat() is used for the file check so that a symlink is
+ * never followed, even though realpath() has already canonicalised the path.
+ */
+static int tls_engine_path_trusted(const char *path, char *real_path)
+{
+ struct stat st;
+ char dir[PATH_MAX];
+ char *slash, *next;
+
+ if (!path)
+ return -1;
+
+ if (!realpath(path, real_path)) {
+ wpa_printf(MSG_INFO,
+ "ENGINE: Refusing to load %s: realpath: %s",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (os_strncmp(TRUSTED_PATH, real_path, os_strlen(TRUSTED_PATH)) != 0) {
+ wpa_printf(MSG_INFO,
+ "ENGINE: Refusing to load %s: not in trusted path %s",
+ real_path, TRUSTED_PATH);
+ return -1;
+ }
+
+ if (lstat(real_path, &st) != 0 || !S_ISREG(st.st_mode) ||
+ st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH))) {
+ wpa_printf(MSG_INFO,
+ "ENGINE: Refusing to load %s: not a root-owned, non-writable regular file",
+ real_path);
+ return -1;
+ }
+
+ os_strlcpy(dir, real_path, sizeof(dir));
+ slash = dir;
+ while ((next = os_strchr(slash + 1, '/')) {
+ *next = '\0';
+ if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode) ||
+ st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH))) {
+ wpa_printf(MSG_INFO,
+ "ENGINE: Refusing to load %s: insecure ancestor directory %s",
+ real_path, dir);
+ return -1;
+ }
+ *next = '/';
+ slash = next;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
+
+
/**
* tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
* @pkcs11_so_path: pksc11_so_path from the configuration
@@ -880,6 +1114,10 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
const char *pkcs11_module_path)
{
char *engine_id = "pkcs11";
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+ char real_pkcs11_so_path[PATH_MAX];
+ char real_pkcs11_module_path[PATH_MAX];
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
const char *pre_cmd[] = {
"SO_PATH", NULL /* pkcs11_so_path */,
"ID", NULL /* engine_id */,
@@ -896,12 +1134,29 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
if (!pkcs11_so_path)
return 0;
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+ if (tls_engine_path_trusted(pkcs11_so_path, real_pkcs11_so_path) < 0)
+ return -1;
+ pre_cmd[1] = real_pkcs11_so_path;
+#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
pre_cmd[1] = pkcs11_so_path;
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
pre_cmd[3] = engine_id;
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+ if (pkcs11_module_path) {
+ if (tls_engine_path_trusted(pkcs11_module_path,
+ real_pkcs11_module_path) < 0)
+ return -1;
+ post_cmd[1] = real_pkcs11_module_path;
+ } else {
+ post_cmd[0] = NULL;
+ }
+#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
if (pkcs11_module_path)
post_cmd[1] = pkcs11_module_path;
else
post_cmd[0] = NULL;
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
pkcs11_so_path);
@@ -917,6 +1172,9 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
{
char *engine_id = "opensc";
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+ char real_opensc_so_path[PATH_MAX];
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
const char *pre_cmd[] = {
"SO_PATH", NULL /* opensc_so_path */,
"ID", NULL /* engine_id */,
@@ -928,7 +1186,13 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
if (!opensc_so_path)
return 0;
+#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH
+ if (tls_engine_path_trusted(opensc_so_path, real_opensc_so_path) < 0)
+ return -1;
+ pre_cmd[1] = real_opensc_so_path;
+#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
pre_cmd[1] = opensc_so_path;
+#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */
pre_cmd[3] = engine_id;
wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
@@ -1020,6 +1284,9 @@ void * tls_init(const struct tls_config *conf)
void openssl_load_legacy_provider(void);
openssl_load_legacy_provider();
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+ openssl_load_pkcs11_provider();
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
tls_global = context = tls_context_new(conf);
if (context == NULL)
@@ -1088,6 +1355,7 @@ void * tls_init(const struct tls_config *conf)
else
ssl = NULL;
if (ssl == NULL) {
+ tls_show_errors(MSG_INFO, "SSL_CTX_new", "init");
tls_openssl_ref_count--;
if (context != tls_global)
os_free(context);
@@ -1192,7 +1460,13 @@ void tls_deinit(void *ssl_ctx)
if (data->tls_session_lifetime > 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions");
+#if OPENSSL_VERSION_NUMBER >= 0x30400000L && \
+ !defined(LIBRESSL_VERSION_NUMBER) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+ SSL_CTX_flush_sessions_ex(ssl, 0);
+#else /* OpenSSL version >= 3.4 */
SSL_CTX_flush_sessions(ssl, 0);
+#endif /* OpenSSL version >= 3.4 */
wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions - done");
}
while ((sess_data = dl_list_first(&context->sessions,
@@ -1211,6 +1485,9 @@ void tls_deinit(void *ssl_ctx)
tls_openssl_ref_count--;
if (tls_openssl_ref_count == 0) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+ openssl_unload_pkcs11_provider();
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
@@ -1369,6 +1646,12 @@ err:
return ret;
#else /* OPENSSL_NO_ENGINE */
+#ifndef ANDROID
+ conn->private_key = provider_load_key(key_id);
+ if (!conn->private_key)
+ return -1;
+#endif /* !ANDROID */
+
return 0;
#endif /* OPENSSL_NO_ENGINE */
}
@@ -1376,12 +1659,12 @@ err:
static void tls_engine_deinit(struct tls_connection *conn)
{
-#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
- wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
if (conn->private_key) {
EVP_PKEY_free(conn->private_key);
conn->private_key = NULL;
}
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+ wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
if (conn->engine) {
#if !defined(OPENSSL_IS_BORINGSSL)
ENGINE_finish(conn->engine);
@@ -1854,8 +2137,9 @@ static int tls_match_altsubject_component(X509 *cert, int type,
gen = sk_GENERAL_NAME_value(ext, i);
if (gen->type != type)
continue;
- if (os_strlen((char *) gen->d.ia5->data) == len &&
- os_memcmp(value, gen->d.ia5->data, len) == 0)
+ if ((size_t) ASN1_STRING_length(gen->d.ia5) == len &&
+ os_memcmp(value, ASN1_STRING_get0_data(gen->d.ia5), len) ==
+ 0)
found++;
}
@@ -1901,6 +2185,8 @@ static int tls_match_altsubject(X509 *cert, const char *match)
len = os_strlen(pos);
if (tls_match_altsubject_component(cert, type, pos, len) > 0)
return 1;
+ if (!end)
+ break;
pos = end + 1;
} while (end);
@@ -1994,7 +2280,7 @@ static int match_dn_field(const X509 *cert, int nid, const char *field,
const struct tls_dn_field_order_cnt *dn_cnt)
{
int i, ret = 0, len, config_dn_field_index, match_index = 0;
- X509_NAME *name;
+ const X509_NAME *name;
len = os_strlen(value);
name = X509_get_subject_name((X509 *) cert);
@@ -2006,9 +2292,10 @@ static int match_dn_field(const X509 *cert, int nid, const char *field,
return 0;
/* Fetch value based on NID */
- for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
- X509_NAME_ENTRY *e;
- ASN1_STRING *cn;
+ for (i = -1; (i = X509_NAME_get_index_by_NID((X509_NAME *) name, nid,
+ i)) > -1;) {
+ const X509_NAME_ENTRY *e;
+ const ASN1_STRING *cn;
e = X509_NAME_get_entry(name, i);
if (!e)
@@ -2163,7 +2450,7 @@ static int tls_match_suffix_helper(X509 *cert, const char *match,
int i;
stack_index_t j;
int dns_name = 0;
- X509_NAME *name;
+ const X509_NAME *name;
wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
full ? "": "suffix ", match);
@@ -2176,10 +2463,10 @@ static int tls_match_suffix_helper(X509 *cert, const char *match,
continue;
dns_name++;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
- gen->d.dNSName->data,
- gen->d.dNSName->length);
- if (domain_suffix_match(gen->d.dNSName->data,
- gen->d.dNSName->length,
+ ASN1_STRING_get0_data(gen->d.dNSName),
+ ASN1_STRING_length(gen->d.dNSName));
+ if (domain_suffix_match(ASN1_STRING_get0_data(gen->d.dNSName),
+ ASN1_STRING_length(gen->d.dNSName),
match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
@@ -2197,10 +2484,11 @@ static int tls_match_suffix_helper(X509 *cert, const char *match,
name = X509_get_subject_name(cert);
i = -1;
for (;;) {
- X509_NAME_ENTRY *e;
- ASN1_STRING *cn;
+ const X509_NAME_ENTRY *e;
+ const ASN1_STRING *cn;
- i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+ i = X509_NAME_get_index_by_NID((X509_NAME *) name,
+ NID_commonName, i);
if (i == -1)
break;
e = X509_NAME_get_entry(name, i);
@@ -2210,8 +2498,10 @@ static int tls_match_suffix_helper(X509 *cert, const char *match,
if (cn == NULL)
continue;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
- cn->data, cn->length);
- if (domain_suffix_match(cn->data, cn->length,
+ ASN1_STRING_get0_data(cn),
+ ASN1_STRING_length(cn));
+ if (domain_suffix_match(ASN1_STRING_get0_data(cn),
+ ASN1_STRING_length(cn),
match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
full ? "Match" : "Suffix match");
@@ -2420,7 +2710,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
gen->type != GEN_URI)
continue;
- pos = os_malloc(10 + gen->d.ia5->length + 1);
+ pos = os_malloc(10 + ASN1_STRING_length(gen->d.ia5) + 1);
if (pos == NULL)
break;
altsubject[num_altsubject++] = pos;
@@ -2440,8 +2730,9 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
break;
}
- os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
- pos += gen->d.ia5->length;
+ os_memcpy(pos, ASN1_STRING_get0_data(gen->d.ia5),
+ ASN1_STRING_length(gen->d.ia5));
+ pos += ASN1_STRING_length(gen->d.ia5);
*pos = '\0';
}
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
@@ -2532,7 +2823,27 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
suffix_match = conn->suffix_match;
domain_match = conn->domain_match;
- if (!preverify_ok && !conn->ca_cert_verify)
+ if (!conn->ca_cert_verify && depth == 0 &&
+ !(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) {
+ if (X509_cmp_current_time(X509_get_notBefore(err_cert)) > 0) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Server certificate is not valid at the current time");
+ err = X509_V_ERR_CERT_NOT_YET_VALID;
+ X509_STORE_CTX_set_error(x509_ctx, err);
+ preverify_ok = 0;
+ } else if (X509_cmp_current_time(X509_get_notAfter(err_cert)) <
+ 0) {
+ wpa_printf(MSG_INFO,
+ "TLS: Server certificate has expired");
+ err = X509_V_ERR_CERT_HAS_EXPIRED;
+ X509_STORE_CTX_set_error(x509_ctx, err);
+ preverify_ok = 0;
+ }
+ }
+
+ if (!preverify_ok && !conn->ca_cert_verify &&
+ !(err == X509_V_ERR_CERT_HAS_EXPIRED ||
+ err == X509_V_ERR_CERT_NOT_YET_VALID))
preverify_ok = 1;
if (!preverify_ok && depth > 0 && conn->server_cert_only)
preverify_ok = 1;
@@ -2576,7 +2887,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
err_str = "Server certificate mismatch";
err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
preverify_ok = 0;
- } else if (!preverify_ok) {
+ } else if (!preverify_ok &&
+ err != X509_V_ERR_CERT_HAS_EXPIRED &&
+ err != X509_V_ERR_CERT_NOT_YET_VALID) {
/*
* Certificate matches pinned certificate, allow
* regardless of other problems.
@@ -2778,6 +3091,31 @@ static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
#endif /* OPENSSL_NO_STDIO */
+static int tls_add_ca_cert(SSL_CTX *ssl_ctx, X509 *cert)
+{
+ unsigned long err;
+
+ if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), cert) == 1)
+ return 0;
+
+ err = ERR_peek_error();
+
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ ERR_get_error();
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: %s - ignoring cert already in hash table error",
+ __func__);
+ return 0;
+ }
+
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add ca_cert_blob to certificate store");
+
+ return -1;
+}
+
+
static int tls_connection_ca_cert(struct tls_data *data,
struct tls_connection *conn,
const char *ca_cert, const u8 *ca_cert_blob,
@@ -2840,49 +3178,69 @@ static int tls_connection_ca_cert(struct tls_data *data,
}
if (ca_cert_blob) {
- X509 *cert = d2i_X509(NULL,
- (const unsigned char **) &ca_cert_blob,
- ca_cert_blob_len);
- if (cert == NULL) {
- BIO *bio = BIO_new_mem_buf(ca_cert_blob,
- ca_cert_blob_len);
-
- if (bio) {
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- BIO_free(bio);
- }
+ unsigned long err;
+ X509 *cert;
+ int count;
+ BIO *bio;
- if (!cert) {
- tls_show_errors(MSG_WARNING, __func__,
- "Failed to parse ca_cert_blob");
+ cert = d2i_X509(NULL,
+ (const unsigned char **) &ca_cert_blob,
+ ca_cert_blob_len);
+ if (cert) {
+ if (tls_add_ca_cert(ssl_ctx, cert) < 0) {
+ X509_free(cert);
return -1;
}
- while (ERR_get_error()) {
- /* Ignore errors from DER conversion. */
- }
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: %s - added ca_cert_blob to certificate store",
+ __func__);
+ X509_free(cert);
+ return 0;
}
- if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
- cert)) {
- unsigned long err = ERR_peek_error();
- tls_show_errors(MSG_WARNING, __func__,
- "Failed to add ca_cert_blob to "
- "certificate store");
- if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
- ERR_GET_REASON(err) ==
- X509_R_CERT_ALREADY_IN_HASH_TABLE) {
- wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
- "cert already in hash table error",
- __func__);
- } else {
+ count = 0;
+ bio = BIO_new_mem_buf(ca_cert_blob, ca_cert_blob_len);
+ if (bio) {
+ while ((cert = PEM_read_bio_X509(bio, NULL, NULL,
+ NULL))) {
+ if (count == 0) {
+ /* Ignore errors from DER conversion
+ * if we detect a certificate in PEM
+ * format. */
+ ERR_clear_error();
+ }
+ count++;
+ if (tls_add_ca_cert(ssl_ctx, cert) < 0) {
+ X509_free(cert);
+ BIO_free(bio);
+ return -1;
+ }
X509_free(cert);
- return -1;
}
+ BIO_free(bio);
}
- X509_free(cert);
- wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
- "to certificate store", __func__);
+
+ if (count == 0) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to parse ca_cert_blob");
+ return -1;
+ }
+
+ /* When the loop ends successfully, it's because of EOF */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+ ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to parse ca_cert_blob bundle");
+ return -1;
+ }
+
+ ERR_clear_error();
+
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: %s - added %d ca_cert_blob certificates to certificate store",
+ __func__, count);
return 0;
}
@@ -3799,11 +4157,17 @@ static int tls_engine_get_cert(struct tls_connection *conn,
static int tls_connection_engine_client_cert(struct tls_connection *conn,
const char *cert_id)
{
-#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
X509 *cert;
+#ifndef OPENSSL_NO_ENGINE
if (tls_engine_get_cert(conn, cert_id, &cert))
return -1;
+#else /* OPENSSL_NO_ENGINE */
+ cert = provider_load_cert(cert_id);
+ if (!cert)
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
if (!SSL_use_certificate(conn->ssl, cert)) {
tls_show_errors(MSG_ERROR, __func__,
@@ -3812,13 +4176,12 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn,
return -1;
}
X509_free(cert);
- wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+ wpa_printf(MSG_DEBUG, "ENGINE/provider: SSL_use_certificate --> "
"OK");
return 0;
-
-#else /* OPENSSL_NO_ENGINE */
+#else /* ANDROID */
return -1;
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID */
}
@@ -3826,13 +4189,19 @@ static int tls_connection_engine_ca_cert(struct tls_data *data,
struct tls_connection *conn,
const char *ca_cert_id)
{
-#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
X509 *cert;
SSL_CTX *ssl_ctx = data->ssl;
X509_STORE *store;
+#ifndef OPENSSL_NO_ENGINE
if (tls_engine_get_cert(conn, ca_cert_id, &cert))
return -1;
+#else /* OPENSSL_NO_ENGINE */
+ cert = provider_load_cert(ca_cert_id);
+ if (!cert)
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
/* start off the same as tls_connection_ca_cert */
store = X509_STORE_new();
@@ -3846,7 +4215,7 @@ static int tls_connection_engine_ca_cert(struct tls_data *data,
if (!X509_STORE_add_cert(store, cert)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
- "Failed to add CA certificate from engine "
+ "Failed to add CA certificate from engine/provider "
"to certificate store");
if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
@@ -3859,25 +4228,24 @@ static int tls_connection_engine_ca_cert(struct tls_data *data,
}
}
X509_free(cert);
- wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
- "to certificate store", __func__);
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: %s - added CA certificate from engine/provider to certificate store",
+ __func__);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
conn->ca_cert_verify = 1;
return 0;
-
-#else /* OPENSSL_NO_ENGINE */
+#else /* ANDROID */
return -1;
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID */
}
static int tls_connection_engine_private_key(struct tls_connection *conn)
{
-#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
tls_show_errors(MSG_ERROR, __func__,
- "ENGINE: cannot use private key for TLS");
+ "ENGINE/provider: cannot use private key for TLS");
return -1;
}
if (!SSL_check_private_key(conn->ssl)) {
@@ -3886,11 +4254,6 @@ static int tls_connection_engine_private_key(struct tls_connection *conn)
return -1;
}
return 0;
-#else /* OPENSSL_NO_ENGINE */
- wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
- "engine support was not compiled in");
- return -1;
-#endif /* OPENSSL_NO_ENGINE */
}
@@ -5437,6 +5800,10 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
if (engine_id && ca_cert_id) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+ if (!openssl_can_use_provider(engine_id, ca_cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
@@ -5446,6 +5813,10 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
if (engine_id && cert_id) {
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+ if (!openssl_can_use_provider(engine_id, cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
if (tls_connection_engine_client_cert(conn, cert_id))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_client_cert(conn, params->client_cert,
@@ -5454,7 +5825,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
if (engine_id && key_id) {
- wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE)
+ if (!openssl_can_use_provider(engine_id, key_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+#endif /* !ANDROID && OPENSSL_NO_ENGINE */
+ wpa_printf(MSG_DEBUG,
+ "TLS: Using private key from engine/provider");
if (tls_connection_engine_private_key(conn))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_private_key(data, conn,
@@ -5778,7 +6154,9 @@ int tls_global_set_params(void *tls_ctx,
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
-#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
+#if (defined(OPENSSL_IS_BORINGSSL) || \
+ (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || \
+ LIBRESSL_VERSION_NUMBER >= 0x4020000fL)
static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
const SSL_CIPHER **cipher, void *arg)
diff --git a/contrib/wpa/src/crypto/tls_wolfssl.c b/contrib/wpa/src/crypto/tls_wolfssl.c
index 0b2947daf9b7..87abbde90317 100644
--- a/contrib/wpa/src/crypto/tls_wolfssl.c
+++ b/contrib/wpa/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 */
@@ -63,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;
@@ -80,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];
@@ -120,6 +125,22 @@ static struct tls_context * tls_context_new(const struct tls_config *conf)
}
+static void tls_context_free(struct tls_context *context)
+{
+ if (context) {
+ 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)
{
@@ -145,6 +166,9 @@ static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
if (!data)
return -1;
+ if (!data->in_data)
+ return -2; /* WANT_READ */
+
if (get > (wpabuf_len(data->in_data) - data->consumed))
get = wpabuf_len(data->in_data) - data->consumed;
@@ -184,7 +208,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 +216,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);
}
@@ -223,11 +247,158 @@ 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 "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:" SUITEB_OLDTLS_192_CIPHERS
+#define SUITEB_TLS13_128_CIPHERS "TLS13-AES128-GCM-SHA256:" SUITEB_TLS13_192_CIPHERS
+#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 "ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:" SUITEB_TLS_192_SIGALGS
+
+#define SUITEB_TLS_192_CURVES "P-384:P-521"
+#define SUITEB_TLS_128_CURVES "P-256:" SUITEB_TLS_192_CURVES
+
+#define SUITEB_TLS_128_RSA_KEY_SZ 2048
+#define SUITEB_TLS_192_RSA_KEY_SZ 3072
+
+#define SUITEB_TLS_128_ECC_KEY_SZ 256
+#define SUITEB_TLS_192_ECC_KEY_SZ 384
+
+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;
+ bool tls13 = !(flags & TLS_CONN_DISABLE_TLSv1_3);
+ unsigned int tls13_only_mask = TLS_CONN_DISABLE_TLSv1_2 |
+ TLS_CONN_DISABLE_TLSv1_1 | TLS_CONN_DISABLE_TLSv1_0;
+ bool old_tls_only = ((flags & tls13_only_mask) != tls13_only_mask) &&
+ !tls13;
+ bool tls13only = ((flags & tls13_only_mask) == tls13_only_mask) &&
+ !(flags & TLS_CONN_DISABLE_TLSv1_3);
+ short key_sz = 0;
+ short ecc_key_sz = 0;
+
+ if (openssl_ciphers) {
+ if (os_strcmp(openssl_ciphers, "SUITEB128") == 0) {
+ if (tls13only)
+ ciphers = SUITEB_TLS13_128_CIPHERS;
+ else if (old_tls_only)
+ ciphers = SUITEB_OLDTLS_128_CIPHERS;
+ else
+ ciphers = SUITEB_TLS_128_CIPHERS;
+ sigalgs = SUITEB_TLS_128_SIGALGS;
+ key_sz = SUITEB_TLS_128_RSA_KEY_SZ;
+ ecc_key_sz = SUITEB_TLS_128_ECC_KEY_SZ;
+ curves = SUITEB_TLS_128_CURVES;
+ } else if (os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+ if (tls13only)
+ ciphers = SUITEB_TLS13_192_CIPHERS;
+ else if (old_tls_only)
+ ciphers = SUITEB_OLDTLS_192_CIPHERS;
+ else
+ ciphers = SUITEB_TLS_192_CIPHERS;
+ sigalgs = SUITEB_TLS_192_SIGALGS;
+ key_sz = SUITEB_TLS_192_RSA_KEY_SZ;
+ ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ;
+ curves = SUITEB_TLS_192_CURVES;
+ } else {
+ ciphers = openssl_ciphers;
+ }
+ } else if (flags & TLS_CONN_SUITEB) {
+ if (tls13only)
+ ciphers = SUITEB_TLS13_192_CIPHERS;
+ else if (old_tls_only)
+ ciphers = SUITEB_OLDTLS_192_CIPHERS;
+ else
+ ciphers = SUITEB_TLS_192_CIPHERS;
+ sigalgs = SUITEB_TLS_192_SIGALGS;
+ key_sz = SUITEB_TLS_192_RSA_KEY_SZ;
+ ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ;
+ 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", key_sz);
+
+ 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 (key_sz) {
+ if ((ssl_ctx &&
+ wolfSSL_CTX_SetMinRsaKey_Sz(ssl_ctx, key_sz) != 1) ||
+ (ssl && wolfSSL_SetMinRsaKey_Sz(ssl, key_sz) != 1) ||
+ (ssl_ctx &&
+ wolfSSL_CTX_SetMinDhKey_Sz(ssl_ctx, key_sz) != 1) ||
+ (ssl && wolfSSL_SetMinDhKey_Sz(ssl, key_sz) != 1)) {
+ wpa_printf(MSG_ERROR,
+ "wolfSSL: Failed to set min key size");
+ return -1;
+ }
+ }
+
+ if (ecc_key_sz) {
+ if ((ssl_ctx &&
+ wolfSSL_CTX_SetMinEccKey_Sz(ssl_ctx, ecc_key_sz) != 1) ||
+ (ssl && wolfSSL_SetMinEccKey_Sz(ssl, ecc_key_sz) != 1) ||
+ (ssl_ctx &&
+ wolfSSL_CTX_SetTmpEC_DHE_Sz(ssl_ctx,
+ ecc_key_sz / 8) != 1) ||
+ (ssl &&
+ wolfSSL_SetTmpEC_DHE_Sz(ssl, ecc_key_sz / 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);
@@ -255,16 +426,17 @@ 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;
}
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,
@@ -280,36 +452,33 @@ 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, "wolfssl: Error setting ciphersuites");
tls_deinit(ssl_ctx);
return NULL;
}
+
return ssl_ctx;
}
void tls_deinit(void *ssl_ctx)
{
- struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+ struct tls_context *context;
+ /* Need to cast the const away to be able to free this */
+ context = (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;
}
}
@@ -351,8 +520,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 = ssl_ctx_get_tls_context(ssl_ctx);
/* Need randoms post-hanshake for EAP-FAST, export key and deriving
* session ID in EAP methods. */
@@ -378,6 +547,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);
@@ -427,7 +597,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;
@@ -461,6 +632,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;
}
@@ -819,6 +998,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;
}
@@ -838,6 +1019,148 @@ 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_INFO,
+ "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)
+ * @processed_nids: 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 *processed_nids)
+{
+ 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; processed_nids[i] != NID_undef && i < DN_FIELD_LEN; i++) {
+ if (processed_nids[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;
+ }
+ processed_nids[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 processed_nids[DN_FIELD_LEN], i;
+
+ for (i = 0; i < DN_FIELD_LEN; i++)
+ processed_nids[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, processed_nids)) {
+ wpa_printf(MSG_INFO, "wolfSSL: No match for DN '%s'",
+ field);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
{
struct wpabuf *buf = NULL;
@@ -845,7 +1168,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;
@@ -859,7 +1182,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;
@@ -877,13 +1200,44 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn,
}
+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,
const char *subject)
{
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;
@@ -964,6 +1318,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);
@@ -979,8 +1334,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);
@@ -996,7 +1352,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;
@@ -1069,6 +1425,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'",
@@ -1082,7 +1440,19 @@ 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);
@@ -1116,8 +1486,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) {
@@ -1129,31 +1497,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);
@@ -1237,15 +1580,9 @@ 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,
@@ -1262,6 +1599,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;
}
@@ -1272,19 +1610,24 @@ 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);
}
@@ -1296,7 +1639,8 @@ 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) {
+ params->domain_match,
+ params->check_cert_subject) < 0) {
wpa_printf(MSG_INFO, "Error setting subject match");
return -1;
}
@@ -1324,13 +1668,17 @@ 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, "wolfssl: Error setting ciphersuites");
+ return -1;
+ }
+
+ if (params->openssl_ecdh_curves &&
+ wolfSSL_set1_curves_list(conn->ssl, params->openssl_ecdh_curves) !=
+ 1) {
+ wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'",
+ params->openssl_ecdh_curves);
return -1;
}
@@ -1524,10 +1872,25 @@ 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)
{
+ /* Need to cast away const as this is one of the only places
+ * where we should modify it */
+ struct tls_context *context =
+ (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'",
@@ -1556,35 +1919,74 @@ 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, "wolfssl: Error setting ciphersuites");
return -1;
}
- if (params->openssl_ecdh_curves) {
- wpa_printf(MSG_INFO,
- "wolfSSL: openssl_ecdh_curves not supported");
+ if (params->openssl_ecdh_curves &&
+ wolfSSL_CTX_set1_curves_list((WOLFSSL_CTX *) tls_ctx,
+ params->openssl_ecdh_curves) != 1) {
+ wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'",
+ params->openssl_ecdh_curves);
return -1;
}
#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) &&
+ 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 */
@@ -1610,12 +2012,13 @@ 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;
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;
@@ -1629,7 +2032,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, 0);
+ 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
@@ -1645,7 +2048,7 @@ 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? */
+ tls_set_conn_flags(conn->ssl, flags);
return 0;
}
@@ -1692,12 +2095,28 @@ 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++;
}
+
+ /* Generate extra events */
+ if (err == OCSP_CERT_REVOKED ||
+ err == BAD_CERTIFICATE_STATUS_ERROR ||
+ err == OCSP_CERT_REVOKED) {
+ char buf[256];
+ WOLFSSL_X509 *err_cert;
+
+ 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;
@@ -1866,11 +2285,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
char buf[128], *pos, *end;
u8 *c;
int ret;
+ bool set_sig_algs = false;
if (!conn || !conn->ssl || !ciphers)
return -1;
- buf[0] = '\0';
+ buf[0] = buf[1] = '\0';
pos = buf;
end = pos + sizeof(buf);
@@ -1890,6 +2310,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";
+ set_sig_algs = true;
break;
case TLS_CIPHER_RSA_DHE_AES256_SHA:
suite = "DHE-RSA-AES256-SHA";
@@ -1910,10 +2331,16 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
c++;
}
- wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+ /* +1 to skip the ":" */
+ if (handle_ciphersuites(NULL, conn->ssl, buf + 1, conn->flags) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "wolfssl: Cipher suite configuration failed");
+ return -1;
+ }
- if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
- wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+ if (set_sig_algs &&
+ wolfSSL_set1_sigalgs_list(conn->ssl, SUITEB_TLS_128_SIGALGS) != 1) {
+ wpa_printf(MSG_DEBUG, "wolfssl: Sigalg configuration failed");
return -1;
}
@@ -1924,34 +2351,19 @@ 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);
+ 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;
- 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;
}
@@ -2273,13 +2685,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);
@@ -2302,7 +2714,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);
}
diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h
index 3e77474899eb..ea5e28be0b74 100644
--- a/contrib/wpa/src/drivers/driver.h
+++ b/contrib/wpa/src/drivers/driver.h
@@ -22,11 +22,16 @@
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
+#include "common/nan_defs.h"
#ifdef CONFIG_MACSEC
#include "pae/ieee802_1x_kay.h"
#endif /* CONFIG_MACSEC */
#include "utils/list.h"
+struct nan_subscribe_params;
+struct nan_publish_params;
+struct pr_pasn_ranging_params;
+
#define HOSTAPD_CHAN_DISABLED 0x00000001
#define HOSTAPD_CHAN_NO_IR 0x00000002
#define HOSTAPD_CHAN_RADAR 0x00000008
@@ -47,6 +52,7 @@
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+#define HOSTAPD_CHAN_AUTO_BW 0x00040000
/* Allowed bandwidth mask */
enum hostapd_chan_width_attr {
@@ -155,6 +161,12 @@ struct hostapd_channel_data {
* need to set this)
*/
long double interference_factor;
+
+ /**
+ * interference_bss_based - Indicates whether the interference was
+ * calculated from number of BSSs
+ */
+ bool interference_bss_based;
#endif /* CONFIG_ACS */
/**
@@ -208,6 +220,7 @@ struct eht_capabilities {
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
+#define HOSTAPD_MODE_FLAG_EHT_INFO_KNOWN BIT(3)
enum ieee80211_op_mode {
@@ -317,6 +330,27 @@ struct hostapd_hw_modes {
};
+/**
+ * struct hostapd_multi_hw_info: Supported multiple underlying hardware info
+ */
+struct hostapd_multi_hw_info {
+ /**
+ * hw_idx - Hardware index
+ */
+ u8 hw_idx;
+
+ /**
+ * start_freq - Frequency range start in MHz
+ */
+ int start_freq;
+
+ /**
+ * end_freq - Frequency range end in MHz
+ */
+ int end_freq;
+};
+
+
#define IEEE80211_CAP_ESS 0x0001
#define IEEE80211_CAP_IBSS 0x0002
#define IEEE80211_CAP_PRIVACY 0x0010
@@ -357,6 +391,8 @@ struct hostapd_hw_modes {
* of TSF of the BSS specified by %tsf_bssid.
* @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
* @beacon_newer: Whether the Beacon frame data is known to be newer
+ * @mlo_tput_accumulated: Whether the scan result throughput is accumulated
+ * (used during scan result processing)
* @ie_len: length of the following IE field in octets
* @beacon_ie_len: length of the following Beacon IE field in octets
*
@@ -391,6 +427,7 @@ struct wpa_scan_res {
u64 parent_tsf;
u8 tsf_bssid[ETH_ALEN];
bool beacon_newer;
+ bool mlo_tput_accumulated;
size_t ie_len;
size_t beacon_ie_len;
/* Followed by ie_len + beacon_ie_len octets of IE data */
@@ -867,6 +904,14 @@ struct hostapd_freq_params {
bool eht_enabled;
/**
+ * punct_bitmap - Preamble puncturing bitmap
+ * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+ * channel with the lowest frequency. A bit set to 1 indicates that the
+ * subchannel is punctured, otherwise active.
+ */
+ u16 punct_bitmap;
+
+ /**
* link_id: If >=0 indicates the link of the AP MLD to configure
*/
int link_id;
@@ -1363,6 +1408,42 @@ struct wpa_driver_associate_params {
* mld_params - MLD association parameters
*/
struct wpa_driver_mld_params mld_params;
+
+ /**
+ * rsn_overriding - wpa_supplicant RSN overriding support
+ */
+ bool rsn_overriding;
+
+ /**
+ * spp_amsdu - SPP A-MSDU used on this connection
+ */
+ bool spp_amsdu;
+
+ /**
+ * bssid_filter - Allowed BSSIDs for the current association
+ * This can be %NULL to indicate no constraint. */
+ const u8 *bssid_filter;
+
+ /**
+ * bssid_filter_count - Number of allowed BSSIDs
+ */
+ unsigned int bssid_filter_count;
+
+ /**
+ * p2p_mode - P2P R1 only, P2P R2 only, or PCC mode
+ */
+ enum wpa_p2p_mode p2p_mode;
+
+ /**
+ * eppke_supported - Whether EPPKE authentication is supported
+ */
+ bool eppke_supported;
+
+ /**
+ * ieee8021x_auth_supported - Whether IEEE 802.1X authentication in
+ * Authentication frames is supported by wpa_supplicant
+ */
+ bool ieee8021x_auth_supported;
};
enum hide_ssid {
@@ -1403,6 +1484,72 @@ struct unsol_bcast_probe_resp {
size_t unsol_bcast_probe_resp_tmpl_len;
};
+struct mbssid_data {
+ /**
+ * mbssid_tx_iface - Transmitting interface of the MBSSID set
+ */
+ const char *mbssid_tx_iface;
+
+ /**
+ * mbssid_tx_iface_linkid - Link ID of the transmitting interface if
+ * it is part of an MLD. Otherwise, -1.
+ */
+ int mbssid_tx_iface_linkid;
+
+ /**
+ * mbssid_index - The index of this BSS in the MBSSID set
+ */
+ unsigned int mbssid_index;
+
+ /**
+ * mbssid_elem - Buffer containing all MBSSID elements
+ */
+ u8 *mbssid_elem;
+
+ /**
+ * mbssid_elem_len - Total length of all MBSSID elements
+ */
+ size_t mbssid_elem_len;
+
+ /**
+ * mbssid_elem_count - The number of MBSSID elements
+ */
+ u8 mbssid_elem_count;
+
+ /**
+ * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+ * Kernel will use these offsets to generate multiple BSSID beacons.
+ */
+ u8 **mbssid_elem_offset;
+
+ /**
+ * ema - Enhanced MBSSID advertisements support.
+ */
+ bool ema;
+
+ /**
+ * rnr_elem - This buffer contains all of reduced neighbor report (RNR)
+ * elements
+ */
+ u8 *rnr_elem;
+
+ /**
+ * rnr_elem_len - Length of rnr_elem buffer
+ */
+ size_t rnr_elem_len;
+
+ /**
+ * rnr_elem_count - Number of RNR elements
+ */
+ u8 rnr_elem_count;
+
+ /**
+ * rnr_elem_offset - The offsets to the elements in rnr_elem.
+ * The driver will use these to include RNR elements in EMA beacons.
+ */
+ u8 **rnr_elem_offset;
+};
+
struct wpa_driver_ap_params {
/**
* head - Beacon head from IEEE 802.11 header to IEs before TIM IE
@@ -1607,11 +1754,6 @@ struct wpa_driver_ap_params {
int disable_dgaf;
/**
- * osen - Whether OSEN security is enabled
- */
- int osen;
-
- /**
* freq - Channel parameters for dynamic bandwidth changes
*/
struct hostapd_freq_params *freq;
@@ -1746,40 +1888,11 @@ struct wpa_driver_ap_params {
size_t fd_frame_tmpl_len;
/**
- * mbssid_tx_iface - Transmitting interface of the MBSSID set
- */
- const char *mbssid_tx_iface;
-
- /**
- * mbssid_index - The index of this BSS in the MBSSID set
- */
- unsigned int mbssid_index;
-
- /**
- * mbssid_elem - Buffer containing all MBSSID elements
- */
- u8 *mbssid_elem;
-
- /**
- * mbssid_elem_len - Total length of all MBSSID elements
- */
- size_t mbssid_elem_len;
-
- /**
- * mbssid_elem_count - The number of MBSSID elements
- */
- u8 mbssid_elem_count;
-
- /**
- * mbssid_elem_offset - Offsets to elements in mbssid_elem.
- * Kernel will use these offsets to generate multiple BSSID beacons.
- */
- u8 **mbssid_elem_offset;
-
- /**
- * ema - Enhanced MBSSID advertisements support.
+ * mbssid - MBSSID element related params for Beacon frames
+ *
+ * This is used to add MBSSID element in beacon data.
*/
- bool ema;
+ struct mbssid_data mbssid;
/**
* punct_bitmap - Preamble puncturing bitmap
@@ -1789,27 +1902,6 @@ struct wpa_driver_ap_params {
*/
u16 punct_bitmap;
- /**
- * rnr_elem - This buffer contains all of reduced neighbor report (RNR)
- * elements
- */
- u8 *rnr_elem;
-
- /**
- * rnr_elem_len - Length of rnr_elem buffer
- */
- size_t rnr_elem_len;
-
- /**
- * rnr_elem_count - Number of RNR elements
- */
- unsigned int rnr_elem_count;
-
- /**
- * rnr_elem_offset - The offsets to the elements in rnr_elem.
- * The driver will use these to include RNR elements in EMA beacons.
- */
- u8 **rnr_elem_offset;
/* Unsolicited broadcast Probe Response data */
struct unsol_bcast_probe_resp ubpr;
@@ -1894,6 +1986,12 @@ struct wpa_driver_set_key_params {
const char *ifname;
/**
+ * own_addr - Own MAC address identifying the virtual interface for
+ * the key operation, or %NULL to use the default interface (ifname)
+ */
+ const u8 *own_addr;
+
+ /**
* alg - Encryption algorithm
*
* (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
@@ -1988,10 +2086,17 @@ struct wpa_driver_set_key_params {
* %KEY_FLAG_GROUP_TX_DEFAULT
* GTK key valid for TX only, immediately taking over TX.
* %KEY_FLAG_PAIRWISE_RX_TX
- * Pairwise key immediately becoming the active pairwise key.
+ * Pairwise key immediately becoming the active pairwise key. If this
+ * key was previously set as an alternative RX-only key with
+ * %KEY_FLAG_PAIRWISE_RX | %KEY_FLAG_NEXT, the alternative RX-only key
+ * is taken into use for both TX and RX without changing the RX counter
+ * values.
* %KEY_FLAG_PAIRWISE_RX
* Pairwise key not yet valid for TX. (Only usable when Extended
- * Key ID is supported by the driver.)
+ * Key ID is supported by the driver or when configuring the next TK
+ * for RX-only with %KEY_FLAG_NEXT in which case the new TK can be used
+ * as an alternative key for decrypting received frames without
+ * replacing the possibly already configured old TK.)
* %KEY_FLAG_PAIRWISE_RX_TX_MODIFY
* Enable TX for a pairwise key installed with
* KEY_FLAG_PAIRWISE_RX.
@@ -2007,6 +2112,21 @@ struct wpa_driver_set_key_params {
*
* Set to a valid Link ID (0-14) when applicable, otherwise -1. */
int link_id;
+
+ /**
+ * ltf_keyseed_len - Length of the LTF keyseed in octets
+ *
+ * Set to 0 if no LTF keyseed is provided.
+ */
+ u8 ltf_keyseed_len;
+
+ /**
+ * ltf_keyseed - LTF keyseed for secure ranging (IEEE 802.11az)
+ *
+ * Used to configure the secure LTF key seed for a peer measurement
+ * session. Set to NULL if not applicable.
+ */
+ const u8 *ltf_keyseed;
};
enum wpa_driver_if_type {
@@ -2072,11 +2192,106 @@ enum wpa_driver_if_type {
*/
WPA_IF_NAN,
+ /*
+ * WPA_IF_NAN_DATA - NAN Data interface
+ */
+ WPA_IF_NAN_DATA,
+
+ /*
+ * WPA_IF_PD - Proxymity Detection (PD) Device
+ */
+ WPA_IF_PD,
+
/* keep last */
WPA_IF_MAX
};
/**
+ * struct nan_capa - NAN capabilities
+ *
+ * @drv_flags: NAN capability flags (WPA_DRIVER_FLAGS_NAN_*)
+ * @num_radios: Maximum number of NAN radios
+ * @sched_chans: Maximum number of channels in NAN schedule (per map)
+ * @slot_duration: NAN schedule bitmap slot duration (16, 32, 64, or 128) in TUs
+ * @schedule_period: Schedule period (powers of 2 in range: 128-8192) in TUs
+ * @max_channel_switch_time: Max channel switch time in microseconds
+ * @num_antennas: Number of antennas (lower nibble TX, upper nibble RX)
+ * @op_modes: NAN capability operation modes
+ * @dev_capa: NAN device capabilities
+ * @ht_capab: HT capabilities information as defined in IEEE Std 802.11-2024,
+ * 9.4.2.54.2 (HT Capability Information field)
+ * @ht_ampdu_params: HT A-MPDU parameters as defined in IEEE Std 802.11-2024,
+ * 9.4.2.54.3 (A-MPDU Parameters field)
+ * @ht_mcs_set: HT MCS set as defined in IEEE Std 802.11-2024,9.4.2.54.4
+ * (Supported MCS Set field)
+ * @vht_capab: VHT capabilities information as defined in IEEE Std 802.11-2024,
+ * 9.4.2.156.2 (VHT Capabilities Information field)
+ * @vht_mcs_set: VHT MCS set as defined in IEEE Std 802.11-2024, 9.4.2.156.3
+ * (Supported VHT-MCS and NSS Set field)
+ * @vht_valid: Whether &vht_capab and &vht_mcs_set are both valid
+ * @he_capab: HE capabilities. See &struct he_capabilities.
+ * @he_valid: Whether HE capabilities are valid
+ *
+ * For the schedule capabilities, even if the driver/device supports multiple
+ * options, only a single option should be selected. For example, if both 16 TU
+ * and 32 TU slot durations are supported, the driver should report the shortest
+ * supported slot duration (16 TU). For schedule period, the driver should
+ * report the maximum supported period, as longer periods can always be
+ * represented by repetitions of the shorter schedule bitmap. In any case,
+ * 512/16 configuration is recommended for better interoperability.
+ *
+ * As for the PHY capabilities, HT must be supported for NAN Data path, and
+ * without valid HT capabilities NAN Data path would be disabled.
+ *
+ * TODO: For now support only a single PHY capabilities configuration. This
+ * might need to be extended to support multiple configurations in the future.
+ */
+struct nan_capa {
+/* Driver supports dual band NAN operation */
+#define WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND 0x00000001
+/* Driver supports NAN synchronization configuration */
+#define WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG 0x00000002
+/* Driver supports DW notifications and SDF TX/RX over NAN device interface */
+#define WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE 0x00000004
+/** Driver supports NAN Data path */
+#define WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP 0x00000008
+/** Driver supports NAN beacon protection */
+#define WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT 0x00000010
+ u32 drv_flags;
+ u8 num_radios;
+ u8 sched_chans;
+ u8 slot_duration;
+ u16 schedule_period;
+ u16 max_channel_switch_time;
+ u8 num_antennas;
+ u8 op_modes;
+ u8 dev_capa;
+
+ u16 ht_capab;
+ u8 ht_mcs_set[16];
+ u8 ht_ampdu_params;
+
+ u32 vht_capab;
+ u8 vht_mcs_set[8];
+ bool vht_valid;
+
+ struct he_capabilities he_capab;
+ bool he_valid;
+};
+
+/* Bit index values for pd_bandwidths bitmap */
+#define WPA_PR_CHAN_WIDTH_20 0
+#define WPA_PR_CHAN_WIDTH_40 1
+#define WPA_PR_CHAN_WIDTH_80 2
+#define WPA_PR_CHAN_WIDTH_80P80 3
+#define WPA_PR_CHAN_WIDTH_160 4
+
+/* Bit index values for pd_preambles bitmap */
+#define WPA_PR_PREAMBLE_HT 0
+#define WPA_PR_PREAMBLE_VHT 1
+#define WPA_PR_PREAMBLE_HE 2
+
+/**
* struct wpa_driver_capa - Driver capability information
*/
struct wpa_driver_capa {
@@ -2103,7 +2318,6 @@ struct wpa_driver_capa {
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000
#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000
-#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000
#define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY 0x01000000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY 0x02000000
/** Bitfield of supported key management suites */
@@ -2330,6 +2544,38 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP 0x0000000000100000ULL
/** Driver supports TWT responder in HT and VHT modes */
#define WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER 0x0000000000200000ULL
+/** Driver supports RSN override elements */
+#define WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA 0x0000000000400000ULL
+/** Driver supports NAN USD offload */
+#define WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD 0x0000000000800000ULL
+/** Driver/device supports SPP A-MSDUs */
+#define WPA_DRIVER_FLAGS2_SPP_AMSDU 0x0000000001000000ULL
+/** Driver supports P2P V2 */
+#define WPA_DRIVER_FLAGS2_P2P_FEATURE_V2 0x0000000002000000ULL
+/** Driver supports P2P PCC mode */
+#define WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE 0x0000000004000000ULL
+/** Driver supports arbitrary channel width changes in AP mode */
+#define WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE 0x0000000008000000ULL
+/** Driver supports FTM initiator functionality */
+#define WPA_DRIVER_FLAGS2_FTM_INITIATOR 0x0000000010000000ULL
+/** Driver supports non-trigger based ranging responder functionality */
+#define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_RESPONDER 0x0000000020000000ULL
+/** Driver supports non-trigger based ranging initiator functionality */
+#define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR 0x0000000040000000ULL
+/** Driver supports NAN Device interface and NAN Synchronization */
+#define WPA_DRIVER_FLAGS2_SUPPORT_NAN 0x0000000080000000ULL
+/** Driver supports P2P assisted DFS */
+#define WPA_DRIVER_FLAGS2_P2P_ASSISTED_DFS 0x0000000100000000ULL
+/** Driver supports (Re)Association Request/Response frame encryption */
+#define WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION 0x0000000200000000ULL
+/** Driver supports EPPKE authentication */
+#define WPA_DRIVER_FLAGS2_EPPKE 0x0000000400000000ULL
+/** Driver supports IEEE 802.1X authentication in Authentication frames */
+#define WPA_DRIVER_FLAGS2_802_1X_AUTH 0x0000000800000000ULL
+/** Driver supports PMKSA caching privacy */
+#define WPA_DRIVER_FLAGS2_PMKSA_PRIVACY 0x0000001000000000ULL
+/** Driver supports MAC address filter for remain-on-channel */
+#define WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER 0x0000002000000000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2450,6 +2696,58 @@ struct wpa_driver_capa {
unsigned int mbssid_max_interfaces;
/* Maximum profile periodicity for enhanced MBSSID advertisement */
unsigned int ema_max_periodicity;
+
+ /* Maximum number of bytes of extra IE(s) that can be added to Probe
+ * Request frames */
+ size_t max_probe_req_ie_len;
+
+ /* EDCA based ranging capabilities */
+ u8 max_tx_antenna;
+ u8 max_rx_antenna;
+ u32 edca_min_ranging_interval;
+
+ /* NTB based ranging capabilities */
+ u8 max_tx_ltf_repetations;
+ u8 max_rx_ltf_repetations;
+ u8 max_tx_ltf_total;
+ u8 max_rx_ltf_total;
+ u8 max_rx_sts_le_80;
+ u8 max_rx_sts_gt_80;
+ u8 max_tx_sts_le_80;
+ u8 max_tx_sts_gt_80;
+ u32 ntb_min_ranging_interval;
+
+ /* Peer measurement capabilities */
+ u32 pmsr_max_peers;
+ u8 max_ftms_per_burst;
+ bool concurrent_ista_rsta;
+ bool support_6ghz;
+ bool asap_support;
+ u32 pd_preambles;
+ u32 pd_bandwidths;
+
+ struct {
+ bool support_ntb;
+ bool support_tb;
+ bool support_edca;
+ u32 max_peers;
+ } ista;
+
+ struct {
+ bool support_ntb;
+ bool support_tb;
+ bool support_edca;
+ u32 max_peers;
+ } rsta;
+
+ struct {
+ bool infra_support;
+ bool pd_support;
+ } ranging_type;
+
+#ifdef CONFIG_NAN
+ struct nan_capa nan_capa;
+#endif /* CONFIG_NAN */
};
@@ -2539,6 +2837,9 @@ struct hostapd_sta_add_params {
size_t eht_capab_len;
u32 flags; /* bitmask of WPA_STA_* flags */
u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_ENC_ASSOC
+ bool epp_sta;
+#endif /* CONFIG_ENC_ASSOC */
#ifdef CONFIG_MESH
enum mesh_plink_state plink_state;
u16 peer_aid;
@@ -2556,6 +2857,16 @@ struct hostapd_sta_add_params {
bool mld_link_sta;
s8 mld_link_id;
const u8 *mld_link_addr;
+ u16 eml_cap;
+
+#ifdef CONFIG_NAN
+ /*
+ * For a station added to a NAN Data Interface (NDI) indicate the
+ * address of the NAN Management Interface (NMI) to which this station
+ * belongs.
+ */
+ const u8 *nmi_addr;
+#endif /* CONFIG_NAN */
};
struct mac_address {
@@ -2603,6 +2914,7 @@ struct wpa_bss_params {
#define WPA_STA_TDLS_PEER BIT(4)
#define WPA_STA_AUTHENTICATED BIT(5)
#define WPA_STA_ASSOCIATED BIT(6)
+#define WPA_STA_SPP_AMSDU BIT(7)
enum tdls_oper {
TDLS_DISCOVERY_REQ,
@@ -2671,6 +2983,22 @@ struct wpa_mlo_signal_info {
};
/**
+ * struct wpa_mlo_reconfig_info - Information about user-requested add and/or
+ * remove setup links for the current MLO association.
+ *
+ * @add_links: Bitmask of links to be added
+ * @add_link_bssid: Array of BSSIDs for the links to be added
+ * @add_link_freq: Array of frequencies for the links to be added
+ * @delete_links: Bitmask of links to be removed
+ */
+struct wpa_mlo_reconfig_info {
+ u16 add_links;
+ u8 add_link_bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+ int add_link_freq[MAX_NUM_MLD_LINKS];
+ u16 delete_links;
+};
+
+/**
* struct wpa_channel_info - Information about the current channel
* @frequency: Center frequency of the primary 20 MHz channel
* @chanwidth: Width of the current operating channel
@@ -2706,6 +3034,7 @@ struct wpa_channel_info {
* @proberesp_ies_len: Length of proberesp_ies in octets
* @proberesp_ies_len: Length of proberesp_ies in octets
* @probe_resp_len: Length of probe response template (@probe_resp)
+ * @mbssid: MBSSID element(s) to add into Beacon frames
*/
struct beacon_data {
u8 *head, *tail;
@@ -2719,6 +3048,8 @@ struct beacon_data {
size_t proberesp_ies_len;
size_t assocresp_ies_len;
size_t probe_resp_len;
+
+ struct mbssid_data mbssid;
};
/**
@@ -2730,7 +3061,6 @@ struct beacon_data {
* @beacon_after: Next beacon/probe resp/asooc resp info
* @counter_offset_beacon: Offset to the count field in beacon's tail
* @counter_offset_presp: Offset to the count field in probe resp.
- * @punct_bitmap - Preamble puncturing bitmap
* @link_id: Link ID to determine the link for MLD; -1 for non-MLD
* @ubpr: Unsolicited broadcast Probe Response frame data
*/
@@ -2745,7 +3075,6 @@ struct csa_settings {
u16 counter_offset_beacon[2];
u16 counter_offset_presp[2];
- u16 punct_bitmap;
int link_id;
struct unsol_bcast_probe_resp ubpr;
@@ -2904,6 +3233,16 @@ enum wpa_drv_update_connect_params_mask {
* from user space to the driver.
* @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
* @mld_addr: AP's MLD address or %NULL if MLO is not used
+ * @auth_alg: Authentication algorithm to be used
+ * @pairwise_cipher: Pairwise cipher to be used, WPA_CIPHER_*
+ * @group_cipher: Group cipher to be used, WPA_CIPHER_*
+ * @group_mgmt_cipher: Group management cipher to be used, WPA_CIPHER_*
+ * @rsn_capab: RSN capabilities
+ * @rsnxe_data: Extended RSN capabilities data
+ * @rsnxe_data_len: Length of Extended RSN capabilities data in octets
+ * @kck: Key Confirmation Key (KCK) to pass to driver for IEEE 802.1X
+ * Authentication offload
+ * @kck_len: Length of the KCK
*/
struct external_auth {
enum {
@@ -2917,6 +3256,16 @@ struct external_auth {
u16 status;
const u8 *pmkid;
const u8 *mld_addr;
+ unsigned int auth_alg;
+ int pairwise_cipher;
+ int group_cipher;
+ int group_mgmt_cipher;
+ u16 rsn_capab;
+ const u8 *rsnxe_data;
+ size_t rsnxe_data_len;
+
+ const u8 *kck;
+ size_t kck_len;
};
#define WPAS_MAX_PASN_PEERS 10
@@ -2943,6 +3292,14 @@ enum pasn_status {
* @akmp: Authentication key management protocol type supported.
* @cipher: Cipher suite.
* @group: Finite cyclic group. Default group used is 19 (ECC).
+ * @temporary_network: Indicates if a temporary network was created to perform
+ * PASN authentication.
+ * @password: Password of user requested network.
+ * @comeback_len: Length of the comeback cookie data.
+ * @comeback: Comeback cookie data that may be present in case a temporary
+ * rejection is received from the AP.
+ * @comeback_after: The time after which the STA can try for PASN handshake in
+ * case of temporary rejection.
* @ltf_keyseed_required: Indicates whether LTF keyseed generation is required
* @status: PASN response status, %PASN_STATUS_SUCCESS for successful
* authentication, use %PASN_STATUS_FAILURE if PASN authentication
@@ -2956,6 +3313,11 @@ struct pasn_peer {
int akmp;
int cipher;
int group;
+ bool temporary_network;
+ char *password;
+ size_t comeback_len;
+ u8 *comeback;
+ u16 comeback_after;
bool ltf_keyseed_required;
enum pasn_status status;
};
@@ -3043,6 +3405,135 @@ struct driver_sta_mlo_info {
};
/**
+ * struct nan_band_config - NAN band specific configuration
+ *
+ * For details on NAN band configuration, see the Wi-Fi Aware Specification
+ *
+ * @frequency: Frequency in MHz for the band
+ * @rssi_close: RSSI close threshold used for NAN state transition algorithm
+ * @rssi_middle: RSSI middle threshold used for NAN state transition algorithm
+ * @awake_dw_interval: Committed DW information
+ * @disable_scan: Disable scan for this band
+ */
+struct nan_band_config {
+ u16 frequency;
+ s8 rssi_close;
+ s8 rssi_middle;
+ u8 awake_dw_interval;
+ bool disable_scan;
+};
+
+/**
+ * struct nan_cluster_config - NAN cluster configuration
+ *
+ * @master_pref: Master preference for the cluster
+ * @dual_band: Dual band operation enabled
+ * @enable_dw_notif: Enable discovery window notifications
+ * @cluster_id: Cluster ID for the NAN cluster
+ * @scan_period: Period for NAN scan in seconds
+ * @scan_dwell_time: Dwell time for NAN scan in TUs per channel
+ * @discovery_beacon_interval: Interval for discovery beacon in TUs
+ * @low_band_cfg: Configuration for low band NAN operation
+ * @high_band_cfg: Configuration for high band NAN operation
+ * @extra_nan_attrs: Extra NAN attributes to be included in NAN beacons
+ * @extra_nan_attrs_len: Length of the extra NAN attributes
+ * @vendor_elems: Vendor specific elements to be included in NAN beacons
+ * @vendor_elems_len: Length of the vendor specific elements
+ */
+struct nan_cluster_config {
+ u8 master_pref;
+ u8 dual_band;
+ bool enable_dw_notif;
+
+ u8 cluster_id[ETH_ALEN];
+ u16 scan_period;
+ u16 scan_dwell_time;
+ u8 discovery_beacon_interval;
+ struct nan_band_config low_band_cfg;
+ struct nan_band_config high_band_cfg;
+ const u8 *extra_nan_attrs;
+ size_t extra_nan_attrs_len;
+ const u8 *vendor_elems;
+ size_t vendor_elems_len;
+};
+
+/**
+ * Even if the device supports more channels, 4 channels should be enough
+ * for any practical purpose
+ */
+#define MAX_NUM_NAN_SCHEDULE_CHANNELS 4
+
+#define MAX_NUM_NAN_MAPS 2
+
+/**
+ * struct nan_schedule_config - NAN schedule configuration
+ *
+ * @deferred: true if the driver should announce the schedule update to peers
+ * before applying it
+ * @num_channels: Number of channels in the schedule
+ * @channels: Channel specific schedule information
+ * @avail_attr: NAN Availability attribute as defined in Wi-Fi Aware spec v4.0,
+ * Table 93
+ */
+struct nan_schedule_config {
+ bool deferred;
+ u8 num_channels;
+
+ /**
+ * channels - Channel specific schedule information
+ *
+ * @freq: Frequency in MHz
+ * @center_freq1: Center frequency 1 in MHz
+ * @center_freq2: Center frequency 2 in MHz
+ * @bandwidth: Channel bandwidth
+ * @time_bitmap: Bitmap indicating the committed availability
+ * on the channel.
+ * @rx_nss: Number of spatial streams supported for RX on the
+ * channel
+ * @chan_entry: Channel Entry as defined in Wi-Fi Aware spec v4.0,
+ * Table 100 (Channel Entry format for the NAN Availability
+ * attribute).
+ *
+ * Note: Time bitmap slot duration and schedule length equal to the
+ * reported NAN capabilities (see &nan_slot_duration and
+ * &nan_schedule_length in &struct wpa_driver_capa).
+ */
+ struct nan_schedule_channel {
+ int freq;
+ int center_freq1;
+ int center_freq2;
+ int bandwidth;
+ struct wpabuf *time_bitmap;
+ u8 rx_nss;
+
+ u8 chan_entry[6];
+ } channels[MAX_NUM_NAN_SCHEDULE_CHANNELS];
+
+ struct wpabuf *avail_attr;
+};
+
+/**
+ * struct nan_peer_schedule_config - NAN peer schedule configuration
+ *
+ * @n_maps: Number of maps in the schedule
+ * @maps: Map specific schedule information
+ */
+struct nan_peer_schedule_config {
+ u8 n_maps;
+
+ /**
+ * maps - Map specific schedule information
+ *
+ * @map_id: Map ID
+ * @sched: NAN schedule configuration for the map
+ */
+ struct nan_schedule_map {
+ u8 map_id;
+ struct nan_schedule_config sched;
+ } maps[MAX_NUM_NAN_MAPS];
+};
+
+/**
* struct wpa_driver_ops - Driver interface API definition
*
* This structure defines the API that each driver interface needs to implement
@@ -3101,6 +3592,53 @@ struct wpa_driver_ops {
* broadcast keys, so key index 0 is available for this kind of
* configuration.
*
+ * For pairwise keys, there are potential race conditions between
+ * enabling a new TK on each end of the connection and sending the first
+ * protected frame. Drivers have multiple options on which style of key
+ * configuration to support with the simplest option not providing any
+ * protection for the race condition while the more complex options do
+ * provide partial or full protection.
+ *
+ * Option 1: Do not support extended key IDs (i.e., use only Key ID 0
+ * for pairwise keys) and do not support configuration of the next TK
+ * as an alternative RX key. This provides no protection, but is simple
+ * to support. The driver needs to ignore set_key() calls with
+ * KEY_FLAG_NEXT.
+ *
+ * Option 2: Do not support extended key IDs (i.e., use only Key ID 0
+ * for pairwise keys), but support configuration of the next TK as an
+ * alternative RX key for the initial 4-way handshake. This provides
+ * protection for the initial key setup at the beginning of an
+ * association. The driver needs to configure the initial TK for RX-only
+ * when receiving a set_key() call with KEY_FLAG_NEXT. This RX-only key
+ * is ready for receiving protected Data frames from the peer before the
+ * local device has enabled the key for TX. Unprotected EAPOL frames
+ * need to be allowed even when this next TK is configured as RX-only
+ * key. The same key is then set with KEY_FLAG_PAIRWISE_RX_TX to enable
+ * its use for both TX and RX. The driver ignores set_key() calls with
+ * KEY_FLAG_NEXT when a TK has been configured. When fully enabling the
+ * TK for TX and RX, the RX counters associated with the TK must not be
+ * cleared.
+ *
+ * Option 3: Same as option 2, but the driver supports multiple RX keys
+ * in parallel during PTK rekeying. The driver processed set_key() calls
+ * with KEY_FLAG_NEXT also when a TK has been configured. At that point
+ * in the rekeying sequence the driver uses the previously configured TK
+ * for TX and decrypts received frames with either the previously
+ * configured TK or the next TK (RX-only).
+ *
+ * Option 4: The driver supports extended Key IDs and they are used for
+ * an association but does not support KEY_FLAG_NEXT (options 2 and 3).
+ * The next TK is configured as RX-only with KEY_FLAG_PAIRWISE_RX and
+ * it is enabled for TX and RX with KEY_FLAG_PAIRWISE_RX_TX_MODIFY. When
+ * extended key ID is not used for an association, the driver behaves
+ * like in option 1.
+ *
+ * Option 5 and 6: Like option 4 but with support for KEY_FLAG_NEXT as
+ * described above for options 2 and 3, respectively. Option 4 is used
+ * for cases where extended key IDs are used for an association. Option
+ * 2 or 3 is used for cases where extended key IDs are not used.
+ *
* Please note that TKIP keys include separate TX and RX MIC keys and
* some drivers may expect them in different order than wpa_supplicant
* is using. If the TX/RX keys are swapped, all TKIP encrypted packets
@@ -3344,12 +3882,17 @@ struct wpa_driver_ops {
* @num_modes: Variable for returning the number of returned modes
* flags: Variable for returning hardware feature flags
* @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
+ * @alpha2: Buffer for returning the country code (at least 3 bytes),
+ * or %NULL if not needed. Set to empty string if not available.
+ * @alpha2_len: Length of the @alpha2 buffer
* Returns: Pointer to allocated hardware data on success or %NULL on
* failure. Caller is responsible for freeing this.
*/
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
- u16 *flags, u8 *dfs);
+ u16 *flags, u8 *dfs,
+ char *alpha2,
+ size_t alpha2_len);
/**
* send_mlme - Send management frame from MLME
@@ -3458,12 +4001,15 @@ struct wpa_driver_ops {
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
* @global_priv: private driver global data from global_init()
+ * @p2p_mode: P2P mode for a GO (not applicable for other interface
+ * types)
* Returns: Pointer to private data, %NULL on failure
*
* This function can be used instead of init() if the driver wrapper
* uses global data.
*/
- void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+ void * (*init2)(void *ctx, const char *ifname, void *global_priv,
+ enum wpa_p2p_mode p2p_mode);
/**
* get_interfaces - Get information about available interfaces
@@ -3704,13 +4250,14 @@ struct wpa_driver_ops {
* @own_addr: Source address and BSSID for the Disassociation frame
* @addr: MAC address of the station to disassociate
* @reason: Reason code for the Disassociation frame
+ * @link_id: Link ID to use for Disassociation frame, or -1 if not set
* Returns: 0 on success, -1 on failure
*
* This function requests a specific station to be disassociated and
* a Disassociation frame to be sent to it.
*/
int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason);
+ u16 reason, int link_id);
/**
* sta_remove - Remove a station entry (AP only)
@@ -3904,18 +4451,6 @@ struct wpa_driver_ops {
int vlan_id, int link_id);
/**
- * commit - Optional commit changes handler (AP only)
- * @priv: driver private data
- * Returns: 0 on success, -1 on failure
- *
- * This optional handler function can be registered if the driver
- * interface implementation needs to commit changes (e.g., by setting
- * network interface up) at the end of initial configuration. If set,
- * this handler will be called after initial setup has been completed.
- */
- int (*commit)(void *priv);
-
- /**
* set_radius_acl_auth - Notification of RADIUS ACL change
* @priv: Private driver interface data
* @mac: MAC address of the station
@@ -3999,7 +4534,10 @@ struct wpa_driver_ops {
* @bssid: BSSID (Address 3)
* @data: Frame body
* @data_len: data length in octets
- @ @no_cck: Whether CCK rates must not be used to transmit this frame
+ * @no_cck: Whether CCK rates must not be used to transmit this frame
+ * @link_id: Link ID of the specified link; -1 for non-MLO cases and for
+ * frames that target the MLD instead of a specific link in MLO
+ * cases
* Returns: 0 on success, -1 on failure
*
* This command can be used to request the driver to transmit an action
@@ -4020,7 +4558,8 @@ struct wpa_driver_ops {
*/
int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
const u8 *dst, const u8 *src, const u8 *bssid,
- const u8 *data, size_t data_len, int no_cck);
+ const u8 *data, size_t data_len, int no_cck,
+ int link_id);
/**
* send_action_cancel_wait - Cancel action frame TX wait
@@ -4037,6 +4576,8 @@ struct wpa_driver_ops {
* @priv: Private driver interface data
* @freq: Frequency (in MHz) of the channel
* @duration: Duration in milliseconds
+ * @filter_addr: an additional MAC address to accept frames to (NULL for
+ * using only the interface address)
* Returns: 0 on success, -1 on failure
*
* This command is used to request the driver to remain awake on the
@@ -4045,6 +4586,10 @@ struct wpa_driver_ops {
* Probe Request frames may also be requested to be reported by calling
* probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
*
+ * If filter_addr is provided, the driver will configure an additional
+ * MAC address for frame filtering, i.e., this new address will be used
+ * in addition to the interface address.
+ *
* The driver may not be at the requested channel when this function
* returns, i.e., the return code is only indicating whether the
* request was accepted. The caller will need to wait until the
@@ -4055,7 +4600,7 @@ struct wpa_driver_ops {
* executed.
*/
int (*remain_on_channel)(void *priv, unsigned int freq,
- unsigned int duration);
+ unsigned int duration, const u8 *filter_addr);
/**
* cancel_remain_on_channel - Cancel remain-on-channel operation
@@ -4320,26 +4865,21 @@ struct wpa_driver_ops {
struct wpa_mlo_signal_info *mlo_signal_info);
/**
- * channel_info - Get parameters of the current operating channel
+ * setup_link_reconfig - Used to initiate Link Reconfiguration request
+ * for the current MLO association.
* @priv: Private driver interface data
- * @channel_info: Channel info structure
- * Returns: 0 on success, negative (<0) on failure
+ * @info: Link reconfiguration request info
*/
- int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
+ int (*setup_link_reconfig)(void *priv,
+ struct wpa_mlo_reconfig_info *info);
/**
- * set_authmode - Set authentication algorithm(s) for static WEP
+ * channel_info - Get parameters of the current operating channel
* @priv: Private driver interface data
- * @authmode: 1=Open System, 2=Shared Key, 3=both
- * Returns: 0 on success, -1 on failure
- *
- * This function can be used to set authentication algorithms for AP
- * mode when static WEP is used. If the driver uses user space MLME/SME
- * implementation, there is no need to implement this function.
- *
- * DEPRECATED - use set_ap() instead
+ * @channel_info: Channel info structure
+ * Returns: 0 on success, negative (<0) on failure
*/
- int (*set_authmode)(void *priv, int authmode);
+ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
#ifdef ANDROID
/**
@@ -4426,20 +4966,6 @@ struct wpa_driver_ops {
struct wpa_driver_sta_auth_params *params);
/**
- * add_tspec - Add traffic stream
- * @priv: Private driver interface data
- * @addr: MAC address of the station to associate
- * @tspec_ie: tspec ie buffer
- * @tspec_ielen: tspec ie length
- * Returns: 0 on success, -1 on failure
- *
- * This function adds the traffic steam for the station
- * and fills the medium_time in tspec_ie.
- */
- int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
- size_t tspec_ielen);
-
- /**
* add_sta_node - Add a station node in the driver
* @priv: Private driver interface data
* @addr: MAC address of the station to add
@@ -5194,15 +5720,19 @@ struct wpa_driver_ops {
/**
* is_drv_shared - Check whether the driver interface is shared
* @priv: Private driver interface data from init()
- * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ * @link_id: Link ID to match
+ * Returns: true if it is being used or else false.
*
* Checks whether the driver interface is being used by other partner
* BSS(s) or not. This is used to decide whether the driver interface
* needs to be deinitilized when one interface is getting deinitialized.
*
- * Returns: true if it is being used or else false.
+ * NOTE: @link_id will be used only when there is only one BSS
+ * present and if that single link is active. In that case, the
+ * link ID is matched with the active link_id to decide whether the
+ * driver interface is being used by other partner BSS(s).
*/
- bool (*is_drv_shared)(void *priv, void *bss_ctx);
+ bool (*is_drv_shared)(void *priv, int link_id);
/**
* link_sta_remove - Remove a link STA from an MLD STA
@@ -5213,11 +5743,232 @@ struct wpa_driver_ops {
*/
int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr);
+ /**
+ * nan_flush - Flush all NAN offload services
+ * @priv: Private driver interface data
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_flush)(void *priv);
+
+ /**
+ * nan_publish - NAN offload for Publish()
+ * @priv: Private driver interface data
+ * @src: Source P2P device addr
+ * @publish_id: Publish instance to add
+ * @service_name: Service name
+ * @service_id: Service ID (6 octet value derived from service name)
+ * @srv_proto_type: Service protocol type
+ * @ssi: Service specific information or %NULL
+ * @elems: Information elements for Element Container attribute or %NULL
+ * @params: Configuration parameters
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_publish)(void *priv, const u8 *src, int publish_id,
+ const char *service_name, const u8 *service_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi, const struct wpabuf *elems,
+ struct nan_publish_params *params,
+ const u8 *network_id);
+
+ /**
+ * nan_cancel_publish - NAN offload for CancelPublish()
+ * @priv: Private driver interface data
+ * @publish_id: Publish instance to cancel
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_cancel_publish)(void *priv, int publish_id);
+
+ /**
+ * nan_update_publish - NAN offload for UpdatePublish()
+ * @priv: Private driver interface data
+ * @ssi: Service specific information or %NULL
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_update_publish)(void *priv, int publish_id,
+ const struct wpabuf *ssi);
+
+ /**
+ * nan_subscribe - NAN offload for Subscribe()
+ * @priv: Private driver interface data
+ * @src: Source P2P device addr
+ * @subscribe_id: Subscribe instance to add
+ * @service_name: Service name
+ * @service_id: Service ID (6 octet value derived from service name)
+ * @srv_proto_type: Service protocol type
+ * @ssi: Service specific information or %NULL
+ * @elems: Information elements for Element Container attribute or %NULL
+ * @params: Configuration parameters
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_subscribe)(void *priv, const u8 *src, int subscribe_id,
+ const char *service_name, const u8 *service_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ struct nan_subscribe_params *params,
+ const u8 *network_id);
+
+ /**
+ * nan_cancel_subscribe - NAN offload for CancelSubscribe()
+ * @priv: Private driver interface data
+ * @subscribe_id: Subscribe instance to cancel
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*nan_cancel_subscribe)(void *priv, int subscribe_id);
+
+ /**
+ * can_share_drv - Check whether driver interface can be shared
+ * @ctx: Pointer to hostapd context
+ * @params: Configuration for the driver wrapper
+ * @hapd: Pointer for overwriting the hapd context or %NULL
+ * if can't find a shared drv
+ *
+ * Checks whether the driver interface with same phy name is
+ * already present under the global driver which can be shared
+ * instead of creating a new driver interface instance. If present,
+ * @hapd will be overwritten with the hapd pointer which this shared
+ * drv's first BSS is using. This will help the caller to later call
+ * if_add().
+ *
+ * Returns: true if it can be shared or else false.
+ */
+ bool (*can_share_drv)(void *ctx, struct wpa_init_params *params,
+ void **hapd);
+
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
const u8 *match, size_t match_len,
bool multicast);
#endif /* CONFIG_TESTING_OPTIONS */
+
+ /**
+ * get_multi_hw_info - Get multiple underlying hardware information
+ * (hardware IDx and supported frequency range)
+ * @priv: Private driver interface data
+ * @num_multi_hws: Variable for returning the number of returned
+ * hardware info data
+ * Returns: Pointer to allocated multiple hardware data on success
+ * or %NULL on failure. Caller is responsible for freeing this.
+ */
+ struct hostapd_multi_hw_info *
+ (*get_multi_hw_info)(void *priv, unsigned int *num_multi_hws);
+
+#ifdef CONFIG_NAN
+ /**
+ * nan_start - Start NAN operation
+ * @priv: Private driver interface data
+ * @conf: NAN configuration parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command joins an existing NAN cluster or starts a new one.
+ */
+ int (*nan_start)(void *priv, const struct nan_cluster_config *conf);
+
+ /**
+ * nan_change_config - Update the NAN cluster configuration
+ * @priv: Private driver interface data
+ * @conf: NAN configuration parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command modifies the NAN cluster configuration.
+ */
+ int (*nan_change_config)(void *priv,
+ const struct nan_cluster_config *conf);
+
+ /**
+ * nan_stop - Stop NAN operation
+ * @priv: Private driver interface data
+ */
+ void (*nan_stop)(void *priv);
+
+ /**
+ * nan_config_schedule - Configure NAN schedule
+ * @priv: Private driver interface data
+ * @map_id: NAN schedule map ID
+ * @conf: NAN schedule configuration parameters
+ * Returns 0 on success, -1 on failure
+ *
+ * This command configures the local NAN schedule. It should be
+ * executed on NAN device interface after NAN has been started.
+ * The configured schedule should be valid for RX for all NAN
+ * activities (management and data).
+ *
+ * For devices that support multiple concurrent NAN radios, this
+ * callback should be called for each radio with the corresponding
+ * %map_id.
+ *
+ * If previous configuration exists, it is replaced with the new
+ * one. To delete previous schedule, set %conf.num_channels = 0.
+ */
+ int (*nan_config_schedule)(void *priv, u8 map_id,
+ struct nan_schedule_config *conf);
+
+ /**
+ * nan_config_peer_schedule - Configure NAN peer schedule
+ * @priv: Private driver interface data
+ * @peer: Peer's NAN device address
+ * @cdw: Peer's committed DW.
+ * @sequence_id: Peer's schedule sequence ID
+ * @max_chan_switch_time: Maximum channel switch time in TUs
+ * @ulw: Peer's unaligned window attributes or %NULL
+ * @sched: NAN peer schedule configuration parameters
+ * Returns 0 on success, -1 on failure
+ *
+ * This command configures peer's NAN schedule. To remove previous
+ * schedule for a given %map_id, set %sched.num_channels = 0.
+ * %ulw attributes are used to provide the initial information about
+ * peer's unaligned schedule. Further updates to ULW should be tracked
+ * internally by the device/driver.
+ */
+ int (*nan_config_peer_schedule)(void *priv, const u8 *peer,
+ u16 cdw, u8 sequence_id,
+ u16 max_chan_switch_time,
+ const struct wpabuf *ulw,
+ struct nan_peer_schedule_config *sched);
+#endif /* CONFIG_NAN */
+
+#ifdef CONFIG_PR
+ /**
+ * pd_start - Create interface for Proximity Detection
+ * @priv: Private driver interface data
+ * @addr: Requested MAC address
+ * @pd_addr: MAC address of the interface
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*pd_start)(void *priv, const u8 *addr, u8 *pd_addr);
+
+ /**
+ * pd_stop - Stop interface for Proximity Detection
+ * @priv: Private driver interface data
+ */
+ void (*pd_stop)(void *priv);
+
+ /**
+ * start_peer_measurement - Start peer measurement (FTM ranging)
+ * @priv: Private driver interface data
+ * @peer_addr: Peer MAC address
+ * @freq: Operating frequency in MHz
+ * @channel: Operating channel number
+ * @bw: Channel bandwidth in MHz
+ * @params: Ranging parameters (EDCA/NTB specific)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function triggers peer measurement (FTM ranging) after
+ * successful PASN authentication for proximity ranging.
+ */
+ int (*start_peer_measurement)(void *priv, const u8 *peer_addr,
+ int freq, u8 channel, int bw,
+ struct pr_pasn_ranging_params *params);
+
+ /**
+ * stop_peer_measurement - Stop peer measurement and destroy ranging socket
+ * @priv: Private driver interface data
+ *
+ * Unregisters the ranging socket from eloop and frees all associated
+ * resources. Safe to call when no ranging session is active (no-op).
+ */
+ void (*stop_peer_measurement)(void *priv);
+#endif /* CONFIG_PR */
};
/**
@@ -5845,6 +6596,87 @@ enum wpa_event_type {
* EVENT_LINK_RECONFIG - Notification that AP links removed
*/
EVENT_LINK_RECONFIG,
+
+ /**
+ * EVENT_MLD_INTERFACE_FREED - Notification of AP MLD interface removal
+ */
+ EVENT_MLD_INTERFACE_FREED,
+
+ /**
+ * EVENT_SETUP_LINK_RECONFIG - Notification that new AP links added
+ */
+ EVENT_SETUP_LINK_RECONFIG,
+
+ /**
+ * EVENT_NAN_CLUSTER_JOIN - Notification of a new cluster having been
+ * joined or started.
+ *
+ * This event is used to notify wpa_supplicant that a NAN cluster has
+ * been joined or started. The event data includes the NAN cluster ID
+ * and a boolean indicating whether a new cluster was started or an
+ * existing cluster was joined.
+ *
+ * Described in wpa_event_data.nan_cluster_join_info.
+ */
+ EVENT_NAN_CLUSTER_JOIN,
+
+ /**
+ * EVENT_NAN_NEXT_DW - Notification of NAN next Discovery Window
+ *
+ * This event is used to notify wpa_supplicant that the device/driver
+ * is ready for the next Discovery Window (DW) frames. It may be used
+ * to trigger transmission of multicast SDFs (active subscribe and
+ * unsolicited publish).
+ * The event data includes the DW frequency.
+ */
+ EVENT_NAN_NEXT_DW,
+
+ /**
+ * EVENT_INCUMBT_SIG_INTF_DETECTED - Notification of incumbent signal
+ * interference detection.
+ */
+ EVENT_INCUMBT_SIG_INTF_DETECTED,
+
+ /**
+ * EVENT_NAN_SCHED_UPDATE_DONE - NAN schedule update completed
+ *
+ * This event is used to notify wpa_supplicant that a NAN schedule
+ * update has been completed by the driver/firmware.
+ */
+ EVENT_NAN_SCHED_UPDATE_DONE,
+
+ /**
+ * EVENT_NAN_ULW_UPDATE - NAN ULW blob update
+ *
+ * This event is used to notify wpa_supplicant that the device ULW blob
+ * has changed and can be attached to outgoing NAN frames.
+ */
+ EVENT_NAN_ULW_UPDATE,
+
+ /**
+ * EVENT_NAN_CHAN_EVACUATION - Notification of NAN channel evacuation
+ *
+ * This event is used to notify wpa_supplicant that the device/driver
+ * evacuated one of the channels used for NAN operation, e.g., due to
+ * resource constraints (not enough resources to support NAN and other
+ * activities).
+ */
+ EVENT_NAN_CHAN_EVACUATION,
+
+ /**
+ * EVENT_PEER_MEASUREMENT_RESULT - Ranging measurement result
+ *
+ * This event is used to indicate a ranging measurement result.
+ */
+ EVENT_PEER_MEASUREMENT_RESULT,
+
+ /**
+ * EVENT_PEER_MEASUREMENT_COMPLETE - Ranging session fully done
+ *
+ * This event is used to notify that the entire peer
+ * measurement session identified by the cookie has ended.
+ */
+ EVENT_PEER_MEASUREMENT_COMPLETE,
};
@@ -6107,6 +6939,13 @@ union wpa_event_data {
* -1 if MLO is not used
*/
int assoc_link_id;
+
+ /**
+ * assoc_encrypted - Indicate that the (Re)Association
+ * Request/Response frames are transmitted encrypted over the
+ * air.
+ */
+ bool assoc_encrypted;
} assoc_info;
/**
@@ -6271,6 +7110,8 @@ union wpa_event_data {
u16 status_code;
const u8 *ies;
size_t ies_len;
+ const u8 *frame_body;
+ size_t frame_body_len;
} auth;
/**
@@ -6352,6 +7193,7 @@ union wpa_event_data {
const u8 *bssid;
const u8 *addr;
int wds;
+ int link_id;
} rx_from_unknown;
/**
@@ -6832,6 +7674,132 @@ union wpa_event_data {
u8 valid_links;
struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
} t2l_map_info;
+
+ /**
+ * struct reconfig_info - Data for EVENT_SETUP_LINK_RECONFIG
+ */
+ struct reconfig_info {
+ u16 added_links;
+ u8 count;
+ const u8 *status_list;
+ const u8 *resp_ie; /* Starting from Group Key Data */
+ size_t resp_ie_len;
+ } reconfig_info;
+
+ struct nan_cluster_join_info {
+ const u8 *bssid;
+ bool new_cluster;
+ } nan_cluster_join_info;
+
+ struct nan_next_dw_info {
+ int freq;
+ } nan_next_dw_info;
+
+ /**
+ * Data for EVENT_INCUMBT_SIG_INTF
+ */
+ struct incumbt_sig_intf_event {
+ int freq;
+ enum chan_width chan_width;
+ int cf1;
+ int cf2;
+ u32 chan_bw_interference_bitmap;
+ int link_id;
+ } incumbt_sig_intf_event;
+
+ /**
+ * struct nan_sched_update_done_info - Data for EVENT_NAN_SCHED_UPDATE_DONE
+ * @success: Indicates whether the NAN schedule update was successful
+ */
+ struct nan_sched_update_done_info {
+ bool success;
+ } nan_sched_update_done_info;
+
+ /**
+ * struct nan_ulw_update_info - Data for EVENT_NAN_ULW_UPDATE
+ * @ulw: Pointer to ULW blob data, containing one or more Unaligned
+ * Schedule attributes as defined in the Wi-Fi Aware spec v4.0,
+ * Table 109 (Unaligned Schedule attribute format), including
+ * attribute header for each attribute.
+ * @ulw_len: ULW blob length in bytes
+ */
+ struct nan_ulw_update_info {
+ const u8 *ulw;
+ size_t ulw_len;
+ } nan_ulw_update_info;
+
+ /**
+ * struct nan_chan_evacuation_info - Data for EVENT_NAN_CHAN_EVACUATION
+ * @freq: Control frequency of the evacuated channel in MHz
+ */
+ struct nan_chan_evacuation_info {
+ int freq;
+ } nan_chan_evacuation_info;
+
+ /**
+ * struct peer_measurement_result - Ranging measurement result for
+ * EVENT_PEER_MEASUREMENT_RESULT
+ */
+ struct peer_measurement_result {
+ u8 addr[ETH_ALEN];
+ u32 status;
+ u64 host_time;
+ u64 ap_tsf;
+ bool final;
+ u64 cookie;
+
+ /**
+ * struct peer_measurement_ftm_result -
+ * FTM-specific measurement results
+ */
+ struct peer_measurement_ftm_result {
+ bool fail;
+ u32 fail_reason;
+ u32 burst_index;
+ u32 num_ftmr_attempts;
+ u32 num_ftmr_successes;
+ u32 busy_retry_time;
+ u32 num_bursts_exp;
+ u32 burst_duration;
+ u32 ftms_per_burst;
+ s32 rssi_avg;
+ s32 rssi_spread;
+ s64 rtt_avg;
+ u64 rtt_variance;
+ u64 rtt_spread;
+ s64 dist_avg;
+ u64 dist_variance;
+ u64 dist_spread;
+ const u8 *lci;
+ size_t lci_len;
+ const u8 *civicloc;
+ size_t civicloc_len;
+ /* Additional FTM parameters */
+ u32 burst_period;
+ u32 tx_ltf_repetition_count;
+ u32 rx_ltf_repetition_count;
+ u32 max_time_between_measurements;
+ u32 min_time_between_measurements;
+ u32 num_tx_spatial_streams;
+ u32 num_rx_spatial_streams;
+ u32 nominal_time;
+ u8 availability_window;
+ u32 band_width;
+ u32 preamble;
+ bool is_delayed_lmr;
+ /* Flag indicating if FTM data is present */
+ bool has_data;
+ } ftm;
+
+ } peer_measurement_result;
+
+ /**
+ * struct peer_measurement_complete - Ranging session complete for
+ * EVENT_PEER_MEASUREMENT_COMPLETE
+ */
+ struct peer_measurement_complete {
+ u64 cookie;
+ } peer_measurement_complete;
};
/**
@@ -6934,6 +7902,8 @@ int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
bool he_supported(const struct hostapd_hw_modes *hw_mode,
enum ieee80211_op_mode op_mode);
+bool eht_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode);
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
@@ -6955,9 +7925,6 @@ extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
/* driver_nl80211.c */
extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
#endif /* CONFIG_DRIVER_NL80211 */
-#ifdef CONFIG_DRIVER_HOSTAP
-extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
-#endif /* CONFIG_DRIVER_HOSTAP */
#ifdef CONFIG_DRIVER_BSD
extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
#endif /* CONFIG_DRIVER_BSD */
@@ -6983,10 +7950,6 @@ extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
/* driver_roboswitch.c */
extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
#endif /* CONFIG_DRIVER_ROBOSWITCH */
-#ifdef CONFIG_DRIVER_ATHEROS
-/* driver_atheros.c */
-extern const struct wpa_driver_ops wpa_driver_atheros_ops;
-#endif /* CONFIG_DRIVER_ATHEROS */
#ifdef CONFIG_DRIVER_NONE
extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
#endif /* CONFIG_DRIVER_NONE */
diff --git a/contrib/wpa/src/drivers/driver_atheros.c b/contrib/wpa/src/drivers/driver_atheros.c
deleted file mode 100644
index 71863306afad..000000000000
--- a/contrib/wpa/src/drivers/driver_atheros.c
+++ /dev/null
@@ -1,2286 +0,0 @@
-/*
- * hostapd / Driver interaction with Atheros driver
- * Copyright (c) 2004, Sam Leffler <sam@errno.com>
- * Copyright (c) 2004, Video54 Technologies
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2009, Atheros Communications
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <net/if.h>
-#include <sys/ioctl.h>
-
-#include "common.h"
-#include "eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "l2_packet/l2_packet.h"
-
-#include "common.h"
-#ifndef _BYTE_ORDER
-#ifdef WORDS_BIGENDIAN
-#define _BYTE_ORDER _BIG_ENDIAN
-#else
-#define _BYTE_ORDER _LITTLE_ENDIAN
-#endif
-#endif /* _BYTE_ORDER */
-
-/*
- * Note, the ATH_WPS_IE setting must match with the driver build.. If the
- * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail.
- */
-#define ATH_WPS_IE
-
-#include "ieee80211_external.h"
-
-/* Avoid conflicting definition from the driver header files with
- * common/wpa_common.h */
-#undef WPA_OUI_TYPE
-
-
-#ifdef CONFIG_WPS
-#include <netpacket/packet.h>
-#endif /* CONFIG_WPS */
-
-#ifndef ETH_P_80211_RAW
-#define ETH_P_80211_RAW 0x0019
-#endif
-
-#include "linux_wext.h"
-
-#include "driver.h"
-#include "eloop.h"
-#include "priv_netlink.h"
-#include "l2_packet/l2_packet.h"
-#include "common/ieee802_11_defs.h"
-#include "netlink.h"
-#include "linux_ioctl.h"
-
-
-struct atheros_driver_data {
- struct hostapd_data *hapd; /* back pointer */
-
- char iface[IFNAMSIZ + 1];
- int ifindex;
- struct l2_packet_data *sock_xmit; /* raw packet xmit socket */
- struct l2_packet_data *sock_recv; /* raw packet recv socket */
- int ioctl_sock; /* socket for ioctl() use */
- struct netlink_data *netlink;
- int we_version;
- int fils_en; /* FILS enable/disable in driver */
- u8 acct_mac[ETH_ALEN];
- struct hostap_sta_driver_data acct_data;
-
- struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
- struct wpabuf *wpa_ie;
- struct wpabuf *wps_beacon_ie;
- struct wpabuf *wps_probe_resp_ie;
- u8 own_addr[ETH_ALEN];
-};
-
-static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code, int link_id);
-static int atheros_set_privacy(void *priv, int enabled);
-
-static const char * athr_get_ioctl_name(int op)
-{
- switch (op) {
- case IEEE80211_IOCTL_SETPARAM:
- return "SETPARAM";
- case IEEE80211_IOCTL_GETPARAM:
- return "GETPARAM";
- case IEEE80211_IOCTL_SETKEY:
- return "SETKEY";
- case IEEE80211_IOCTL_SETWMMPARAMS:
- return "SETWMMPARAMS";
- case IEEE80211_IOCTL_DELKEY:
- return "DELKEY";
- case IEEE80211_IOCTL_GETWMMPARAMS:
- return "GETWMMPARAMS";
- case IEEE80211_IOCTL_SETMLME:
- return "SETMLME";
- case IEEE80211_IOCTL_GETCHANINFO:
- return "GETCHANINFO";
- case IEEE80211_IOCTL_SETOPTIE:
- return "SETOPTIE";
- case IEEE80211_IOCTL_GETOPTIE:
- return "GETOPTIE";
- case IEEE80211_IOCTL_ADDMAC:
- return "ADDMAC";
- case IEEE80211_IOCTL_DELMAC:
- return "DELMAC";
- case IEEE80211_IOCTL_GETCHANLIST:
- return "GETCHANLIST";
- case IEEE80211_IOCTL_SETCHANLIST:
- return "SETCHANLIST";
- case IEEE80211_IOCTL_KICKMAC:
- return "KICKMAC";
- case IEEE80211_IOCTL_CHANSWITCH:
- return "CHANSWITCH";
- case IEEE80211_IOCTL_GETMODE:
- return "GETMODE";
- case IEEE80211_IOCTL_SETMODE:
- return "SETMODE";
- case IEEE80211_IOCTL_GET_APPIEBUF:
- return "GET_APPIEBUF";
- case IEEE80211_IOCTL_SET_APPIEBUF:
- return "SET_APPIEBUF";
- case IEEE80211_IOCTL_SET_ACPARAMS:
- return "SET_ACPARAMS";
- case IEEE80211_IOCTL_FILTERFRAME:
- return "FILTERFRAME";
- case IEEE80211_IOCTL_SET_RTPARAMS:
- return "SET_RTPARAMS";
- case IEEE80211_IOCTL_SET_MEDENYENTRY:
- return "SET_MEDENYENTRY";
- case IEEE80211_IOCTL_GET_MACADDR:
- return "GET_MACADDR";
- case IEEE80211_IOCTL_SET_HBRPARAMS:
- return "SET_HBRPARAMS";
- case IEEE80211_IOCTL_SET_RXTIMEOUT:
- return "SET_RXTIMEOUT";
- case IEEE80211_IOCTL_STA_STATS:
- return "STA_STATS";
- case IEEE80211_IOCTL_GETWPAIE:
- return "GETWPAIE";
- default:
- return "??";
- }
-}
-
-
-static const char * athr_get_param_name(int op)
-{
- switch (op) {
- case IEEE80211_IOC_MCASTCIPHER:
- return "MCASTCIPHER";
- case IEEE80211_PARAM_MCASTKEYLEN:
- return "MCASTKEYLEN";
- case IEEE80211_PARAM_UCASTCIPHERS:
- return "UCASTCIPHERS";
- case IEEE80211_PARAM_KEYMGTALGS:
- return "KEYMGTALGS";
- case IEEE80211_PARAM_RSNCAPS:
- return "RSNCAPS";
- case IEEE80211_PARAM_WPA:
- return "WPA";
- case IEEE80211_PARAM_AUTHMODE:
- return "AUTHMODE";
- case IEEE80211_PARAM_PRIVACY:
- return "PRIVACY";
- case IEEE80211_PARAM_COUNTERMEASURES:
- return "COUNTERMEASURES";
- default:
- return "??";
- }
-}
-
-
-#ifdef CONFIG_FILS
-static int
-get80211param(struct atheros_driver_data *drv, int op, int *data)
-{
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.mode = op;
-
- if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
- return -1;
-
- *data = iwr.u.mode;
- return 0;
-}
-#endif /* CONFIG_FILS */
-
-
-static int
-set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
-{
- struct iwreq iwr;
- int do_inline = len < IFNAMSIZ;
-
- /* Certain ioctls must use the non-inlined method */
- if (op == IEEE80211_IOCTL_SET_APPIEBUF ||
- op == IEEE80211_IOCTL_FILTERFRAME)
- do_inline = 0;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- if (do_inline) {
- /*
- * Argument data fits inline; put it there.
- */
- os_memcpy(iwr.u.name, data, len);
- } else {
- /*
- * Argument data too big for inline transfer; setup a
- * parameter block instead; the kernel will transfer
- * the data for the driver.
- */
- iwr.u.data.pointer = data;
- iwr.u.data.length = len;
- }
-
- if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
- wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x "
- "(%s) len=%d failed: %d (%s)",
- __func__, drv->iface, op,
- athr_get_ioctl_name(op),
- len, errno, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-set80211param(struct atheros_driver_data *drv, int op, int arg)
-{
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.mode = op;
- os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg));
-
- if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
- wpa_printf(MSG_INFO,
- "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
- __func__, drv->iface, op, athr_get_param_name(op),
- arg, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-#ifndef CONFIG_NO_STDOUT_DEBUG
-static const char *
-ether_sprintf(const u8 *addr)
-{
- static char buf[sizeof(MACSTR)];
-
- if (addr != NULL)
- os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
- else
- os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0);
- return buf;
-}
-#endif /* CONFIG_NO_STDOUT_DEBUG */
-
-/*
- * Configure WPA parameters.
- */
-static int
-atheros_configure_wpa(struct atheros_driver_data *drv,
- struct wpa_bss_params *params)
-{
- int v;
-
- switch (params->wpa_group) {
- case WPA_CIPHER_CCMP:
- v = IEEE80211_CIPHER_AES_CCM;
- break;
-#ifdef ATH_GCM_SUPPORT
- case WPA_CIPHER_CCMP_256:
- v = IEEE80211_CIPHER_AES_CCM_256;
- break;
- case WPA_CIPHER_GCMP:
- v = IEEE80211_CIPHER_AES_GCM;
- break;
- case WPA_CIPHER_GCMP_256:
- v = IEEE80211_CIPHER_AES_GCM_256;
- break;
-#endif /* ATH_GCM_SUPPORT */
- case WPA_CIPHER_TKIP:
- v = IEEE80211_CIPHER_TKIP;
- break;
- case WPA_CIPHER_WEP104:
- v = IEEE80211_CIPHER_WEP;
- break;
- case WPA_CIPHER_WEP40:
- v = IEEE80211_CIPHER_WEP;
- break;
- case WPA_CIPHER_NONE:
- v = IEEE80211_CIPHER_NONE;
- break;
- default:
- wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
- params->wpa_group);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
- if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
- wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v);
- return -1;
- }
- if (v == IEEE80211_CIPHER_WEP) {
- /* key length is done only for specific ciphers */
- v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
- if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
- wpa_printf(MSG_INFO,
- "Unable to set group key length to %u", v);
- return -1;
- }
- }
-
- v = 0;
- if (params->wpa_pairwise & WPA_CIPHER_CCMP)
- v |= 1<<IEEE80211_CIPHER_AES_CCM;
-#ifdef ATH_GCM_SUPPORT
- if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
- v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
- if (params->wpa_pairwise & WPA_CIPHER_GCMP)
- v |= 1<<IEEE80211_CIPHER_AES_GCM;
- if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
- v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
-#endif /* ATH_GCM_SUPPORT */
- if (params->wpa_pairwise & WPA_CIPHER_TKIP)
- v |= 1<<IEEE80211_CIPHER_TKIP;
- if (params->wpa_pairwise & WPA_CIPHER_NONE)
- v |= 1<<IEEE80211_CIPHER_NONE;
- wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
- if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
- wpa_printf(MSG_INFO,
- "Unable to set pairwise key ciphers to 0x%x", v);
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
- __func__, params->wpa_key_mgmt);
- if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
- params->wpa_key_mgmt)) {
- wpa_printf(MSG_INFO,
- "Unable to set key management algorithms to 0x%x",
- params->wpa_key_mgmt);
- return -1;
- }
-
- v = 0;
- if (params->rsn_preauth)
- v |= BIT(0);
- if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- v |= BIT(7);
- if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
- v |= BIT(6);
- }
-
- wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
- if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
- wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
- v);
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
- if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
- wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
- return -1;
- }
- return 0;
-}
-
-static int
-atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
-{
- struct atheros_driver_data *drv = priv;
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
-
- if (!params->enabled) {
- /* XXX restore state */
- if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
- IEEE80211_AUTH_AUTO) < 0)
- return -1;
- /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
- return atheros_set_privacy(drv, 0);
- }
- if (!params->wpa && !params->ieee802_1x) {
- wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
- return -1;
- }
- if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
- wpa_printf(MSG_WARNING, "Error configuring WPA state!");
- return -1;
- }
- if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
- (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
- wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
- return -1;
- }
-
- return 0;
-}
-
-static int
-atheros_set_privacy(void *priv, int enabled)
-{
- struct atheros_driver_data *drv = priv;
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
-
- return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
-}
-
-static int
-atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
- __func__, ether_sprintf(addr), authorized);
-
- if (authorized)
- mlme.im_op = IEEE80211_MLME_AUTHORIZE;
- else
- mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
- mlme.im_reason = 0;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
- __func__, authorized ? "" : "un", MAC2STR(addr));
- }
-
- return ret;
-}
-
-static int
-atheros_sta_set_flags(void *priv, const u8 *addr,
- unsigned int total_flags, unsigned int flags_or,
- unsigned int flags_and)
-{
- /* For now, only support setting Authorized flag */
- if (flags_or & WPA_STA_AUTHORIZED)
- return atheros_set_sta_authorized(priv, addr, 1);
- if (!(flags_and & WPA_STA_AUTHORIZED))
- return atheros_set_sta_authorized(priv, addr, 0);
- return 0;
-}
-
-static int
-atheros_del_key(void *priv, const u8 *addr, int key_idx)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_del_key wk;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
- __func__, ether_sprintf(addr), key_idx);
-
- os_memset(&wk, 0, sizeof(wk));
- if (addr != NULL) {
- os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
- } else {
- wk.idk_keyix = key_idx;
- }
-
- ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
- " key_idx %d)", __func__, ether_sprintf(addr),
- key_idx);
- }
-
- return ret;
-}
-
-static int
-atheros_set_key(void *priv, struct wpa_driver_set_key_params *params)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_key wk;
- u_int8_t cipher;
- int ret;
- enum wpa_alg alg = params->alg;
- const u8 *addr = params->addr;
- int key_idx = params->key_idx;
- int set_tx = params->set_tx;
- const u8 *key = params->key;
- size_t key_len = params->key_len;
-
- if (alg == WPA_ALG_NONE)
- return atheros_del_key(drv, addr, key_idx);
-
- wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
- __func__, alg, ether_sprintf(addr), key_idx);
-
- switch (alg) {
- case WPA_ALG_WEP:
- cipher = IEEE80211_CIPHER_WEP;
- break;
- case WPA_ALG_TKIP:
- cipher = IEEE80211_CIPHER_TKIP;
- break;
- case WPA_ALG_CCMP:
- cipher = IEEE80211_CIPHER_AES_CCM;
- break;
-#ifdef ATH_GCM_SUPPORT
- case WPA_ALG_CCMP_256:
- cipher = IEEE80211_CIPHER_AES_CCM_256;
- break;
- case WPA_ALG_GCMP:
- cipher = IEEE80211_CIPHER_AES_GCM;
- break;
- case WPA_ALG_GCMP_256:
- cipher = IEEE80211_CIPHER_AES_GCM_256;
- break;
-#endif /* ATH_GCM_SUPPORT */
- case WPA_ALG_BIP_CMAC_128:
- cipher = IEEE80211_CIPHER_AES_CMAC;
- break;
-#ifdef ATH_GCM_SUPPORT
- case WPA_ALG_BIP_CMAC_256:
- cipher = IEEE80211_CIPHER_AES_CMAC_256;
- break;
- case WPA_ALG_BIP_GMAC_128:
- cipher = IEEE80211_CIPHER_AES_GMAC;
- break;
- case WPA_ALG_BIP_GMAC_256:
- cipher = IEEE80211_CIPHER_AES_GMAC_256;
- break;
-#endif /* ATH_GCM_SUPPORT */
- default:
- wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
- __func__, alg);
- return -1;
- }
-
- if (key_len > sizeof(wk.ik_keydata)) {
- wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
- (unsigned long) key_len);
- return -3;
- }
-
- os_memset(&wk, 0, sizeof(wk));
- wk.ik_type = cipher;
- wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
- if (addr == NULL || is_broadcast_ether_addr(addr)) {
- os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
- wk.ik_keyix = key_idx;
- if (set_tx)
- wk.ik_flags |= IEEE80211_KEY_DEFAULT;
- } else {
- os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.ik_keyix = IEEE80211_KEYIX_NONE;
- }
- wk.ik_keylen = key_len;
- os_memcpy(wk.ik_keydata, key, key_len);
-
- ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
- " key_idx %d alg %d key_len %lu set_tx %d)",
- __func__, ether_sprintf(wk.ik_macaddr), key_idx,
- alg, (unsigned long) key_len, set_tx);
- }
-
- return ret;
-}
-
-
-static int
-atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- int link_id, u8 *seq)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_key wk;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
- __func__, ether_sprintf(addr), idx);
-
- os_memset(&wk, 0, sizeof(wk));
- if (addr == NULL)
- os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
- else
- os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.ik_keyix = idx;
-
- if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
- wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
- "(addr " MACSTR " key_idx %d)",
- __func__, MAC2STR(wk.ik_macaddr), idx);
- return -1;
- }
-
-#ifdef WORDS_BIGENDIAN
- {
- /*
- * wk.ik_keytsc is in host byte order (big endian), need to
- * swap it to match with the byte order used in WPA.
- */
- int i;
-#ifndef WPA_KEY_RSC_LEN
-#define WPA_KEY_RSC_LEN 8
-#endif
- u8 tmp[WPA_KEY_RSC_LEN];
- os_memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
- for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
- seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
- }
- }
-#else /* WORDS_BIGENDIAN */
- os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
-#endif /* WORDS_BIGENDIAN */
- return 0;
-}
-
-
-static int
-atheros_flush(void *priv, int link_id)
-{
- u8 allsta[IEEE80211_ADDR_LEN];
- os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
- return atheros_sta_deauth(priv, NULL, allsta,
- IEEE80211_REASON_AUTH_LEAVE, -1);
-}
-
-
-static int
-atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
- const u8 *addr)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_sta_stats stats;
-
- os_memset(data, 0, sizeof(*data));
-
- /*
- * Fetch statistics for station from the system.
- */
- os_memset(&stats, 0, sizeof(stats));
- os_memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
- if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS,
- &stats, sizeof(stats))) {
- wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
- MACSTR ")", __func__, MAC2STR(addr));
- if (ether_addr_equal(addr, drv->acct_mac)) {
- os_memcpy(data, &drv->acct_data, sizeof(*data));
- return 0;
- }
-
- wpa_printf(MSG_INFO,
- "Failed to get station stats information element");
- return -1;
- }
-
- data->rx_packets = stats.is_stats.ns_rx_data;
- data->rx_bytes = stats.is_stats.ns_rx_bytes;
- data->tx_packets = stats.is_stats.ns_tx_data;
- data->tx_bytes = stats.is_stats.ns_tx_bytes;
- return 0;
-}
-
-
-static int
-atheros_sta_clear_stats(void *priv, const u8 *addr)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
-
- mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
- sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
- MACSTR ")", __func__, MAC2STR(addr));
- }
-
- return ret;
-}
-
-
-static int
-atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
-{
- struct atheros_driver_data *drv = priv;
- u8 buf[512];
- struct ieee80211req_getset_appiebuf *app_ie;
-
- wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
- (unsigned long) ie_len);
- wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
-
- wpabuf_free(drv->wpa_ie);
- if (ie)
- drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
- else
- drv->wpa_ie = NULL;
-
- app_ie = (struct ieee80211req_getset_appiebuf *) buf;
- if (ie)
- os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
- app_ie->app_buflen = ie_len;
-
- app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
-
- /* append WPS IE for Beacon */
- if (drv->wps_beacon_ie != NULL) {
- os_memcpy(&(app_ie->app_buf[ie_len]),
- wpabuf_head(drv->wps_beacon_ie),
- wpabuf_len(drv->wps_beacon_ie));
- app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
- }
- wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
- app_ie->app_buf, app_ie->app_buflen);
- set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
- sizeof(struct ieee80211req_getset_appiebuf) +
- app_ie->app_buflen);
-
- /* append WPS IE for Probe Response */
- app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
- if (drv->wps_probe_resp_ie != NULL) {
- os_memcpy(&(app_ie->app_buf[ie_len]),
- wpabuf_head(drv->wps_probe_resp_ie),
- wpabuf_len(drv->wps_probe_resp_ie));
- app_ie->app_buflen = ie_len +
- wpabuf_len(drv->wps_probe_resp_ie);
- } else
- app_ie->app_buflen = ie_len;
- wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
- app_ie->app_buf, app_ie->app_buflen);
- set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
- sizeof(struct ieee80211req_getset_appiebuf) +
- app_ie->app_buflen);
- return 0;
-}
-
-static int
-atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code, int link_id)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
- __func__, ether_sprintf(addr), reason_code);
-
- mlme.im_op = IEEE80211_MLME_DEAUTH;
- mlme.im_reason = reason_code;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
- " reason %d)",
- __func__, MAC2STR(addr), reason_code);
- }
-
- return ret;
-}
-
-static int
-atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
- __func__, ether_sprintf(addr), reason_code);
-
- mlme.im_op = IEEE80211_MLME_DISASSOC;
- mlme.im_reason = reason_code;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
- MACSTR " reason %d)",
- __func__, MAC2STR(addr), reason_code);
- }
-
- return ret;
-}
-
-static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
- u8 qos_map_set_len)
-{
-#ifdef CONFIG_ATHEROS_QOS_MAP
- struct atheros_driver_data *drv = ctx;
- struct ieee80211req_athdbg req;
- struct ieee80211_qos_map *qos_map = &req.data.qos_map;
- struct iwreq iwr;
- int i, up_start;
-
- if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
- qos_map_set_len & 1) {
- wpa_printf(MSG_ERROR, "Invalid QoS Map");
- return -1;
- } else {
- os_memset(&req, 0, sizeof(struct ieee80211req_athdbg));
- req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
- iwr.u.data.pointer = (void *) &req;
- iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
- }
-
- qos_map->valid = 1;
- qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
- if (qos_map->num_dscp_except) {
- for (i = 0; i < qos_map->num_dscp_except; i++) {
- qos_map->dscp_exception[i].dscp = qos_map_set[i * 2];
- qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1];
- }
- }
-
- up_start = qos_map_set_len - 16;
- for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
- qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
- qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
- }
-
- if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
- wpa_printf(MSG_ERROR,
- "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
- __func__, drv->iface, strerror(errno));
- return -1;
- }
-#endif /* CONFIG_ATHEROS_QOS_MAP */
-
- return 0;
-}
-
-
-static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
- size_t len)
-{
- struct atheros_driver_data *drv = ctx;
- const struct ieee80211_mgmt *mgmt;
- union wpa_event_data event;
- u16 fc, stype;
- int ielen;
- const u8 *iebuf;
-
- if (len < IEEE80211_HDRLEN)
- return;
-
- mgmt = (const struct ieee80211_mgmt *) buf;
-
- fc = le_to_host16(mgmt->frame_control);
-
- if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
- return;
-
- stype = WLAN_FC_GET_STYPE(fc);
-
- wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
- (int) len);
-
- if (stype == WLAN_FC_STYPE_PROBE_REQ) {
- if (len < IEEE80211_HDRLEN)
- return;
-
- os_memset(&event, 0, sizeof(event));
- event.rx_probe_req.sa = mgmt->sa;
- event.rx_probe_req.da = mgmt->da;
- event.rx_probe_req.bssid = mgmt->bssid;
- event.rx_probe_req.ie = buf + IEEE80211_HDRLEN;
- event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN;
- wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
- return;
- }
-
- if (stype == WLAN_FC_STYPE_ACTION &&
- (ether_addr_equal(drv->own_addr, mgmt->bssid) ||
- is_broadcast_ether_addr(mgmt->bssid))) {
- os_memset(&event, 0, sizeof(event));
- event.rx_mgmt.frame = buf;
- event.rx_mgmt.frame_len = len;
- wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
- return;
- }
-
- if (!ether_addr_equal(drv->own_addr, mgmt->bssid)) {
- wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
- __func__);
- return;
- }
-
- switch (stype) {
- case WLAN_FC_STYPE_ASSOC_REQ:
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
- break;
- ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
- iebuf = mgmt->u.assoc_req.variable;
- drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
- NULL, -1, 0);
- break;
- case WLAN_FC_STYPE_REASSOC_REQ:
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
- break;
- ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
- iebuf = mgmt->u.reassoc_req.variable;
- drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0,
- NULL, -1, 1);
- break;
- case WLAN_FC_STYPE_AUTH:
- if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
- break;
- os_memset(&event, 0, sizeof(event));
- if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) {
- event.rx_mgmt.frame = buf;
- event.rx_mgmt.frame_len = len;
- wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
- break;
- }
- os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
- os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
- event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
- event.auth.status_code =
- le_to_host16(mgmt->u.auth.status_code);
- event.auth.auth_transaction =
- le_to_host16(mgmt->u.auth.auth_transaction);
- event.auth.ies = mgmt->u.auth.variable;
- event.auth.ies_len = len - IEEE80211_HDRLEN -
- sizeof(mgmt->u.auth);
- wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
- break;
- default:
- break;
- }
-}
-
-
-static int atheros_receive_pkt(struct atheros_driver_data *drv)
-{
- int ret = 0;
- struct ieee80211req_set_filter filt;
-
- wpa_printf(MSG_DEBUG, "%s Enter", __func__);
- filt.app_filterype = 0;
-#ifdef CONFIG_WPS
- filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
-#endif /* CONFIG_WPS */
- filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
- IEEE80211_FILTER_TYPE_AUTH |
- IEEE80211_FILTER_TYPE_ACTION);
-#ifdef CONFIG_WNM
- filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_HS20
- filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
-#endif /* CONFIG_HS20 */
- if (filt.app_filterype) {
- ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
- sizeof(struct ieee80211req_set_filter));
- if (ret)
- return ret;
- }
-
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
- drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
- atheros_raw_receive, drv, 1);
- if (drv->sock_raw == NULL)
- return -1;
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */
- return ret;
-}
-
-static int atheros_reset_appfilter(struct atheros_driver_data *drv)
-{
- struct ieee80211req_set_filter filt;
- filt.app_filterype = 0;
- return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
- sizeof(struct ieee80211req_set_filter));
-}
-
-#ifdef CONFIG_WPS
-static int
-atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
-{
- struct atheros_driver_data *drv = priv;
- u8 buf[512];
- struct ieee80211req_getset_appiebuf *beac_ie;
-
- wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__,
- (unsigned long) len, frametype);
- wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
-
- beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
- beac_ie->app_frmtype = frametype;
- beac_ie->app_buflen = len;
- if (ie)
- os_memcpy(&(beac_ie->app_buf[0]), ie, len);
-
- /* append the WPA/RSN IE if it is set already */
- if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
- (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
- (drv->wpa_ie != NULL)) {
- wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
- drv->wpa_ie);
- os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
- wpabuf_len(drv->wpa_ie));
- beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
- }
-
- wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
- beac_ie->app_buf, beac_ie->app_buflen);
- return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
- sizeof(struct ieee80211req_getset_appiebuf) +
- beac_ie->app_buflen);
-}
-
-static int
-atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp,
- const struct wpabuf *assocresp)
-{
- struct atheros_driver_data *drv = priv;
-
- wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
- wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
- proberesp);
- wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
- assocresp);
- wpabuf_free(drv->wps_beacon_ie);
- drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
- wpabuf_free(drv->wps_probe_resp_ie);
- drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
-
- atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
- assocresp ? wpabuf_len(assocresp) : 0,
- IEEE80211_APPIE_FRAME_ASSOC_RESP);
- if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
- beacon ? wpabuf_len(beacon) : 0,
- IEEE80211_APPIE_FRAME_BEACON))
- return -1;
- return atheros_set_wps_ie(priv,
- proberesp ? wpabuf_head(proberesp) : NULL,
- proberesp ? wpabuf_len(proberesp): 0,
- IEEE80211_APPIE_FRAME_PROBE_RESP);
-}
-#else /* CONFIG_WPS */
-#define atheros_set_ap_wps_ie NULL
-#endif /* CONFIG_WPS */
-
-static int
-atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
- __func__, ether_sprintf(params->addr), params->status);
-
-#ifdef CONFIG_FILS
- /* Copy FILS AAD parameters if the driver supports FILS */
- if (params->fils_auth && drv->fils_en) {
- wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
- __func__);
- os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
- IEEE80211_FILS_NONCE_LEN);
- os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
- IEEE80211_FILS_NONCE_LEN);
- os_memcpy(mlme.fils_aad.kek, params->fils_kek,
- IEEE80211_MAX_WPA_KEK_LEN);
- mlme.fils_aad.kek_len = params->fils_kek_len;
- mlme.im_op = IEEE80211_MLME_AUTH_FILS;
- wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
- mlme.fils_aad.ANonce, FILS_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
- mlme.fils_aad.SNonce, FILS_NONCE_LEN);
- wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
- mlme.fils_aad.kek, mlme.fils_aad.kek_len);
- } else {
- mlme.im_op = IEEE80211_MLME_AUTH;
- }
-#else /* CONFIG_FILS */
- mlme.im_op = IEEE80211_MLME_AUTH;
-#endif /* CONFIG_FILS */
-
- mlme.im_reason = params->status;
- mlme.im_seq = params->seq;
- os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
- mlme.im_optie_len = params->len;
- if (params->len) {
- if (params->len < IEEE80211_MAX_OPT_IE) {
- os_memcpy(mlme.im_optie, params->ie, params->len);
- } else {
- wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
- "opt_ie STA (addr " MACSTR " reason %d, "
- "ie_len %d)",
- __func__, MAC2STR(params->addr),
- params->status, (int) params->len);
- return -1;
- }
- }
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
- " reason %d)",
- __func__, MAC2STR(params->addr), params->status);
- }
- return ret;
-}
-
-static int
-atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
- int reassoc, u16 status_code, const u8 *ie, size_t len)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int ret;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
- __func__, ether_sprintf(addr), status_code, reassoc);
-
- if (reassoc)
- mlme.im_op = IEEE80211_MLME_REASSOC;
- else
- mlme.im_op = IEEE80211_MLME_ASSOC;
- mlme.im_reason = status_code;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- mlme.im_optie_len = len;
- if (len) {
- if (len < IEEE80211_MAX_OPT_IE) {
- os_memcpy(mlme.im_optie, ie, len);
- } else {
- wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
- "opt_ie STA (addr " MACSTR " reason %d, "
- "ie_len %d)",
- __func__, MAC2STR(addr), status_code,
- (int) len);
- return -1;
- }
- }
- ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
- " reason %d)",
- __func__, MAC2STR(addr), status_code);
- }
- return ret;
-}
-
-
-static void
-atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
-{
- struct hostapd_data *hapd = drv->hapd;
- struct ieee80211req_wpaie ie;
- int ielen = 0;
- u8 *iebuf = NULL;
-
- /*
- * Fetch negotiated WPA/RSN parameters from the system.
- */
- os_memset(&ie, 0, sizeof(ie));
- os_memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
- if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
- /*
- * See ATH_WPS_IE comment in the beginning of the file for a
- * possible cause for the failure..
- */
- wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s",
- __func__, strerror(errno));
- goto no_ie;
- }
- wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE",
- ie.wpa_ie, IEEE80211_MAX_OPT_IE);
- wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
- ie.rsn_ie, IEEE80211_MAX_OPT_IE);
-#ifdef ATH_WPS_IE
- wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
- ie.wps_ie, IEEE80211_MAX_OPT_IE);
-#endif /* ATH_WPS_IE */
- iebuf = ie.wpa_ie;
- /* atheros seems to return some random data if WPA/RSN IE is not set.
- * Assume the IE was not included if the IE type is unknown. */
- if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
- iebuf[1] = 0;
- if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
- /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
- * set. This is needed for WPA2. */
- iebuf = ie.rsn_ie;
- if (iebuf[0] != WLAN_EID_RSN)
- iebuf[1] = 0;
- }
-
- ielen = iebuf[1];
-
-#ifdef ATH_WPS_IE
- /* if WPS IE is present, preference is given to WPS */
- if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) {
- iebuf = ie.wps_ie;
- ielen = ie.wps_ie[1];
- }
-#endif /* ATH_WPS_IE */
-
- if (ielen == 0)
- iebuf = NULL;
- else
- ielen += 2;
-
-no_ie:
- drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
-
- if (ether_addr_equal(addr, drv->acct_mac)) {
- /* Cached accounting data is not valid anymore. */
- os_memset(drv->acct_mac, 0, ETH_ALEN);
- os_memset(&drv->acct_data, 0, sizeof(drv->acct_data));
- }
-}
-
-static void
-atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
- char *custom, char *end)
-{
-#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
- wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
-
- if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
- char *pos;
- u8 addr[ETH_ALEN];
- pos = os_strstr(custom, "addr=");
- if (pos == NULL) {
- wpa_printf(MSG_DEBUG,
- "MLME-MICHAELMICFAILURE.indication "
- "without sender address ignored");
- return;
- }
- pos += 5;
- if (hwaddr_aton(pos, addr) == 0) {
- union wpa_event_data data;
- os_memset(&data, 0, sizeof(data));
- data.michael_mic_failure.unicast = 1;
- data.michael_mic_failure.src = addr;
- wpa_supplicant_event(drv->hapd,
- EVENT_MICHAEL_MIC_FAILURE, &data);
- } else {
- wpa_printf(MSG_DEBUG,
- "MLME-MICHAELMICFAILURE.indication "
- "with invalid MAC address");
- }
- } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
- char *key, *value;
- u32 val;
- key = custom;
- while ((key = os_strchr(key, '\n')) != NULL) {
- key++;
- value = os_strchr(key, '=');
- if (value == NULL)
- continue;
- *value++ = '\0';
- val = strtoul(value, NULL, 10);
- if (os_strcmp(key, "mac") == 0)
- hwaddr_aton(value, drv->acct_mac);
- else if (os_strcmp(key, "rx_packets") == 0)
- drv->acct_data.rx_packets = val;
- else if (os_strcmp(key, "tx_packets") == 0)
- drv->acct_data.tx_packets = val;
- else if (os_strcmp(key, "rx_bytes") == 0)
- drv->acct_data.rx_bytes = val;
- else if (os_strcmp(key, "tx_bytes") == 0)
- drv->acct_data.tx_bytes = val;
- key = value;
- }
-#ifdef CONFIG_WPS
- } else if (os_strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) {
- /* Some atheros kernels send push button as a wireless event */
- /* PROBLEM! this event is received for ALL BSSs ...
- * so all are enabled for WPS... ugh.
- */
- wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
- } else if (os_strncmp(custom, "Manage.prob_req ", 16) == 0) {
- /*
- * Atheros driver uses a hack to pass Probe Request frames as a
- * binary data in the custom wireless event. The old way (using
- * packet sniffing) didn't work when bridging.
- * Format: "Manage.prob_req <frame len>" | zero padding | frame
- */
- int len = atoi(custom + 16);
- if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
- "length %d", len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_WPS */
- } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
- /* Format: "Manage.assoc_req <frame len>" | zero padding |
- * frame */
- int len = atoi(custom + 17);
- if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
- wpa_printf(MSG_DEBUG,
- "Invalid Manage.assoc_req event length %d",
- len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) {
- /* Format: "Manage.auth <frame len>" | zero padding | frame */
- int len = atoi(custom + 12);
- if (len < 0 ||
- MGMT_FRAM_TAG_SIZE + len > end - custom) {
- wpa_printf(MSG_DEBUG,
- "Invalid Manage.auth event length %d", len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
- /* Format: "Manage.assoc_req <frame len>" | zero padding | frame
- */
- int len = atoi(custom + 14);
- if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
- wpa_printf(MSG_DEBUG,
- "Invalid Manage.action event length %d",
- len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- }
-}
-
-
-static void send_action_cb_event(struct atheros_driver_data *drv,
- char *data, size_t data_len)
-{
- union wpa_event_data event;
- struct ieee80211_send_action_cb *sa;
- const struct ieee80211_hdr *hdr;
- u16 fc;
-
- if (data_len < sizeof(*sa) + 24) {
- wpa_printf(MSG_DEBUG,
- "athr: Too short event message (data_len=%d sizeof(*sa)=%d)",
- (int) data_len, (int) sizeof(*sa));
- wpa_hexdump(MSG_DEBUG, "athr: Short event message",
- data, data_len);
- return;
- }
-
- sa = (struct ieee80211_send_action_cb *) data;
-
- hdr = (const struct ieee80211_hdr *) (sa + 1);
- fc = le_to_host16(hdr->frame_control);
-
- os_memset(&event, 0, sizeof(event));
- event.tx_status.type = WLAN_FC_GET_TYPE(fc);
- event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
- event.tx_status.dst = sa->dst_addr;
- event.tx_status.data = (const u8 *) hdr;
- event.tx_status.data_len = data_len - sizeof(*sa);
- event.tx_status.ack = sa->ack;
- wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
-}
-
-
-/*
-* Handle size of data problem. WEXT only allows data of 256 bytes for custom
-* events, and p2p data can be much bigger. So the athr driver sends a small
-* event telling me to collect the big data with an ioctl.
-* On the first event, send all pending events to supplicant.
-*/
-static void fetch_pending_big_events(struct atheros_driver_data *drv)
-{
- union wpa_event_data event;
- const struct ieee80211_mgmt *mgmt;
- u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */
- u16 fc, stype;
- struct iwreq iwr;
- size_t data_len;
- u32 freq, frame_type;
-
- while (1) {
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-
- iwr.u.data.pointer = (void *) tbuf;
- iwr.u.data.length = sizeof(tbuf);
- iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME;
-
- if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr)
- < 0) {
- if (errno == ENOSPC) {
- wpa_printf(MSG_DEBUG, "%s:%d exit",
- __func__, __LINE__);
- return;
- }
- wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM["
- "P2P_FETCH_FRAME] failed: %s",
- __func__, strerror(errno));
- return;
- }
- data_len = iwr.u.data.length;
- wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data",
- (u8 *) tbuf, data_len);
- if (data_len < sizeof(freq) + sizeof(frame_type) + 24) {
- wpa_printf(MSG_DEBUG, "athr: frame too short");
- continue;
- }
- os_memcpy(&freq, tbuf, sizeof(freq));
- os_memcpy(&frame_type, &tbuf[sizeof(freq)],
- sizeof(frame_type));
- mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)];
- data_len -= sizeof(freq) + sizeof(frame_type);
-
- if (frame_type == IEEE80211_EV_RX_MGMT) {
- fc = le_to_host16(mgmt->frame_control);
- stype = WLAN_FC_GET_STYPE(fc);
-
- wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u "
- "freq=%u len=%u", stype, freq, (int) data_len);
-
- if (stype == WLAN_FC_STYPE_ACTION) {
- os_memset(&event, 0, sizeof(event));
- event.rx_mgmt.frame = (const u8 *) mgmt;
- event.rx_mgmt.frame_len = data_len;
- wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT,
- &event);
- continue;
- }
- } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) {
- wpa_printf(MSG_DEBUG,
- "%s: ACTION_CB frame_type=%u len=%zu",
- __func__, frame_type, data_len);
- send_action_cb_event(drv, (void *) mgmt, data_len);
- } else {
- wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
- __func__, frame_type);
- continue;
- }
- }
-}
-
-static void
-atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
- int opcode, char *buf, int len)
-{
- switch (opcode) {
- case IEEE80211_EV_P2P_SEND_ACTION_CB:
- wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB");
- fetch_pending_big_events(drv);
- break;
- case IEEE80211_EV_RX_MGMT:
- wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
- fetch_pending_big_events(drv);
- break;
- default:
- break;
- }
-}
-
-static void
-atheros_wireless_event_wireless(struct atheros_driver_data *drv,
- char *data, unsigned int len)
-{
- struct iw_event iwe_buf, *iwe = &iwe_buf;
- char *pos, *end, *custom, *buf;
-
- pos = data;
- end = data + len;
-
- while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
- /* Event data may be unaligned, so make a local, aligned copy
- * before processing. */
- os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
- wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
- iwe->cmd, iwe->len);
- if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
- return;
-
- custom = pos + IW_EV_POINT_LEN;
- if (drv->we_version > 18 &&
- (iwe->cmd == IWEVMICHAELMICFAILURE ||
- iwe->cmd == IWEVASSOCREQIE ||
- iwe->cmd == IWEVCUSTOM)) {
- /* WE-19 removed the pointer from struct iw_point */
- char *dpos = (char *) &iwe_buf.u.data.length;
- int dlen = dpos - (char *) &iwe_buf;
- os_memcpy(dpos, pos + IW_EV_LCP_LEN,
- sizeof(struct iw_event) - dlen);
- } else {
- os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
- custom += IW_EV_POINT_OFF;
- }
-
- switch (iwe->cmd) {
- case IWEVEXPIRED:
- drv_event_disassoc(drv->hapd,
- (u8 *) iwe->u.addr.sa_data);
- break;
- case IWEVREGISTERED:
- atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
- break;
- case IWEVASSOCREQIE:
- /* Driver hack.. Use IWEVASSOCREQIE to bypass
- * IWEVCUSTOM size limitations. Need to handle this
- * just like IWEVCUSTOM.
- */
- case IWEVCUSTOM:
- if (iwe->u.data.length > end - custom)
- return;
- buf = os_malloc(iwe->u.data.length + 1);
- if (buf == NULL)
- return; /* XXX */
- os_memcpy(buf, custom, iwe->u.data.length);
- buf[iwe->u.data.length] = '\0';
-
- if (iwe->u.data.flags != 0) {
- atheros_wireless_event_atheros_custom(
- drv, (int) iwe->u.data.flags,
- buf, len);
- } else {
- atheros_wireless_event_wireless_custom(
- drv, buf, buf + iwe->u.data.length);
- }
- os_free(buf);
- break;
- }
-
- pos += iwe->len;
- }
-}
-
-
-static void
-atheros_wireless_event_rtm_newlink(void *ctx,
- struct ifinfomsg *ifi, u8 *buf, size_t len)
-{
- struct atheros_driver_data *drv = ctx;
- int attrlen, rta_len;
- struct rtattr *attr;
-
- if (ifi->ifi_index != drv->ifindex)
- return;
-
- attrlen = len;
- attr = (struct rtattr *) buf;
-
- rta_len = RTA_ALIGN(sizeof(struct rtattr));
- while (RTA_OK(attr, attrlen)) {
- if (attr->rta_type == IFLA_WIRELESS) {
- atheros_wireless_event_wireless(
- drv, ((char *) attr) + rta_len,
- attr->rta_len - rta_len);
- }
- attr = RTA_NEXT(attr, attrlen);
- }
-}
-
-
-static int
-atheros_get_we_version(struct atheros_driver_data *drv)
-{
- struct iw_range *range;
- struct iwreq iwr;
- int minlen;
- size_t buflen;
-
- drv->we_version = 0;
-
- /*
- * Use larger buffer than struct iw_range in order to allow the
- * structure to grow in the future.
- */
- buflen = sizeof(struct iw_range) + 500;
- range = os_zalloc(buflen);
- if (range == NULL)
- return -1;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.data.pointer = (caddr_t) range;
- iwr.u.data.length = buflen;
-
- minlen = ((char *) &range->enc_capa) - (char *) range +
- sizeof(range->enc_capa);
-
- if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
- strerror(errno));
- os_free(range);
- return -1;
- } else if (iwr.u.data.length >= minlen &&
- range->we_version_compiled >= 18) {
- wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
- "WE(source)=%d enc_capa=0x%x",
- range->we_version_compiled,
- range->we_version_source,
- range->enc_capa);
- drv->we_version = range->we_version_compiled;
- }
-
- os_free(range);
- return 0;
-}
-
-
-static int
-atheros_wireless_event_init(struct atheros_driver_data *drv)
-{
- struct netlink_config *cfg;
-
- atheros_get_we_version(drv);
-
- cfg = os_zalloc(sizeof(*cfg));
- if (cfg == NULL)
- return -1;
- cfg->ctx = drv;
- cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
- drv->netlink = netlink_init(cfg);
- if (drv->netlink == NULL) {
- os_free(cfg);
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr, u32 flags, int link_id)
-{
- struct atheros_driver_data *drv = priv;
- unsigned char buf[3000];
- unsigned char *bp = buf;
- struct l2_ethhdr *eth;
- size_t len;
- int status;
-
- /*
- * Prepend the Ethernet header. If the caller left us
- * space at the front we could just insert it but since
- * we don't know we copy to a local buffer. Given the frequency
- * and size of frames this probably doesn't matter.
- */
- len = data_len + sizeof(struct l2_ethhdr);
- if (len > sizeof(buf)) {
- bp = os_malloc(len);
- if (bp == NULL) {
- wpa_printf(MSG_INFO,
- "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
- (unsigned long) len);
- return -1;
- }
- }
- eth = (struct l2_ethhdr *) bp;
- os_memcpy(eth->h_dest, addr, ETH_ALEN);
- os_memcpy(eth->h_source, own_addr, ETH_ALEN);
- eth->h_proto = host_to_be16(ETH_P_EAPOL);
- os_memcpy(eth + 1, data, data_len);
-
- wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
-
- status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
-
- if (bp != buf)
- os_free(bp);
- return status;
-}
-
-static void
-handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
-{
- struct atheros_driver_data *drv = ctx;
- drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
- len - sizeof(struct l2_ethhdr));
-}
-
-
-static void atheros_read_fils_cap(struct atheros_driver_data *drv)
-{
- int fils = 0;
-
-#ifdef CONFIG_FILS
- /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
- * to automatically check this against the driver header files. */
- if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
- wpa_printf(MSG_DEBUG,
- "%s: Failed to get FILS capability from driver",
- __func__);
- /* Assume driver does not support FILS */
- fils = 0;
- }
-#endif /* CONFIG_FILS */
- drv->fils_en = fils;
- wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
-}
-
-
-static void *
-atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
-{
- struct atheros_driver_data *drv;
- struct ifreq ifr;
- struct iwreq iwr;
- char brname[IFNAMSIZ];
-
- drv = os_zalloc(sizeof(struct atheros_driver_data));
- if (drv == NULL) {
- wpa_printf(MSG_INFO,
- "Could not allocate memory for atheros driver data");
- return NULL;
- }
-
- drv->hapd = hapd;
- drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->ioctl_sock < 0) {
- wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- goto bad;
- }
- os_memcpy(drv->iface, params->ifname, sizeof(drv->iface));
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
- if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
- wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
- strerror(errno));
- goto bad;
- }
- drv->ifindex = ifr.ifr_ifindex;
-
- drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
- handle_read, drv, 1);
- if (drv->sock_xmit == NULL)
- goto bad;
- if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
- goto bad;
- os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN);
- if (params->bridge[0]) {
- wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
- params->bridge[0]);
- drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
- ETH_P_EAPOL, handle_read, drv,
- 1);
- if (drv->sock_recv == NULL)
- goto bad;
- } else if (linux_br_get(brname, drv->iface) == 0) {
- wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
- "EAPOL receive", brname);
- drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
- handle_read, drv, 1);
- if (drv->sock_recv == NULL)
- goto bad;
- } else
- drv->sock_recv = drv->sock_xmit;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-
- iwr.u.mode = IW_MODE_MASTER;
-
- if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
- wpa_printf(MSG_ERROR,
- "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
- strerror(errno));
- goto bad;
- }
-
- /* mark down during setup */
- linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
- atheros_set_privacy(drv, 0); /* default to no privacy */
-
- if (atheros_receive_pkt(drv))
- goto bad;
-
- if (atheros_wireless_event_init(drv))
- goto bad;
-
- /* Read FILS capability from the driver */
- atheros_read_fils_cap(drv);
-
- return drv;
-bad:
- atheros_reset_appfilter(drv);
- if (drv->sock_raw)
- l2_packet_deinit(drv->sock_raw);
- if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
- l2_packet_deinit(drv->sock_recv);
- if (drv->sock_xmit != NULL)
- l2_packet_deinit(drv->sock_xmit);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
- os_free(drv);
- return NULL;
-}
-
-
-static void
-atheros_deinit(void *priv)
-{
- struct atheros_driver_data *drv = priv;
-
- atheros_reset_appfilter(drv);
-
- if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
- atheros_set_opt_ie(priv, NULL, 0);
- wpabuf_free(drv->wpa_ie);
- wpabuf_free(drv->wps_beacon_ie);
- wpabuf_free(drv->wps_probe_resp_ie);
- }
- netlink_deinit(drv->netlink);
- (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
- if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
- l2_packet_deinit(drv->sock_recv);
- if (drv->sock_xmit != NULL)
- l2_packet_deinit(drv->sock_xmit);
- if (drv->sock_raw)
- l2_packet_deinit(drv->sock_raw);
- os_free(drv);
-}
-
-static int
-atheros_set_ssid(void *priv, const u8 *buf, int len)
-{
- struct atheros_driver_data *drv = priv;
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.essid.flags = 1; /* SSID active */
- iwr.u.essid.pointer = (caddr_t) buf;
- iwr.u.essid.length = len;
-
- if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
- len, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-atheros_get_ssid(void *priv, u8 *buf, int len)
-{
- struct atheros_driver_data *drv = priv;
- struct iwreq iwr;
- int ret = 0;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.essid.pointer = (caddr_t) buf;
- iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
- IW_ESSID_MAX_SIZE : len;
-
- if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
- strerror(errno));
- ret = -1;
- } else
- ret = iwr.u.essid.length;
-
- return ret;
-}
-
-static int
-atheros_set_countermeasures(void *priv, int enabled)
-{
- struct atheros_driver_data *drv = priv;
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
- return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
-}
-
-static int
-atheros_commit(void *priv)
-{
- struct atheros_driver_data *drv = priv;
- return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
-}
-
-static int atheros_set_authmode(void *priv, int auth_algs)
-{
- int authmode;
-
- if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
- (auth_algs & WPA_AUTH_ALG_SHARED))
- authmode = IEEE80211_AUTH_AUTO;
- else if (auth_algs & WPA_AUTH_ALG_OPEN)
- authmode = IEEE80211_AUTH_OPEN;
- else if (auth_algs & WPA_AUTH_ALG_SHARED)
- authmode = IEEE80211_AUTH_SHARED;
- else
- return -1;
-
- return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
-}
-
-static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
-{
- /*
- * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
- * set_generic_elem, and hapd_set_ssid.
- */
-
- wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
- "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
- "wpa_version=0x%x privacy=%d interworking=%d",
- params->pairwise_ciphers, params->group_cipher,
- params->key_mgmt_suites, params->auth_algs,
- params->wpa_version, params->privacy, params->interworking);
- wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
- params->ssid, params->ssid_len);
- if (params->hessid)
- wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
- MAC2STR(params->hessid));
- wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
- params->beacon_ies);
- wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
- params->proberesp_ies);
- wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
- params->assocresp_ies);
-
-#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
- if (params->osen) {
- struct wpa_bss_params bss_params;
-
- os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
- bss_params.enabled = 1;
- bss_params.wpa = 2;
- bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
- bss_params.wpa_group = WPA_CIPHER_CCMP;
- bss_params.ieee802_1x = 1;
-
- if (atheros_set_privacy(priv, 1) ||
- set80211param(priv, IEEE80211_PARAM_OSEN, 1))
- return -1;
-
- return atheros_set_ieee8021x(priv, &bss_params);
- }
-#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
-
- return 0;
-}
-
-
-static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
- int noack, unsigned int freq,
- const u16 *csa_offs, size_t csa_offs_len,
- int no_encrypt, unsigned int wait, int link_id)
-{
- struct atheros_driver_data *drv = priv;
- u8 buf[1510];
- const struct ieee80211_mgmt *mgmt;
- struct ieee80211req_mgmtbuf *mgmt_frm;
-
- mgmt = (const struct ieee80211_mgmt *) frm;
- wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
- (unsigned long) data_len, MAC2STR(mgmt->da));
- mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
- os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
- mgmt_frm->buflen = data_len;
- if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
- wpa_printf(MSG_INFO, "atheros: Too long frame for "
- "atheros_send_mgmt (%u)", (unsigned int) data_len);
- return -1;
- }
- os_memcpy(&mgmt_frm->buf[0], frm, data_len);
- return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
- sizeof(struct ieee80211req_mgmtbuf) + data_len);
-}
-
-
-#ifdef CONFIG_IEEE80211R
-
-static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
- size_t tspec_ielen)
-{
- struct atheros_driver_data *drv = priv;
- int retv;
- struct ieee80211req_res req;
- struct ieee80211req_res_addts *addts = &req.u.addts;
-
- wpa_printf(MSG_DEBUG, "%s", __func__);
- req.type = IEEE80211_RESREQ_ADDTS;
- os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
- os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
- retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
- sizeof(struct ieee80211req_res));
- if (retv < 0) {
- wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
- "retv = %d", __func__, retv);
- return -1;
- }
- os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
- return addts->status;
-}
-
-
-static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211req_res req;
- struct ieee80211req_res_addnode *addnode = &req.u.addnode;
-
- wpa_printf(MSG_DEBUG, "%s", __func__);
- req.type = IEEE80211_RESREQ_ADDNODE;
- os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
- addnode->auth_alg = auth_alg;
- return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
- sizeof(struct ieee80211req_res));
-}
-
-#endif /* CONFIG_IEEE80211R */
-
-
-/* Use only to set a big param, get will not work. */
-static int
-set80211big(struct atheros_driver_data *drv, int op, const void *data, int len)
-{
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-
- iwr.u.data.pointer = (void *) data;
- iwr.u.data.length = len;
- iwr.u.data.flags = op;
- wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x",
- __func__, op, op, athr_get_param_name(op), len);
-
- if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) {
- wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d "
- "value=0x%x,0x%x failed: %d (%s)",
- __func__, op, athr_get_ioctl_name(op), iwr.u.mode,
- iwr.u.mode, iwr.u.data.length,
- iwr.u.data.flags, errno, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-
-static int atheros_send_action(void *priv, unsigned int freq,
- unsigned int wait,
- const u8 *dst, const u8 *src,
- const u8 *bssid,
- const u8 *data, size_t data_len, int no_cck)
-{
- struct atheros_driver_data *drv = priv;
- struct ieee80211_p2p_send_action *act;
- int res;
-
- act = os_zalloc(sizeof(*act) + data_len);
- if (act == NULL)
- return -1;
- act->freq = freq;
- os_memcpy(act->dst_addr, dst, ETH_ALEN);
- os_memcpy(act->src_addr, src, ETH_ALEN);
- os_memcpy(act->bssid, bssid, ETH_ALEN);
- os_memcpy(act + 1, data, data_len);
- wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src="
- MACSTR ", bssid=" MACSTR,
- __func__, act->freq, wait, MAC2STR(act->dst_addr),
- MAC2STR(act->src_addr), MAC2STR(act->bssid));
- wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act));
- wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len);
-
- res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION,
- act, sizeof(*act) + data_len);
- os_free(act);
- return res;
-}
-
-
-#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
-static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
- u8 *ie, u16 *len, enum wnm_oper oper)
-{
-#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */
- u8 buf[IEEE80211_APPIE_MAX];
- struct ieee80211req_getset_appiebuf *tfs_ie;
- u16 val;
-
- wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
- drv->iface, oper, MAC2STR(peer));
-
- switch (oper) {
- case WNM_SLEEP_TFS_REQ_IE_SET:
- if (*len > IEEE80211_APPIE_MAX -
- sizeof(struct ieee80211req_getset_appiebuf)) {
- wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
- return -1;
- }
- tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
- tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
- tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
-
- /* Command header for driver */
- os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
- val = oper;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
- val = *len;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
-
- /* copy the ie */
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
-
- if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
- IEEE80211_APPIE_MAX)) {
- wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
- "%s", __func__, strerror(errno));
- return -1;
- }
- break;
- case WNM_SLEEP_TFS_RESP_IE_ADD:
- tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
- tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
- tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
- sizeof(struct ieee80211req_getset_appiebuf);
- /* Command header for driver */
- os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
- val = oper;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
- val = 0;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
-
- if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
- IEEE80211_APPIE_MAX)) {
- wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
- "%s", __func__, strerror(errno));
- return -1;
- }
-
- *len = tfs_ie->app_buflen;
- os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
- wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
- *len);
- break;
- case WNM_SLEEP_TFS_RESP_IE_NONE:
- *len = 0;
- break;
- case WNM_SLEEP_TFS_IE_DEL:
- tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
- tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
- tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
- sizeof(struct ieee80211req_getset_appiebuf);
- /* Command header for driver */
- os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
- val = oper;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
- val = 0;
- os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
-
- if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
- IEEE80211_APPIE_MAX)) {
- wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
- "%s", __func__, strerror(errno));
- return -1;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
- break;
- }
-
- return 0;
-}
-
-
-static int atheros_wnm_sleep(struct atheros_driver_data *drv,
- const u8 *peer, enum wnm_oper oper)
-{
- u8 *data, *pos;
- size_t dlen;
- int ret;
- u16 val;
-
- wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
- oper, MAC2STR(peer));
-
- dlen = ETH_ALEN + 2 + 2;
- data = os_malloc(dlen);
- if (data == NULL)
- return -1;
-
- /* Command header for driver */
- pos = data;
- os_memcpy(pos, peer, ETH_ALEN);
- pos += ETH_ALEN;
-
- val = oper;
- os_memcpy(pos, &val, 2);
- pos += 2;
-
- val = 0;
- os_memcpy(pos, &val, 2);
-
- ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
-
- os_free(data);
-
- return ret;
-}
-
-
-static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
- u8 *buf, u16 *buf_len)
-{
- struct atheros_driver_data *drv = priv;
-
- switch (oper) {
- case WNM_SLEEP_ENTER_CONFIRM:
- case WNM_SLEEP_ENTER_FAIL:
- case WNM_SLEEP_EXIT_CONFIRM:
- case WNM_SLEEP_EXIT_FAIL:
- return atheros_wnm_sleep(drv, peer, oper);
- case WNM_SLEEP_TFS_REQ_IE_SET:
- case WNM_SLEEP_TFS_RESP_IE_ADD:
- case WNM_SLEEP_TFS_RESP_IE_NONE:
- case WNM_SLEEP_TFS_IE_DEL:
- return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
- default:
- wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
- oper);
- return -1;
- }
-}
-#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
-
-
-const struct wpa_driver_ops wpa_driver_atheros_ops = {
- .name = "atheros",
- .hapd_init = atheros_init,
- .hapd_deinit = atheros_deinit,
- .set_ieee8021x = atheros_set_ieee8021x,
- .set_privacy = atheros_set_privacy,
- .set_key = atheros_set_key,
- .get_seqnum = atheros_get_seqnum,
- .flush = atheros_flush,
- .set_generic_elem = atheros_set_opt_ie,
- .sta_set_flags = atheros_sta_set_flags,
- .read_sta_data = atheros_read_sta_driver_data,
- .hapd_send_eapol = atheros_send_eapol,
- .sta_disassoc = atheros_sta_disassoc,
- .sta_deauth = atheros_sta_deauth,
- .hapd_set_ssid = atheros_set_ssid,
- .hapd_get_ssid = atheros_get_ssid,
- .set_countermeasures = atheros_set_countermeasures,
- .sta_clear_stats = atheros_sta_clear_stats,
- .commit = atheros_commit,
- .set_ap_wps_ie = atheros_set_ap_wps_ie,
- .set_authmode = atheros_set_authmode,
- .set_ap = atheros_set_ap,
- .sta_assoc = atheros_sta_assoc,
- .sta_auth = atheros_sta_auth,
- .send_mlme = atheros_send_mgmt,
-#ifdef CONFIG_IEEE80211R
- .add_tspec = atheros_add_tspec,
- .add_sta_node = atheros_add_sta_node,
-#endif /* CONFIG_IEEE80211R */
- .send_action = atheros_send_action,
-#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
- .wnm_oper = atheros_wnm_oper,
-#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
- .set_qos_map = atheros_set_qos_map,
-};
diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c
index 3c1ff1b88044..6faac2ceea4c 100644
--- a/contrib/wpa/src/drivers/driver_bsd.c
+++ b/contrib/wpa/src/drivers/driver_bsd.c
@@ -354,6 +354,9 @@ bsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
const u8 *key = params->key;
size_t key_len = params->key_len;
+ if (params->key_flag & KEY_FLAG_NEXT)
+ return -1;
+
wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
"seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
set_tx, seq_len, key_len);
@@ -1052,7 +1055,7 @@ bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code,
static int
bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason_code)
+ u16 reason_code, int link_id)
{
return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
addr);
@@ -1675,7 +1678,8 @@ get80211opmode(struct bsd_driver_data *drv)
}
static void *
-wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
+wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv,
+ enum wpa_p2p_mode p2p_mode)
{
#define GETPARAM(drv, param, v) \
(((v) = get80211param(drv, param)) != -1)
diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c
index 9bc5a731dd82..812305cae263 100644
--- a/contrib/wpa/src/drivers/driver_common.c
+++ b/contrib/wpa/src/drivers/driver_common.c
@@ -100,6 +100,16 @@ const char * event_to_string(enum wpa_event_type event)
E2S(LINK_CH_SWITCH_STARTED);
E2S(TID_LINK_MAP);
E2S(LINK_RECONFIG);
+ E2S(MLD_INTERFACE_FREED);
+ E2S(SETUP_LINK_RECONFIG);
+ E2S(NAN_CLUSTER_JOIN);
+ E2S(NAN_NEXT_DW);
+ E2S(INCUMBT_SIG_INTF_DETECTED);
+ E2S(NAN_SCHED_UPDATE_DONE);
+ E2S(NAN_ULW_UPDATE);
+ E2S(NAN_CHAN_EVACUATION);
+ E2S(PEER_MEASUREMENT_RESULT);
+ E2S(PEER_MEASUREMENT_COMPLETE);
}
return "UNKNOWN";
@@ -202,6 +212,21 @@ bool he_supported(const struct hostapd_hw_modes *hw_mode,
}
+bool eht_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode)
+{
+ if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_EHT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports EHT. Assume
+ * it does to avoid connection issues.
+ */
+ return true;
+ }
+
+ return hw_mode->eht_capab[op_mode].eht_supported;
+}
+
+
static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
int capa_trigger, u8 *param_trigger)
{
@@ -373,6 +398,7 @@ const char * driver_flag2_to_string(u64 flag2)
DF2S(MLO);
DF2S(SCAN_MIN_PREQ);
DF2S(SAE_OFFLOAD_STA);
+ DF2S(SPP_AMSDU);
}
return "UNKNOWN";
#undef DF2S
diff --git a/contrib/wpa/src/drivers/driver_hostap.c b/contrib/wpa/src/drivers/driver_hostap.c
deleted file mode 100644
index 3aa5860bc433..000000000000
--- a/contrib/wpa/src/drivers/driver_hostap.c
+++ /dev/null
@@ -1,1208 +0,0 @@
-/*
- * Driver interaction with Linux Host AP driver
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "linux_wext.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-#include "eloop.h"
-#include "driver_hostap.h"
-
-
-#include <net/if_arp.h>
-#include <netpacket/packet.h>
-
-#include "priv_netlink.h"
-#include "netlink.h"
-#include "linux_ioctl.h"
-#include "common/ieee802_11_defs.h"
-#include "common/ieee802_11_common.h"
-
-
-/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
- * frames that might be longer than normal default MTU and they are not
- * fragmented */
-#define HOSTAPD_MTU 2290
-
-static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-struct hostap_driver_data {
- struct hostapd_data *hapd;
-
- char iface[IFNAMSIZ + 1];
- int sock; /* raw packet socket for driver access */
- int ioctl_sock; /* socket for ioctl() use */
- struct netlink_data *netlink;
-
- int we_version;
-
- u8 *generic_ie;
- size_t generic_ie_len;
- u8 *wps_ie;
- size_t wps_ie_len;
-};
-
-
-static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
- int len);
-static int hostap_set_iface_flags(void *priv, int dev_up);
-
-static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
- u16 stype)
-{
- struct ieee80211_hdr *hdr;
- u16 fc, ethertype;
- u8 *pos, *sa;
- size_t left;
- union wpa_event_data event;
-
- if (len < sizeof(struct ieee80211_hdr))
- return;
-
- hdr = (struct ieee80211_hdr *) buf;
- fc = le_to_host16(hdr->frame_control);
-
- if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
- printf("Not ToDS data frame (fc=0x%04x)\n", fc);
- return;
- }
-
- sa = hdr->addr2;
- os_memset(&event, 0, sizeof(event));
- event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
- event.rx_from_unknown.addr = sa;
- wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
-
- pos = (u8 *) (hdr + 1);
- left = len - sizeof(*hdr);
-
- if (left < sizeof(rfc1042_header)) {
- printf("Too short data frame\n");
- return;
- }
-
- if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
- printf("Data frame with no RFC1042 header\n");
- return;
- }
- pos += sizeof(rfc1042_header);
- left -= sizeof(rfc1042_header);
-
- if (left < 2) {
- printf("No ethertype in data frame\n");
- return;
- }
-
- ethertype = WPA_GET_BE16(pos);
- pos += 2;
- left -= 2;
- switch (ethertype) {
- case ETH_P_PAE:
- drv_event_eapol_rx(drv->hapd, sa, pos, left);
- break;
-
- default:
- printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
- break;
- }
-}
-
-
-static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf,
- size_t len, int ok)
-{
- struct ieee80211_hdr *hdr;
- u16 fc;
- union wpa_event_data event;
-
- hdr = (struct ieee80211_hdr *) buf;
- fc = le_to_host16(hdr->frame_control);
-
- os_memset(&event, 0, sizeof(event));
- event.tx_status.type = WLAN_FC_GET_TYPE(fc);
- event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
- event.tx_status.dst = hdr->addr1;
- event.tx_status.data = buf;
- event.tx_status.data_len = len;
- event.tx_status.ack = ok;
- wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
-}
-
-
-static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
-{
- struct ieee80211_hdr *hdr;
- u16 fc, type, stype;
- size_t data_len = len;
- int ver;
- union wpa_event_data event;
-
- /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
- * these to user space */
- if (len < 24) {
- wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)",
- (unsigned long) len);
- return;
- }
-
- hdr = (struct ieee80211_hdr *) buf;
- fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
-
- if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) {
- wpa_hexdump(MSG_MSGDUMP, "Received management frame",
- buf, len);
- }
-
- ver = fc & WLAN_FC_PVER;
-
- /* protocol version 2 is reserved for indicating ACKed frame (TX
- * callbacks), and version 1 for indicating failed frame (no ACK, TX
- * callbacks) */
- if (ver == 1 || ver == 2) {
- handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
- return;
- } else if (ver != 0) {
- printf("unknown protocol version %d\n", ver);
- return;
- }
-
- switch (type) {
- case WLAN_FC_TYPE_MGMT:
- os_memset(&event, 0, sizeof(event));
- event.rx_mgmt.frame = buf;
- event.rx_mgmt.frame_len = data_len;
- wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
- break;
- case WLAN_FC_TYPE_CTRL:
- wpa_printf(MSG_DEBUG, "CTRL");
- break;
- case WLAN_FC_TYPE_DATA:
- wpa_printf(MSG_DEBUG, "DATA");
- handle_data(drv, buf, data_len, stype);
- break;
- default:
- wpa_printf(MSG_DEBUG, "unknown frame type %d", type);
- break;
- }
-}
-
-
-static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct hostap_driver_data *drv = eloop_ctx;
- int len;
- unsigned char buf[3000];
-
- len = recv(sock, buf, sizeof(buf), 0);
- if (len < 0) {
- wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
- return;
- }
-
- handle_frame(drv, buf, len);
-}
-
-
-static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
-{
- struct ifreq ifr;
- struct sockaddr_ll addr;
-
- drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
- if (drv->sock < 0) {
- wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
- strerror(errno));
- return -1;
- }
-
- if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
- wpa_printf(MSG_ERROR, "Could not register read socket");
- return -1;
- }
-
- memset(&ifr, 0, sizeof(ifr));
- if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap",
- drv->iface) >= (int) sizeof(ifr.ifr_name)) {
- wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
- return -1;
- }
- if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
- wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
- strerror(errno));
- return -1;
- }
-
- if (hostap_set_iface_flags(drv, 1)) {
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sll_family = AF_PACKET;
- addr.sll_ifindex = ifr.ifr_ifindex;
- wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
- addr.sll_ifindex);
-
- if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
- return -1;
- }
-
- return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr);
-}
-
-
-static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
- unsigned int freq,
- const u16 *csa_offs, size_t csa_offs_len,
- int no_encrypt, unsigned int wait, int link_id)
-{
- struct hostap_driver_data *drv = priv;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
- int res;
-
- /* Request TX callback */
- hdr->frame_control |= host_to_le16(BIT(1));
- res = send(drv->sock, msg, len, 0);
- hdr->frame_control &= ~host_to_le16(BIT(1));
-
- return res;
-}
-
-
-static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt, const u8 *own_addr,
- u32 flags, int link_id)
-{
- struct hostap_driver_data *drv = priv;
- struct ieee80211_hdr *hdr;
- size_t len;
- u8 *pos;
- int res;
-
- len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len;
- hdr = os_zalloc(len);
- if (hdr == NULL) {
- printf("malloc() failed for hostapd_send_data(len=%lu)\n",
- (unsigned long) len);
- return -1;
- }
-
- hdr->frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
- hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
- if (encrypt)
- hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
-
- pos = (u8 *) (hdr + 1);
- memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
- pos += sizeof(rfc1042_header);
- *((u16 *) pos) = htons(ETH_P_PAE);
- pos += 2;
- memcpy(pos, data, data_len);
-
- res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0, 0, 0, -1);
- if (res < 0) {
- wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
- "failed: %d (%s)",
- (unsigned long) len, errno, strerror(errno));
- }
- os_free(hdr);
-
- return res;
-}
-
-
-static int hostap_sta_set_flags(void *priv, const u8 *addr,
- unsigned int total_flags, unsigned int flags_or,
- unsigned int flags_and)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
-
- if (flags_or & WPA_STA_AUTHORIZED)
- flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */
- if (!(flags_and & WPA_STA_AUTHORIZED))
- flags_and = ~BIT(5);
- else
- flags_and = ~0;
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA;
- memcpy(param.sta_addr, addr, ETH_ALEN);
- param.u.set_flags_sta.flags_or = flags_or;
- param.u.set_flags_sta.flags_and = flags_and;
- return hostapd_ioctl(drv, &param, sizeof(param));
-}
-
-
-static int hostap_set_iface_flags(void *priv, int dev_up)
-{
- struct hostap_driver_data *drv = priv;
- struct ifreq ifr;
- char ifname[IFNAMSIZ];
-
- if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) {
- wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
- return -1;
- }
- if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
- return -1;
-
- if (dev_up) {
- memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- ifr.ifr_mtu = HOSTAPD_MTU;
- if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
- wpa_printf(MSG_INFO,
- "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
- strerror(errno));
- }
- }
-
- return 0;
-}
-
-
-static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
- int len)
-{
- struct hostap_driver_data *drv = priv;
- struct iwreq iwr;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.data.pointer = (caddr_t) param;
- iwr.u.data.length = len;
-
- if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
-static int wpa_driver_hostap_set_key(void *priv,
- struct wpa_driver_set_key_params *params)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param *param;
- u8 *buf;
- size_t blen;
- int ret = 0;
- enum wpa_alg alg = params->alg;
- const u8 *addr = params->addr;
- int key_idx = params->key_idx;
- int set_tx = params->set_tx;
- const u8 *key = params->key;
- size_t key_len = params->key_len;
-
- blen = sizeof(*param) + key_len;
- buf = os_zalloc(blen);
- if (buf == NULL)
- return -1;
-
- param = (struct prism2_hostapd_param *) buf;
- param->cmd = PRISM2_SET_ENCRYPTION;
- if (addr == NULL)
- memset(param->sta_addr, 0xff, ETH_ALEN);
- else
- memcpy(param->sta_addr, addr, ETH_ALEN);
- switch (alg) {
- case WPA_ALG_NONE:
- os_strlcpy((char *) param->u.crypt.alg, "NONE",
- HOSTAP_CRYPT_ALG_NAME_LEN);
- break;
- case WPA_ALG_WEP:
- os_strlcpy((char *) param->u.crypt.alg, "WEP",
- HOSTAP_CRYPT_ALG_NAME_LEN);
- break;
- case WPA_ALG_TKIP:
- os_strlcpy((char *) param->u.crypt.alg, "TKIP",
- HOSTAP_CRYPT_ALG_NAME_LEN);
- break;
- case WPA_ALG_CCMP:
- os_strlcpy((char *) param->u.crypt.alg, "CCMP",
- HOSTAP_CRYPT_ALG_NAME_LEN);
- break;
- default:
- os_free(buf);
- return -1;
- }
- param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
- param->u.crypt.idx = key_idx;
- param->u.crypt.key_len = key_len;
- memcpy((u8 *) (param + 1), key, key_len);
-
- if (hostapd_ioctl(drv, param, blen)) {
- printf("Failed to set encryption.\n");
- ret = -1;
- }
- free(buf);
-
- return ret;
-}
-
-
-static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
- int idx, int link_id, u8 *seq)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param *param;
- u8 *buf;
- size_t blen;
- int ret = 0;
-
- blen = sizeof(*param) + 32;
- buf = os_zalloc(blen);
- if (buf == NULL)
- return -1;
-
- param = (struct prism2_hostapd_param *) buf;
- param->cmd = PRISM2_GET_ENCRYPTION;
- if (addr == NULL)
- os_memset(param->sta_addr, 0xff, ETH_ALEN);
- else
- os_memcpy(param->sta_addr, addr, ETH_ALEN);
- param->u.crypt.idx = idx;
-
- if (hostapd_ioctl(drv, param, blen)) {
- printf("Failed to get encryption.\n");
- ret = -1;
- } else {
- os_memcpy(seq, param->u.crypt.seq, 8);
- }
- os_free(buf);
-
- return ret;
-}
-
-
-static int hostap_ioctl_prism2param(void *priv, int param, int value)
-{
- struct hostap_driver_data *drv = priv;
- struct iwreq iwr;
- int *i;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- i = (int *) iwr.u.name;
- *i++ = param;
- *i++ = value;
-
- if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
-static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params)
-{
- struct hostap_driver_data *drv = priv;
- int enabled = params->enabled;
-
- /* enable kernel driver support for IEEE 802.1X */
- if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) {
- printf("Could not setup IEEE 802.1X support in kernel driver."
- "\n");
- return -1;
- }
-
- if (!enabled)
- return 0;
-
- /* use host driver implementation of encryption to allow
- * individual keys and passing plaintext EAPOL frames */
- if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) ||
- hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) {
- printf("Could not setup host-based encryption in kernel "
- "driver.\n");
- return -1;
- }
-
- return 0;
-}
-
-
-static int hostap_set_privacy(void *priv, int enabled)
-{
- struct hostap_drvier_data *drv = priv;
-
- return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
- enabled);
-}
-
-
-static int hostap_set_ssid(void *priv, const u8 *buf, int len)
-{
- struct hostap_driver_data *drv = priv;
- struct iwreq iwr;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.essid.flags = 1; /* SSID active */
- iwr.u.essid.pointer = (caddr_t) buf;
- iwr.u.essid.length = len + 1;
-
- if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
- len, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
-static int hostap_flush(void *priv, int link_id)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
-
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_FLUSH;
- return hostapd_ioctl(drv, &param, sizeof(param));
-}
-
-
-static int hostap_read_sta_data(void *priv,
- struct hostap_sta_driver_data *data,
- const u8 *addr)
-{
- struct hostap_driver_data *drv = priv;
- char buf[1024], line[128], *pos;
- FILE *f;
- unsigned long val;
-
- memset(data, 0, sizeof(*data));
- snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR,
- drv->iface, MAC2STR(addr));
-
- f = fopen(buf, "r");
- if (!f)
- return -1;
- /* Need to read proc file with in one piece, so use large enough
- * buffer. */
- setbuffer(f, buf, sizeof(buf));
-
- while (fgets(line, sizeof(line), f)) {
- pos = strchr(line, '=');
- if (!pos)
- continue;
- *pos++ = '\0';
- val = strtoul(pos, NULL, 10);
- if (strcmp(line, "rx_packets") == 0)
- data->rx_packets = val;
- else if (strcmp(line, "tx_packets") == 0)
- data->tx_packets = val;
- else if (strcmp(line, "rx_bytes") == 0)
- data->rx_bytes = val;
- else if (strcmp(line, "tx_bytes") == 0)
- data->tx_bytes = val;
- }
-
- fclose(f);
-
- return 0;
-}
-
-
-static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
- int tx_supp_rates = 0;
- size_t i;
-
-#define WLAN_RATE_1M BIT(0)
-#define WLAN_RATE_2M BIT(1)
-#define WLAN_RATE_5M5 BIT(2)
-#define WLAN_RATE_11M BIT(3)
-
- for (i = 0; i < params->supp_rates_len; i++) {
- if ((params->supp_rates[i] & 0x7f) == 2)
- tx_supp_rates |= WLAN_RATE_1M;
- if ((params->supp_rates[i] & 0x7f) == 4)
- tx_supp_rates |= WLAN_RATE_2M;
- if ((params->supp_rates[i] & 0x7f) == 11)
- tx_supp_rates |= WLAN_RATE_5M5;
- if ((params->supp_rates[i] & 0x7f) == 22)
- tx_supp_rates |= WLAN_RATE_11M;
- }
-
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_ADD_STA;
- memcpy(param.sta_addr, params->addr, ETH_ALEN);
- param.u.add_sta.aid = params->aid;
- param.u.add_sta.capability = params->capability;
- param.u.add_sta.tx_supp_rates = tx_supp_rates;
- return hostapd_ioctl(drv, &param, sizeof(param));
-}
-
-
-static int hostap_sta_remove(void *priv, const u8 *addr)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
-
- hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED);
-
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
- memcpy(param.sta_addr, addr, ETH_ALEN);
- if (hostapd_ioctl(drv, &param, sizeof(param))) {
- printf("Could not remove station from kernel driver.\n");
- return -1;
- }
- return 0;
-}
-
-
-static int hostap_get_inact_sec(void *priv, const u8 *addr)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
-
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
- memcpy(param.sta_addr, addr, ETH_ALEN);
- if (hostapd_ioctl(drv, &param, sizeof(param))) {
- return -1;
- }
-
- return param.u.get_info_sta.inactive_sec;
-}
-
-
-static int hostap_sta_clear_stats(void *priv, const u8 *addr)
-{
- struct hostap_driver_data *drv = priv;
- struct prism2_hostapd_param param;
-
- memset(&param, 0, sizeof(param));
- param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS;
- memcpy(param.sta_addr, addr, ETH_ALEN);
- if (hostapd_ioctl(drv, &param, sizeof(param))) {
- return -1;
- }
-
- return 0;
-}
-
-
-static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
-{
- struct prism2_hostapd_param *param;
- int res;
- size_t blen, elem_len;
-
- elem_len = drv->generic_ie_len + drv->wps_ie_len;
- blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
- if (blen < sizeof(*param))
- blen = sizeof(*param);
-
- param = os_zalloc(blen);
- if (param == NULL)
- return -1;
-
- param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
- param->u.generic_elem.len = elem_len;
- if (drv->generic_ie) {
- os_memcpy(param->u.generic_elem.data, drv->generic_ie,
- drv->generic_ie_len);
- }
- if (drv->wps_ie) {
- os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
- drv->wps_ie, drv->wps_ie_len);
- }
- wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
- param->u.generic_elem.data, elem_len);
- res = hostapd_ioctl(drv, param, blen);
-
- os_free(param);
-
- return res;
-}
-
-
-static int hostap_set_generic_elem(void *priv,
- const u8 *elem, size_t elem_len)
-{
- struct hostap_driver_data *drv = priv;
-
- os_free(drv->generic_ie);
- drv->generic_ie = NULL;
- drv->generic_ie_len = 0;
- if (elem) {
- drv->generic_ie = os_memdup(elem, elem_len);
- if (drv->generic_ie == NULL)
- return -1;
- drv->generic_ie_len = elem_len;
- }
-
- return hostapd_ioctl_set_generic_elem(drv);
-}
-
-
-static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp,
- const struct wpabuf *assocresp)
-{
- struct hostap_driver_data *drv = priv;
-
- /*
- * Host AP driver supports only one set of extra IEs, so we need to
- * use the Probe Response IEs also for Beacon frames since they include
- * more information.
- */
-
- os_free(drv->wps_ie);
- drv->wps_ie = NULL;
- drv->wps_ie_len = 0;
- if (proberesp) {
- drv->wps_ie = os_memdup(wpabuf_head(proberesp),
- wpabuf_len(proberesp));
- if (drv->wps_ie == NULL)
- return -1;
- drv->wps_ie_len = wpabuf_len(proberesp);
- }
-
- return hostapd_ioctl_set_generic_elem(drv);
-}
-
-
-static void
-hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv,
- char *custom)
-{
- wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
-
- if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
- char *pos;
- u8 addr[ETH_ALEN];
- pos = strstr(custom, "addr=");
- if (pos == NULL) {
- wpa_printf(MSG_DEBUG,
- "MLME-MICHAELMICFAILURE.indication "
- "without sender address ignored");
- return;
- }
- pos += 5;
- if (hwaddr_aton(pos, addr) == 0) {
- union wpa_event_data data;
- os_memset(&data, 0, sizeof(data));
- data.michael_mic_failure.unicast = 1;
- data.michael_mic_failure.src = addr;
- wpa_supplicant_event(drv->hapd,
- EVENT_MICHAEL_MIC_FAILURE, &data);
- } else {
- wpa_printf(MSG_DEBUG,
- "MLME-MICHAELMICFAILURE.indication "
- "with invalid MAC address");
- }
- }
-}
-
-
-static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
- char *data, unsigned int len)
-{
- struct iw_event iwe_buf, *iwe = &iwe_buf;
- char *pos, *end, *custom, *buf;
-
- pos = data;
- end = data + len;
-
- while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
- /* Event data may be unaligned, so make a local, aligned copy
- * before processing. */
- memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
- wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
- iwe->cmd, iwe->len);
- if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
- return;
-
- custom = pos + IW_EV_POINT_LEN;
- if (drv->we_version > 18 &&
- (iwe->cmd == IWEVMICHAELMICFAILURE ||
- iwe->cmd == IWEVCUSTOM)) {
- /* WE-19 removed the pointer from struct iw_point */
- char *dpos = (char *) &iwe_buf.u.data.length;
- int dlen = dpos - (char *) &iwe_buf;
- memcpy(dpos, pos + IW_EV_LCP_LEN,
- sizeof(struct iw_event) - dlen);
- } else {
- memcpy(&iwe_buf, pos, sizeof(struct iw_event));
- custom += IW_EV_POINT_OFF;
- }
-
- switch (iwe->cmd) {
- case IWEVCUSTOM:
- if (iwe->u.data.length > end - custom)
- return;
- buf = malloc(iwe->u.data.length + 1);
- if (buf == NULL)
- return;
- memcpy(buf, custom, iwe->u.data.length);
- buf[iwe->u.data.length] = '\0';
- hostapd_wireless_event_wireless_custom(drv, buf);
- free(buf);
- break;
- }
-
- pos += iwe->len;
- }
-}
-
-
-static void hostapd_wireless_event_rtm_newlink(void *ctx,
- struct ifinfomsg *ifi,
- u8 *buf, size_t len)
-{
- struct hostap_driver_data *drv = ctx;
- int attrlen, rta_len;
- struct rtattr *attr;
-
- /* TODO: use ifi->ifi_index to filter out wireless events from other
- * interfaces */
-
- attrlen = len;
- attr = (struct rtattr *) buf;
-
- rta_len = RTA_ALIGN(sizeof(struct rtattr));
- while (RTA_OK(attr, attrlen)) {
- if (attr->rta_type == IFLA_WIRELESS) {
- hostapd_wireless_event_wireless(
- drv, ((char *) attr) + rta_len,
- attr->rta_len - rta_len);
- }
- attr = RTA_NEXT(attr, attrlen);
- }
-}
-
-
-static int hostap_get_we_version(struct hostap_driver_data *drv)
-{
- struct iw_range *range;
- struct iwreq iwr;
- int minlen;
- size_t buflen;
-
- drv->we_version = 0;
-
- /*
- * Use larger buffer than struct iw_range in order to allow the
- * structure to grow in the future.
- */
- buflen = sizeof(struct iw_range) + 500;
- range = os_zalloc(buflen);
- if (range == NULL)
- return -1;
-
- memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.data.pointer = (caddr_t) range;
- iwr.u.data.length = buflen;
-
- minlen = ((char *) &range->enc_capa) - (char *) range +
- sizeof(range->enc_capa);
-
- if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
- strerror(errno));
- os_free(range);
- return -1;
- } else if (iwr.u.data.length >= minlen &&
- range->we_version_compiled >= 18) {
- wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
- "WE(source)=%d enc_capa=0x%x",
- range->we_version_compiled,
- range->we_version_source,
- range->enc_capa);
- drv->we_version = range->we_version_compiled;
- }
-
- free(range);
- return 0;
-}
-
-
-static int hostap_wireless_event_init(struct hostap_driver_data *drv)
-{
- struct netlink_config *cfg;
-
- hostap_get_we_version(drv);
-
- cfg = os_zalloc(sizeof(*cfg));
- if (cfg == NULL)
- return -1;
- cfg->ctx = drv;
- cfg->newlink_cb = hostapd_wireless_event_rtm_newlink;
- drv->netlink = netlink_init(cfg);
- if (drv->netlink == NULL) {
- os_free(cfg);
- return -1;
- }
-
- return 0;
-}
-
-
-static void * hostap_init(struct hostapd_data *hapd,
- struct wpa_init_params *params)
-{
- struct hostap_driver_data *drv;
-
- drv = os_zalloc(sizeof(struct hostap_driver_data));
- if (drv == NULL) {
- printf("Could not allocate memory for hostapd driver data\n");
- return NULL;
- }
-
- drv->hapd = hapd;
- drv->ioctl_sock = drv->sock = -1;
- memcpy(drv->iface, params->ifname, sizeof(drv->iface));
-
- drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->ioctl_sock < 0) {
- wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- os_free(drv);
- return NULL;
- }
-
- if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
- wpa_printf(MSG_ERROR,
- "Could not enable hostapd mode for interface %s",
- drv->iface);
- close(drv->ioctl_sock);
- os_free(drv);
- return NULL;
- }
-
- if (hostap_init_sockets(drv, params->own_addr) ||
- hostap_wireless_event_init(drv)) {
- close(drv->ioctl_sock);
- os_free(drv);
- return NULL;
- }
-
- return drv;
-}
-
-
-static void hostap_driver_deinit(void *priv)
-{
- struct hostap_driver_data *drv = priv;
-
- netlink_deinit(drv->netlink);
- (void) hostap_set_iface_flags(drv, 0);
- (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0);
- (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0);
-
- if (drv->ioctl_sock >= 0)
- close(drv->ioctl_sock);
-
- if (drv->sock >= 0)
- close(drv->sock);
-
- os_free(drv->generic_ie);
- os_free(drv->wps_ie);
-
- free(drv);
-}
-
-
-static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason, int link_id)
-{
- struct hostap_driver_data *drv = priv;
- struct ieee80211_mgmt mgmt;
-
- if (is_broadcast_ether_addr(addr)) {
- /*
- * New Prism2.5/3 STA firmware versions seem to have issues
- * with this broadcast deauth frame. This gets the firmware in
- * odd state where nothing works correctly, so let's skip
- * sending this for the hostap driver.
- */
- return 0;
- }
-
- memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DEAUTH);
- memcpy(mgmt.da, addr, ETH_ALEN);
- memcpy(mgmt.sa, own_addr, ETH_ALEN);
- memcpy(mgmt.bssid, own_addr, ETH_ALEN);
- mgmt.u.deauth.reason_code = host_to_le16(reason);
- return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth), 0, 0, NULL, 0, 0, 0, -1);
-}
-
-
-static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
- struct hostap_driver_data *drv = priv;
- struct iwreq iwr;
-
- os_memset(&iwr, 0, sizeof(iwr));
- os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
- iwr.u.freq.m = freq->channel;
- iwr.u.freq.e = 0;
-
- if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
-static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason)
-{
- struct hostap_driver_data *drv = priv;
- struct ieee80211_mgmt mgmt;
-
- memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DISASSOC);
- memcpy(mgmt.da, addr, ETH_ALEN);
- memcpy(mgmt.sa, own_addr, ETH_ALEN);
- memcpy(mgmt.bssid, own_addr, ETH_ALEN);
- mgmt.u.disassoc.reason_code = host_to_le16(reason);
- return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.disassoc), 0, 0, NULL, 0, 0, 0,
- -1);
-}
-
-
-static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
- u16 *num_modes,
- u16 *flags, u8 *dfs)
-{
- struct hostapd_hw_modes *mode;
- int i, clen, rlen;
- const short chan2freq[14] = {
- 2412, 2417, 2422, 2427, 2432, 2437, 2442,
- 2447, 2452, 2457, 2462, 2467, 2472, 2484
- };
-
- mode = os_zalloc(sizeof(struct hostapd_hw_modes));
- if (mode == NULL)
- return NULL;
-
- *num_modes = 1;
- *flags = 0;
- *dfs = 0;
-
- mode->mode = HOSTAPD_MODE_IEEE80211B;
- mode->num_channels = 14;
- mode->num_rates = 4;
-
- clen = mode->num_channels * sizeof(struct hostapd_channel_data);
- rlen = mode->num_rates * sizeof(int);
-
- mode->channels = os_zalloc(clen);
- mode->rates = os_zalloc(rlen);
- if (mode->channels == NULL || mode->rates == NULL) {
- os_free(mode->channels);
- os_free(mode->rates);
- os_free(mode);
- return NULL;
- }
-
- for (i = 0; i < 14; i++) {
- mode->channels[i].chan = i + 1;
- mode->channels[i].freq = chan2freq[i];
- mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20;
- /* TODO: Get allowed channel list from the driver */
- if (i >= 11)
- mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
- }
-
- mode->rates[0] = 10;
- mode->rates[1] = 20;
- mode->rates[2] = 55;
- mode->rates[3] = 110;
-
- return mode;
-}
-
-
-static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
- const u8 *addr, int qos)
-{
- struct ieee80211_hdr hdr;
-
- os_memset(&hdr, 0, sizeof(hdr));
-
- /*
- * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
- * but it is apparently not retried so TX Exc events
- * are not received for it.
- * This is the reason the driver overrides the default
- * handling.
- */
- hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
- WLAN_FC_STYPE_DATA);
-
- hdr.frame_control |=
- host_to_le16(WLAN_FC_FROMDS);
- os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
- os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
- os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
-
- hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0,
- -1);
-}
-
-
-const struct wpa_driver_ops wpa_driver_hostap_ops = {
- .name = "hostap",
- .desc = "Host AP driver (Intersil Prism2/2.5/3)",
- .set_key = wpa_driver_hostap_set_key,
- .hapd_init = hostap_init,
- .hapd_deinit = hostap_driver_deinit,
- .set_ieee8021x = hostap_set_ieee8021x,
- .set_privacy = hostap_set_privacy,
- .get_seqnum = hostap_get_seqnum,
- .flush = hostap_flush,
- .set_generic_elem = hostap_set_generic_elem,
- .read_sta_data = hostap_read_sta_data,
- .hapd_send_eapol = hostap_send_eapol,
- .sta_set_flags = hostap_sta_set_flags,
- .sta_deauth = hostap_sta_deauth,
- .sta_disassoc = hostap_sta_disassoc,
- .sta_remove = hostap_sta_remove,
- .hapd_set_ssid = hostap_set_ssid,
- .send_mlme = hostap_send_mlme,
- .sta_add = hostap_sta_add,
- .get_inact_sec = hostap_get_inact_sec,
- .sta_clear_stats = hostap_sta_clear_stats,
- .get_hw_feature_data = hostap_get_hw_feature_data,
- .set_ap_wps_ie = hostap_set_ap_wps_ie,
- .set_freq = hostap_set_freq,
- .poll_client = wpa_driver_hostap_poll_client,
-};
diff --git a/contrib/wpa/src/drivers/driver_hostap.h b/contrib/wpa/src/drivers/driver_hostap.h
deleted file mode 100644
index ac0b83ad24d1..000000000000
--- a/contrib/wpa/src/drivers/driver_hostap.h
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Driver interaction with Linux Host AP driver
- * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef HOSTAP_DRIVER_H
-#define HOSTAP_DRIVER_H
-
-/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
-
-/* New wireless extensions API - SET/GET convention (even ioctl numbers are
- * root only)
- */
-#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
-#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
-#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
-#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
-#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
-#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
-#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
-#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
-#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
-#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
-#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
-#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
-#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
-#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
-
-/* following are not in SIOCGIWPRIV list; check permission in the driver code
- */
-#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
-#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
-
-
-/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
-enum {
- /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
- PRISM2_PARAM_TXRATECTRL = 2,
- PRISM2_PARAM_BEACON_INT = 3,
- PRISM2_PARAM_PSEUDO_IBSS = 4,
- PRISM2_PARAM_ALC = 5,
- /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
- PRISM2_PARAM_DUMP = 7,
- PRISM2_PARAM_OTHER_AP_POLICY = 8,
- PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
- PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
- PRISM2_PARAM_DTIM_PERIOD = 11,
- PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
- PRISM2_PARAM_MAX_WDS = 13,
- PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
- PRISM2_PARAM_AP_AUTH_ALGS = 15,
- PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
- PRISM2_PARAM_HOST_ENCRYPT = 17,
- PRISM2_PARAM_HOST_DECRYPT = 18,
- PRISM2_PARAM_HOST_ROAMING = 21,
- PRISM2_PARAM_BCRX_STA_KEY = 22,
- PRISM2_PARAM_IEEE_802_1X = 23,
- PRISM2_PARAM_ANTSEL_TX = 24,
- PRISM2_PARAM_ANTSEL_RX = 25,
- PRISM2_PARAM_MONITOR_TYPE = 26,
- PRISM2_PARAM_WDS_TYPE = 27,
- PRISM2_PARAM_HOSTSCAN = 28,
- PRISM2_PARAM_AP_SCAN = 29,
- PRISM2_PARAM_ENH_SEC = 30,
- PRISM2_PARAM_IO_DEBUG = 31,
- PRISM2_PARAM_BASIC_RATES = 32,
- PRISM2_PARAM_OPER_RATES = 33,
- PRISM2_PARAM_HOSTAPD = 34,
- PRISM2_PARAM_HOSTAPD_STA = 35,
- PRISM2_PARAM_WPA = 36,
- PRISM2_PARAM_PRIVACY_INVOKED = 37,
- PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
- PRISM2_PARAM_DROP_UNENCRYPTED = 39,
- PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
-};
-
-enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
- HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
-
-
-/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
-enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
- AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
- AP_MAC_CMD_KICKALL = 4 };
-
-
-/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
-enum {
- PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
- /* Note! Old versions of prism2_srec have a fatal error in CRC-16
- * calculation, which will corrupt all non-volatile downloads.
- * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
- * prevent use of old versions of prism2_srec for non-volatile
- * download. */
- PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
- PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
- /* Persistent versions of volatile download commands (keep firmware
- * data in memory and automatically re-download after hw_reset */
- PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
- PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
-};
-
-struct prism2_download_param {
- u32 dl_cmd;
- u32 start_addr;
- u32 num_areas;
- struct prism2_download_area {
- u32 addr; /* wlan card address */
- u32 len;
- caddr_t ptr; /* pointer to data in user space */
- } data[0];
-};
-
-#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
-#define PRISM2_MAX_DOWNLOAD_LEN 262144
-
-
-/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
-enum {
- PRISM2_HOSTAPD_FLUSH = 1,
- PRISM2_HOSTAPD_ADD_STA = 2,
- PRISM2_HOSTAPD_REMOVE_STA = 3,
- PRISM2_HOSTAPD_GET_INFO_STA = 4,
- /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
- PRISM2_SET_ENCRYPTION = 6,
- PRISM2_GET_ENCRYPTION = 7,
- PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
- PRISM2_HOSTAPD_GET_RID = 9,
- PRISM2_HOSTAPD_SET_RID = 10,
- PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
- PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
- PRISM2_HOSTAPD_MLME = 13,
- PRISM2_HOSTAPD_SCAN_REQ = 14,
- PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
-};
-
-#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
-#define PRISM2_HOSTAPD_RID_HDR_LEN \
-((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
-#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
-((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
-
-/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
- */
-#define HOSTAP_CRYPT_ALG_NAME_LEN 16
-
-
-struct prism2_hostapd_param {
- u32 cmd;
- u8 sta_addr[ETH_ALEN];
- union {
- struct {
- u16 aid;
- u16 capability;
- u8 tx_supp_rates;
- } add_sta;
- struct {
- u32 inactive_sec;
- } get_info_sta;
- struct {
- u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
- u32 flags;
- u32 err;
- u8 idx;
- u8 seq[8]; /* sequence counter (set: RX, get: TX) */
- u16 key_len;
- u8 key[0];
- } crypt;
- struct {
- u32 flags_and;
- u32 flags_or;
- } set_flags_sta;
- struct {
- u16 rid;
- u16 len;
- u8 data[0];
- } rid;
- struct {
- u8 len;
- u8 data[0];
- } generic_elem;
- struct {
-#define MLME_STA_DEAUTH 0
-#define MLME_STA_DISASSOC 1
- u16 cmd;
- u16 reason_code;
- } mlme;
- struct {
- u8 ssid_len;
- u8 ssid[SSID_MAX_LEN];
- } scan_req;
- } u;
-};
-
-#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
-#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
-
-#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
-#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
-#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
-#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
-#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
-#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
-
-#endif /* HOSTAP_DRIVER_H */
diff --git a/contrib/wpa/src/drivers/driver_macsec_linux.c b/contrib/wpa/src/drivers/driver_macsec_linux.c
index c867154981e9..57bea3b42244 100644
--- a/contrib/wpa/src/drivers/driver_macsec_linux.c
+++ b/contrib/wpa/src/drivers/driver_macsec_linux.c
@@ -19,6 +19,7 @@
#include <netlink/route/link.h>
#include <netlink/route/link/macsec.h>
#include <linux/if_macsec.h>
+#include <linux/version.h>
#include <inttypes.h>
#include "utils/common.h"
@@ -32,7 +33,8 @@
#define UNUSED_SCI 0xffffffffffffffff
-#if LIBNL_VER_NUM >= LIBNL_VER(3, 6)
+#if (LIBNL_VER_NUM >= LIBNL_VER(3, 6) && \
+ LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))
#define LIBNL_HAS_OFFLOAD
#endif
@@ -1198,6 +1200,7 @@ static int macsec_drv_create_transmit_sc(
rtnl_link_put(link);
wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
err);
+ drv->created_link = false;
return err;
}
@@ -1221,6 +1224,7 @@ static int macsec_drv_create_transmit_sc(
drv->link = rtnl_link_macsec_alloc();
if (!drv->link) {
wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+ drv->created_link = false;
return -1;
}
@@ -1257,6 +1261,11 @@ static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
return 0;
}
+ if (!drv->link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "no link to delete");
+ return -1;
+ }
+
err = rtnl_link_delete(drv->sk, drv->link);
if (err < 0)
wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c
index 5d6db8fe9b2e..646b1f53c5b2 100644
--- a/contrib/wpa/src/drivers/driver_ndis.c
+++ b/contrib/wpa/src/drivers/driver_ndis.c
@@ -1037,6 +1037,9 @@ static int
wpa_driver_ndis_set_key_wrapper(void *priv,
struct wpa_driver_set_key_params *params)
{
+ if (params->key_flag & KEY_FLAG_NEXT)
+ return -1;
+
return wpa_driver_ndis_set_key(params->ifname, priv,
params->alg, params->addr,
params->key_idx, params->set_tx,
diff --git a/contrib/wpa/src/drivers/driver_nl80211.c b/contrib/wpa/src/drivers/driver_nl80211.c
index e6fbad96d57f..f4260bec7d8f 100644
--- a/contrib/wpa/src/drivers/driver_nl80211.c
+++ b/contrib/wpa/src/drivers/driver_nl80211.c
@@ -30,6 +30,9 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_common.h"
+#include "common/nan_defs.h"
+#include "common/nan_de.h"
+#include "common/proximity_ranging.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "netlink.h"
@@ -161,9 +164,10 @@ static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
struct hostapd_freq_params *freq);
static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
- const u8 *set_addr, int first,
- const char *driver_params);
+wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss,
+ const u8 *set_addr,
+ const char *driver_params,
+ enum wpa_p2p_mode p2p_mode);
static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
@@ -179,16 +183,13 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason);
static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason);
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
- int ifidx_reason);
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled);
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
- int reset_mode);
+static int nl80211_leave_ibss(struct i802_bss *bss, int reset_mode);
static int i802_set_iface_flags(struct i802_bss *bss, int up);
static int nl80211_set_param(void *priv, const char *param);
@@ -198,7 +199,10 @@ static int nl80211_put_mesh_config(struct nl_msg *msg,
struct wpa_driver_mesh_bss_params *params);
#endif /* CONFIG_MESH */
static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason);
+ u16 reason, int link_id);
+#ifdef CONFIG_PR
+static void nl80211_pd_stop(void *priv);
+#endif /* CONFIG_PR */
/* Converts nl80211_chan_width to a common format */
@@ -246,6 +250,13 @@ static int is_p2p_net_interface(enum nl80211_iftype nlmode)
}
+static bool nl80211_is_netdev_iftype(enum nl80211_iftype t)
+{
+ return t != NL80211_IFTYPE_P2P_DEVICE && t != NL80211_IFTYPE_NAN &&
+ t != NL80211_IFTYPE_PD;
+}
+
+
struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
int ifindex)
{
@@ -345,6 +356,7 @@ struct nl80211_ack_err_args {
int err;
struct nl_msg *orig_msg;
struct nl80211_err_info *err_info;
+ struct wpa_driver_nl80211_data *drv;
};
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
@@ -381,21 +393,34 @@ static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
if (tb[NLMSGERR_ATTR_MSG]) {
len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
nla_len(tb[NLMSGERR_ATTR_MSG]));
- wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
- len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+ if (err_args->drv)
+ wpa_msg(err_args->drv->ctx, MSG_ERROR, "nl80211: kernel reports: %*s",
+ len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+ else
+ wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+ len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
}
- if (!err_args->err_info)
- return NL_SKIP;
-
- /* Check if it was a per-link error report */
-
+ /* Done if the the problematic attribute was not specified */
if (!tb[NLMSGERR_ATTR_OFFS] ||
os_memcmp(orig_nlh, &err->msg, sizeof(err->msg)) != 0)
return NL_SKIP;
offset = nla_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+ if (offset < orig_nlh->nlmsg_len) {
+ struct nlattr *orig_attr =
+ (void *) ((unsigned char *) orig_nlh + offset);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: kernel reports error at offset %u, attribute type %d",
+ offset, nla_type(orig_attr));
+ }
+
+ /* Resolve problematic link if err_info is not NULL */
+ if (!err_args->err_info)
+ return NL_SKIP;
+
mlo_links = nlmsg_find_attr(orig_nlh, GENL_HDRLEN,
NL80211_ATTR_MLO_LINKS);
if (!mlo_links)
@@ -480,20 +505,146 @@ static int send_event_marker(struct wpa_driver_nl80211_data *drv)
out:
nlmsg_free(msg);
- if (err)
+ if (err < 0)
wpa_printf(MSG_INFO, "nl80211: %s failed: %s",
__func__, nl_geterror(err));
return res;
}
-int send_and_recv(struct nl80211_global *global,
- struct nl_sock *nl_handle, struct nl_msg *msg,
- int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data,
- int (*ack_handler_custom)(struct nl_msg *, void *),
- void *ack_data,
- struct nl80211_err_info *err_info)
+struct nl80211_pending_event {
+ struct dl_list list;
+
+ struct nl_msg *msg;
+ int (*handler)(struct nl_msg *, void *);
+ void *data;
+};
+
+
+static void nl80211_remove_pending_event(struct nl80211_pending_event *event)
+{
+ dl_list_del(&event->list);
+ nlmsg_free(event->msg);
+ os_free(event);
+}
+
+
+static void nl80211_deliver_pending_events(void *eloop_ctx, void *data)
+{
+ struct nl80211_global *global = eloop_ctx;
+ struct dl_list pending_events;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Delivering pending events");
+
+ if (dl_list_empty(&global->pending_events))
+ return;
+
+ global->delivering_pending_events = true;
+
+ dl_list_init(&pending_events);
+
+ global->pending_events.next->prev = &pending_events;
+ global->pending_events.prev->next = &pending_events;
+ pending_events.next = global->pending_events.next;
+ pending_events.prev = global->pending_events.prev;
+ dl_list_init(&global->pending_events);
+
+ while (!dl_list_empty(&pending_events)) {
+ struct nl80211_pending_event *event =
+ dl_list_first(&pending_events,
+ struct nl80211_pending_event, list);
+
+ event->handler(event->msg, event->data);
+ nl80211_remove_pending_event(event);
+ }
+
+ global->delivering_pending_events = false;
+}
+
+
+/* Unfortunately, libnl does not permit us to call an existing handler. If we
+ * could either fetch the information or if nl_cb_call was exposed, this would
+ * be much more elegant. Unfortunately it is not, so we need a hook in every
+ * event handler that might need to be overriden.
+ */
+int nl80211_reply_hook(struct nl80211_global *global, struct nl_msg *msg,
+ int (*delayed_handler)(struct nl_msg *, void *),
+ void *delayed_data)
+{
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ struct nl80211_pending_event *event;
+
+ if (!global->sync_reply_handling) {
+ /* We cannot rely on eloop handling the timeout before trying
+ * to read the socket again (it should usually, but it may not
+ * if multiple timeouts have expired). As such, check here if
+ * there are already pending events, and if there are, queue
+ * this one to preserve delivery order.
+ *
+ * However, if we are already inside
+ * nl80211_deliver_pending_events(), do not re-queue events
+ * triggered by event handlers. Re-queuing during delivery
+ * causes events to accumulate across command cycles. When the
+ * accumulated event count is large enough, a re-queued event's
+ * sequence number eventually matches the reply_seq of a
+ * concurrent send_and_recv_glb() call, causing the reply
+ * handler to fire with the wrong message and corrupt the
+ * caller's stack.
+ */
+ if (!dl_list_empty(&global->pending_events) &&
+ !global->delivering_pending_events)
+ goto queue_event;
+
+ return NL_OK;
+ }
+
+ /* This may be a reply if the port ID is nonzero (broadcast). In that
+ * case the sequence number should also match. We will treat everything
+ * else as an event and queue it for later processing.
+ */
+ if (hdr->nlmsg_pid && hdr->nlmsg_seq == global->reply_seq) {
+ if (global->reply_handler)
+ global->reply_handler(msg, global->reply_data);
+
+ return NL_SKIP;
+ }
+
+ /* Schedule a timeout to deliver the events from the eventloop. */
+ if (dl_list_empty(&global->pending_events)) {
+ if (eloop_register_timeout(0, 0, nl80211_deliver_pending_events,
+ global, NULL) < 0)
+ wpa_printf(MSG_ERROR, "%s: failed to register timeout",
+ __func__);
+ }
+
+queue_event:
+ TEST_FAIL_TAG("queued");
+
+ event = os_zalloc(sizeof(*event));
+ if (!event) {
+ wpa_printf(MSG_ERROR,
+ "%s: failed to allocate delayed event context",
+ __func__);
+ return NL_SKIP;
+ }
+
+ event->handler = delayed_handler;
+ event->data = delayed_data;
+ nlmsg_get(msg);
+ event->msg = msg;
+ dl_list_add_tail(&global->pending_events, &event->list);
+
+ return NL_SKIP;
+}
+
+int send_and_recv_glb(struct nl80211_global *global,
+ struct wpa_driver_nl80211_data *drv, /* may be NULL */
+ struct nl_sock *nl_handle, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data,
+ struct nl80211_err_info *err_info)
{
struct nl_cb *cb, *s_nl_cb;
struct nl80211_ack_err_args err;
@@ -512,13 +663,22 @@ int send_and_recv(struct nl80211_global *global,
/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
opt = 1;
- setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
- NETLINK_EXT_ACK, &opt, sizeof(opt));
+ if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_EXT_ACK, &opt, sizeof(opt)) < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: setsockopt(NETLINK_EXT_ACK) failed: %s (ignored)",
+ strerror(errno));
/* try to set NETLINK_CAP_ACK to 1, ignoring errors */
opt = 1;
- setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
- NETLINK_CAP_ACK, &opt, sizeof(opt));
+ if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_CAP_ACK, &opt, sizeof(opt)) < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: setsockopt(NETLINK_CAP_ACK) failed: %s (ignored)",
+ strerror(errno));
+
+ if (TEST_FAIL_TAG("pre-send-sleep"))
+ os_sleep(1, 0);
err.err = nl_send_auto_complete(nl_handle, msg);
if (err.err < 0) {
@@ -535,6 +695,7 @@ int send_and_recv(struct nl80211_global *global,
err.err = 1;
err.orig_msg = msg;
err.err_info = err_info;
+ err.drv = drv;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
@@ -545,12 +706,13 @@ int send_and_recv(struct nl80211_global *global,
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
ack_handler_custom, ack_data);
} else {
- nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err.err);
}
- if (valid_handler)
- nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
- valid_handler, valid_data);
+ global->sync_reply_handling = true;
+ global->reply_seq = nlmsg_hdr(msg)->nlmsg_seq;
+ global->reply_handler = valid_handler;
+ global->reply_data = valid_data;
while (err.err > 0) {
int res = nl_recvmsgs(nl_handle, cb);
@@ -574,6 +736,12 @@ int send_and_recv(struct nl80211_global *global,
__func__, res, nl_geterror(res));
}
}
+
+ global->sync_reply_handling = false;
+ global->reply_seq = 0;
+ global->reply_handler = NULL;
+ global->reply_data = NULL;
+
out:
nl_cb_put(cb);
/* Always clear the message as it can potentially contain keys */
@@ -650,8 +818,8 @@ static int nl_get_multicast_id(struct nl80211_global *global,
return -1;
}
- ret = send_and_recv(global, global->nl, msg, family_handler, &res,
- NULL, NULL, NULL);
+ ret = send_and_recv_glb(global, NULL, global->nl, msg, family_handler,
+ &res, NULL, NULL, NULL);
if (ret == 0)
ret = res.id;
return ret;
@@ -739,6 +907,8 @@ struct wiphy_idx_data {
enum nl80211_iftype nlmode;
u8 *macaddr;
u8 use_4addr;
+ u64 wdev_id;
+ bool wdev_id_set;
};
@@ -764,6 +934,11 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg)
if (tb[NL80211_ATTR_4ADDR])
info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]);
+ if (tb[NL80211_ATTR_WDEV]) {
+ info->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+ info->wdev_id_set = true;
+ }
+
return NL_SKIP;
}
@@ -846,7 +1021,7 @@ static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
return -1;
}
- ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+ ret = send_and_recv(drv, w->nl_beacons, msg, NULL, NULL,
NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
@@ -947,12 +1122,14 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
"wiphy beacons");
if (w->nl_beacons == NULL) {
+ nl_cb_put(w->nl_cb);
os_free(w);
return NULL;
}
if (nl80211_register_beacons(bss->drv, w)) {
nl_destroy_handles(&w->nl_beacons);
+ nl_cb_put(w->nl_cb);
os_free(w);
return NULL;
}
@@ -1103,7 +1280,7 @@ static int nl80211_get_sta_mlo_info(void *priv,
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
struct nl_msg *msg;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
if (send_and_recv_resp(drv, msg, get_mlo_info,
&drv->sta_mlo_info))
return -1;
@@ -1114,23 +1291,37 @@ static int nl80211_get_sta_mlo_info(void *priv,
}
+int get_sta_mlo_interface_info(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
+ if (!msg ||
+ send_and_recv_resp(drv, msg, get_mlo_info, &drv->sta_mlo_info))
+ return -1;
+
+ return 0;
+}
+
+
static void wpa_driver_nl80211_event_newlink(
- struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+ struct nl80211_global *global, struct i802_bss *bss,
int ifindex, const char *ifname)
{
union wpa_event_data event;
- if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
- if (if_nametoindex(drv->first_bss->ifname) == 0) {
+ if (bss && os_strcmp(bss->ifname, ifname) == 0) {
+ if (if_nametoindex(bss->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
- drv->first_bss->ifname);
+ bss->ifname);
return;
}
- if (!drv->if_removed)
+ if (!bss->if_removed)
return;
wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
- drv->first_bss->ifname);
- drv->if_removed = 0;
+ bss->ifname);
+ bss->if_removed = 0;
}
os_memset(&event, 0, sizeof(event));
@@ -1138,8 +1329,8 @@ static void wpa_driver_nl80211_event_newlink(
os_strlcpy(event.interface_status.ifname, ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
- if (drv)
- wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+ if (bss)
+ wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_STATUS, &event);
else
wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
&event);
@@ -1147,20 +1338,20 @@ static void wpa_driver_nl80211_event_newlink(
static void wpa_driver_nl80211_event_dellink(
- struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+ struct nl80211_global *global, struct i802_bss *bss,
int ifindex, const char *ifname)
{
union wpa_event_data event;
- if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
- if (drv->if_removed) {
+ if (bss && os_strcmp(bss->ifname, ifname) == 0) {
+ if (bss->if_removed) {
wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
ifname);
return;
}
wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
ifname);
- drv->if_removed = 1;
+ bss->if_removed = 1;
} else {
wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
ifname);
@@ -1171,15 +1362,15 @@ static void wpa_driver_nl80211_event_dellink(
os_strlcpy(event.interface_status.ifname, ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
- if (drv)
- wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+ if (bss)
+ wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_STATUS, &event);
else
wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
&event);
}
-static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+static int wpa_driver_nl80211_own_ifname(const char *ifname,
u8 *buf, size_t len)
{
int attrlen, rta_len;
@@ -1191,8 +1382,7 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
- if (os_strcmp(((char *) attr) + rta_len,
- drv->first_bss->ifname) == 0)
+ if (os_strcmp(((char *) attr) + rta_len, ifname) == 0)
return 1;
else
break;
@@ -1204,17 +1394,24 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
}
-static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+static int wpa_driver_nl80211_own_ifindex(struct i802_bss *bss,
int ifindex, u8 *buf, size_t len)
{
- if (drv->ifindex == ifindex)
+ if (bss->ifindex == ifindex)
return 1;
- if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
- nl80211_check_global(drv->global);
+ if (bss->if_removed &&
+ wpa_driver_nl80211_own_ifname(bss->ifname, buf, len)) {
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->first_bss == bss)
+ nl80211_check_global(drv->global);
+
wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
"interface");
- if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+ del_ifidx(drv, bss->ifindex, IFIDX_ANY);
+ if (wpa_driver_nl80211_finish_drv_init(bss, NULL, NULL,
+ WPA_P2P_MODE_WFD_R1) < 0)
return -1;
return 1;
}
@@ -1223,8 +1420,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
}
-static struct wpa_driver_nl80211_data *
-nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+static struct i802_bss *
+nl80211_find_bss(struct nl80211_global *global, int idx, u8 *buf, size_t len,
int *init_failed)
{
struct wpa_driver_nl80211_data *drv;
@@ -1234,16 +1431,21 @@ nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
*init_failed = 0;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
- res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
- if (res < 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Found matching own interface, but failed to complete reinitialization");
- if (init_failed)
- *init_failed = 1;
- return drv;
+ struct i802_bss *bss;
+
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ res = wpa_driver_nl80211_own_ifindex(bss, idx, buf,
+ len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Found matching own interface, but failed to complete reinitialization");
+ if (init_failed)
+ *init_failed = 1;
+ return bss;
+ }
+ if (res > 0)
+ return bss;
}
- if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
- return drv;
}
return NULL;
}
@@ -1282,7 +1484,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
u8 *buf, size_t len)
{
struct nl80211_global *global = ctx;
- struct wpa_driver_nl80211_data *drv;
+ struct wpa_driver_nl80211_data *drv = NULL;
+ struct i802_bss *bss = NULL;
int attrlen;
struct rtattr *attr;
u32 brid = 0;
@@ -1334,36 +1537,34 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
- drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
- if (!drv)
+ bss = nl80211_find_bss(global, ifi->ifi_index, buf, len, &init_failed);
+ if (!bss)
goto event_newlink;
if (init_failed)
return; /* do not update interface state */
- if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ drv = bss->drv;
+
+ if (!bss->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
- drv->ignore_if_down_event = 0;
+ bss->ignore_if_down_event = 0;
/* Re-read MAC address as it may have changed */
nl80211_refresh_mac(drv, ifi->ifi_index, 1);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
namebuf, ifname);
- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface down",
- drv->first_bss->ifname);
- } else if (drv->ignore_if_down_event) {
+ if (bss->ignore_if_down_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event generated by mode change");
- drv->ignore_if_down_event = 0;
+ bss->ignore_if_down_event = 0;
} else {
- drv->if_disabled = 1;
- wpa_supplicant_event(drv->ctx,
+ bss->if_disabled = 1;
+ wpa_supplicant_event(bss->ctx,
EVENT_INTERFACE_DISABLED, NULL);
/*
@@ -1371,14 +1572,17 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
* part of the EVENT_INTERFACE_DISABLED handling for
* dynamic interfaces
*/
- drv = nl80211_find_drv(global, ifi->ifi_index,
- buf, len, NULL);
- if (!drv)
+ drv = NULL;
+ bss = nl80211_find_bss(global, ifi->ifi_index, buf, len,
+ &init_failed);
+ if (!bss)
return;
+
+ drv = bss->drv;
}
}
- if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ if (bss->if_disabled && (ifi->ifi_flags & IFF_UP)) {
namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
@@ -1389,24 +1593,20 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
}
wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
namebuf, ifname);
- if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Not the main interface (%s) - do not indicate interface up",
- drv->first_bss->ifname);
- } else if (if_nametoindex(drv->first_bss->ifname) == 0) {
+ if (if_nametoindex(bss->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s does not exist",
- drv->first_bss->ifname);
- } else if (drv->if_removed) {
+ bss->ifname);
+ } else if (bss->if_removed) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is marked "
- "removed", drv->first_bss->ifname);
+ "removed", bss->ifname);
} else {
/* Re-read MAC address as it may have changed */
nl80211_refresh_mac(drv, ifi->ifi_index, 0);
- drv->if_disabled = 0;
- wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ bss->if_disabled = 0;
+ wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_ENABLED,
NULL);
}
}
@@ -1417,22 +1617,20 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
* fails. This will hit us when wpa_supplicant does not need to do
* IEEE 802.1X authentication
*/
- if (drv->operstate == 1 &&
+ if (bss->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING)) {
wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
- netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+ netlink_send_oper_ifla(drv->global->netlink, bss->ifindex,
-1, IF_OPER_UP);
}
event_newlink:
if (ifname[0])
- wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
+ wpa_driver_nl80211_event_newlink(global, bss, ifi->ifi_index,
ifname);
- if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
- struct i802_bss *bss;
-
+ if (ifi->ifi_family == AF_BRIDGE && brid && drv && bss) {
/* device has been added to bridge */
if (!if_indextoname(brid, namebuf)) {
wpa_printf(MSG_DEBUG,
@@ -1444,12 +1642,8 @@ event_newlink:
brid, namebuf);
add_ifidx(drv, brid, ifi->ifi_index);
- for (bss = drv->first_bss; bss; bss = bss->next) {
- if (os_strcmp(ifname, bss->ifname) == 0) {
- os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
- break;
- }
- }
+ if (os_strcmp(ifname, bss->ifname) == 0)
+ os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
}
}
@@ -1459,7 +1653,8 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
u8 *buf, size_t len)
{
struct nl80211_global *global = ctx;
- struct wpa_driver_nl80211_data *drv;
+ struct wpa_driver_nl80211_data *drv = NULL;
+ struct i802_bss *bss = NULL;
int attrlen;
struct rtattr *attr;
u32 brid = 0;
@@ -1506,7 +1701,9 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
- drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
+ bss = nl80211_find_bss(global, ifi->ifi_index, buf, len, NULL);
+ if (bss)
+ drv = bss->drv;
if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
/* device has been removed from bridge */
@@ -1525,7 +1722,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
}
if (ifi->ifi_family != AF_BRIDGE || !brid)
- wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
+ wpa_driver_nl80211_event_dellink(global, bss, ifi->ifi_index,
ifname);
}
@@ -1717,6 +1914,54 @@ try_again:
}
+const u8 * nl80211_get_assoc_bssid(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_get_assoc_freq_arg arg;
+ int count = 0;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+try_again:
+ msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ if (!msg)
+ return NULL;
+ os_memset(&arg, 0, sizeof(arg));
+ arg.drv = drv;
+ ret = send_and_recv_resp(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg);
+ if (ret == -EAGAIN) {
+ count++;
+ if (count >= 10) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid - try again");
+ goto try_again;
+ }
+ }
+ if (ret == 0) {
+ os_memcpy(drv->bssid, arg.assoc_bssid, ETH_ALEN);
+
+ if (drv->sta_mlo_info.valid_links) {
+ int i;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+ os_memcpy(drv->sta_mlo_info.links[i].bssid,
+ arg.bssid[i], ETH_ALEN);
+ }
+
+ return drv->bssid;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+ ret, strerror(-ret));
+
+ return NULL;
+}
+
+
static int get_link_noise(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1761,15 +2006,16 @@ static int get_link_noise(struct nl_msg *msg, void *arg)
}
-int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+int nl80211_get_link_noise(struct i802_bss *bss,
struct wpa_signal_info *sig_change)
{
struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
sig_change->current_noise = WPA_INVALID_NOISE;
sig_change->frequency = drv->assoc_freq;
- msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
return send_and_recv_resp(drv, msg, get_link_noise, sig_change);
}
@@ -1833,7 +2079,7 @@ static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci)
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
return send_and_recv_resp(drv, msg, get_channel_info, ci);
}
@@ -1972,6 +2218,26 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
if (global->nl_event == NULL)
goto err;
+ /* Needs to be registered early so that process_global_event() calls
+ * the sync reply handler hook.
+ */
+ nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_global_event, global);
+
+#ifdef CONFIG_NAN
+ ret = nl_get_multicast_id(global, "nl80211", "config");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(global->nl_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Could not add multicast membership for config events: %d (%s)",
+ ret, nl_geterror(ret));
+ goto err;
+ }
+#endif /* CONFIG_NAN */
+
ret = nl_get_multicast_id(global, "nl80211", "scan");
if (ret >= 0)
ret = nl_socket_add_membership(global->nl_event, ret);
@@ -2035,11 +2301,6 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
genl_family_put(family);
nl_cache_free(cache);
- nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
- no_seq_check, NULL);
- nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
- process_global_event, global);
-
nl80211_register_eloop_read(&global->nl_event,
wpa_driver_nl80211_event_receive,
global->nl_cb, 0);
@@ -2091,10 +2352,10 @@ static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
/*
- * rtnetlink ifdown handler will report interfaces other than the P2P
- * Device interface as disabled.
+ * rtnetlink ifdown handler will report interfaces other than the
+ * P2P/NAN Device interfaces as disabled.
*/
- if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ if (!nl80211_is_netdev_iftype(drv->nlmode))
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
}
@@ -2113,10 +2374,10 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
nl80211_disable_11b_rates(drv, drv->ifindex, 1);
/*
- * rtnetlink ifup handler will report interfaces other than the P2P
- * Device interface as enabled.
+ * rtnetlink ifup handler will report interfaces other than the P2P/NAN
+ * Device interfaces as enabled.
*/
- if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ if (!nl80211_is_netdev_iftype(drv->nlmode))
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
}
@@ -2218,11 +2479,21 @@ static int nl80211_init_bss(struct i802_bss *bss)
static void nl80211_destroy_bss(struct i802_bss *bss)
{
+ struct nl80211_pending_event *event, *next;
+
nl_cb_put(bss->nl_cb);
bss->nl_cb = NULL;
if (bss->nl_connect)
nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+
+ dl_list_for_each_safe(event, next, &bss->drv->global->pending_events,
+ struct nl80211_pending_event, list) {
+ if (event->data != bss)
+ continue;
+
+ nl80211_remove_pending_event(event);
+ }
}
@@ -2240,25 +2511,27 @@ wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
rcfg->ctx = drv;
- /* rfkill uses netdev sysfs for initialization. However, P2P Device is
- * not associated with a netdev, so use the name of some other interface
- * sharing the same wiphy as the P2P Device interface.
+ /* rfkill uses netdev sysfs for initialization. However, P2P/NAN Device
+ * is not associated with a netdev, so use the name of some other
+ * interface sharing the same wiphy as the P2P/NAN Device interface.
*
- * Note: This is valid, as a P2P Device interface is always dynamically
- * created and is created only once another wpa_s interface was added.
+ * Note: This is valid, as a P2P/NAN Device interface is always
+ * dynamically created and is created only once another wpa_s interface
+ * was added.
*/
- if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ if (!nl80211_is_netdev_iftype(drv->nlmode)) {
struct nl80211_global *global = drv->global;
struct wpa_driver_nl80211_data *tmp1;
dl_list_for_each(tmp1, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
- !tmp1->rfkill)
+ !tmp1->rfkill ||
+ !nl80211_is_netdev_iftype(tmp1->nlmode))
continue;
wpa_printf(MSG_DEBUG,
- "nl80211: Use (%s) to initialize P2P Device rfkill",
+ "nl80211: Use (%s) to initialize P2P/NAN Device rfkill",
tmp1->first_bss->ifname);
os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
sizeof(rcfg->ifname));
@@ -2282,8 +2555,10 @@ wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
void *global_priv, int hostapd,
const u8 *set_addr,
- const char *driver_params)
+ const char *driver_params,
+ enum wpa_p2p_mode p2p_mode)
{
+ static unsigned int next_unique_drv_id = 0;
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
char path[128], buf[200], *pos;
@@ -2316,6 +2591,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
drv->ctx = ctx;
drv->hostapd = !!hostapd;
drv->eapol_sock = -1;
+ drv->unique_drv_id = next_unique_drv_id++;
/*
* There is no driver capability flag for this, so assume it is
@@ -2337,15 +2613,14 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
bss->ctx = ctx;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
- drv->monitor_ifidx = -1;
- drv->monitor_sock = -1;
drv->eapol_tx_sock = -1;
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
if (nl80211_init_bss(bss))
goto failed;
- if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+ if (wpa_driver_nl80211_finish_drv_init(bss, set_addr, driver_params,
+ p2p_mode))
goto failed;
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
@@ -2366,9 +2641,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
"nl80211: wifi status sockopt failed: %s",
strerror(errno));
drv->data_tx_status = 0;
- if (!drv->use_monitor)
- drv->capa.flags &=
- ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
} else {
eloop_register_read_sock(
drv->eapol_tx_sock,
@@ -2388,6 +2661,7 @@ skip_wifi_status:
* Use link ID 0 for the single "link" of a non-MLD.
*/
bss->valid_links = 0;
+ bss->active_links = 0;
bss->flink = &bss->links[0];
os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
@@ -2408,10 +2682,11 @@ failed:
* Returns: Pointer to private data, %NULL on failure
*/
static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
- void *global_priv)
+ void *global_priv,
+ enum wpa_p2p_mode p2p_mode)
{
return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
- NULL);
+ NULL, p2p_mode);
}
@@ -2439,7 +2714,7 @@ static int nl80211_register_frame(struct i802_bss *bss,
return -1;
}
- ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+ ret = send_and_recv(drv, nl_handle, msg, NULL, NULL,
NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
@@ -2493,6 +2768,92 @@ static int nl80211_register_action_frame(struct i802_bss *bss,
}
+#define NAN_SDF_ACTION ((const u8 *) "\x04\x09\x50\x6f\x9a\x13")
+#define NAN_PROTECTED_SDF_ACTION ((const u8 *) "\x09\x09\x50\x6f\x9a\x13")
+#define NAN_NAF_ACTION ((const u8 *) "\x04\x09\x50\x6f\x9a\x18")
+#define NAN_PROTECTED_NAF_ACTION ((const u8 *) "\x09\x09\x50\x6f\x9a\x18")
+
+static int nl80211_mgmt_subscribe_nan(struct i802_bss *bss,
+ enum nl80211_iftype nlmode)
+{
+#ifdef CONFIG_NAN
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!(drv->capa.nan_capa.drv_flags &
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: User space DE is not supported, don't subscribe to NAN public action frames");
+ return 0;
+ }
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Subscribe to mgmt frames for NAN with handle %p: nlmode=%u",
+ bss->nl_mgmt, nlmode);
+
+ /* NAN SDF Public Action */
+ if (nlmode == NL80211_IFTYPE_NAN &&
+ nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, true) < 0) {
+ /* fallback to non-multicast */
+ if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6,
+ false) < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to subscribe to NAN public action frames");
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+ }
+ }
+
+ /* NAF Public Action */
+ if (nl80211_register_action_frame2(bss, NAN_NAF_ACTION, 6, true)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to subscribe to NAFs");
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+ }
+
+#ifdef CONFIG_PASN
+ /* NAN Protected SDF Public Action */
+ if (nlmode == NL80211_IFTYPE_NAN &&
+ nl80211_register_action_frame2(bss, NAN_PROTECTED_SDF_ACTION, 6,
+ true)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to subscribe to NAN protected SDFs");
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+ }
+
+ /* Register for PASN Authentication frames */
+ if (nlmode == NL80211_IFTYPE_NAN &&
+ nl80211_register_frame(bss, bss->nl_mgmt,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_AUTH << 4),
+ (u8 *) "\x07\x00", 2, false)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to subscribe to NAN PASN Authentication frames");
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+ }
+#endif /* CONFIG_PASN */
+
+ /* NAF in Protected Dual of Public Action frame */
+ if (nl80211_register_action_frame2(bss, NAN_PROTECTED_NAF_ACTION, 6,
+ true)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to subscribe to protected NAFs");
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+ }
+
+ nl80211_mgmt_handle_register_eloop(bss);
+
+#endif /* CONFIG_NAN */
+
+ return 0;
+}
+
+
static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -2514,6 +2875,24 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
(u8 *) "\x03\x00", 2, false);
}
+#ifdef CONFIG_ENC_ASSOC
+ if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_EPPKE) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ /* register for EPPKE Authentication frames */
+ nl80211_register_frame(bss, bss->nl_mgmt, type,
+ (const u8 *) "\x09\x00", 2, false);
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
+#ifdef CONFIG_IEEE8021X_AUTH
+ if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_802_1X_AUTH) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ /* register for IEEE 802.1X Authentication frames */
+ nl80211_register_frame(bss, bss->nl_mgmt, type,
+ (const u8 *) "\x08\x00", 2, false);
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
#ifdef CONFIG_PASN
/* register for PASN Authentication frames */
if (nl80211_register_frame(bss, bss->nl_mgmt, type,
@@ -2565,15 +2944,13 @@ 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_frame2(bss, NAN_PUB_ACTION, 6, true) < 0) {
+ if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, true) < 0) {
/* fallback to non-multicast */
- if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6,
+ if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6,
false) < 0)
ret = -1;
}
-#undef NAN_PUB_ACTION
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_DPP
/* DPP Public Action */
@@ -2719,7 +3096,7 @@ static int nl80211_register_spurious_class3(struct i802_bss *bss)
int ret;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
- ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+ ret = send_and_recv(bss->drv, bss->nl_mgmt, msg, NULL, NULL,
NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
@@ -2766,6 +3143,14 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
ret = -1;
#endif /* CONFIG_FST */
+#ifdef CONFIG_IEEE80211BE
+ /* Protected EHT - Link Reconfiguration Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x25\x0b", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_IEEE80211BE */
+ /* Vendor-specific Protected */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x7e", 1) < 0)
+ ret = -1;
/* Vendor-specific */
if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
ret = -1;
@@ -2868,7 +3253,7 @@ static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
}
-static void nl80211_del_p2pdev(struct i802_bss *bss)
+static void nl80211_del_non_netdev(struct i802_bss *bss)
{
struct nl_msg *msg;
int ret;
@@ -2876,9 +3261,13 @@ static void nl80211_del_p2pdev(struct i802_bss *bss)
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
ret = send_and_recv_cmd(bss->drv, msg);
- wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
+ wpa_printf(MSG_DEBUG, "nl80211: Delete P2P/NAN Device %s (0x%llx): %s",
bss->ifname, (long long unsigned int) bss->wdev_id,
strerror(-ret));
+#ifdef CONFIG_NAN
+ if (bss->drv->nlmode == NL80211_IFTYPE_NAN && bss->drv->global->nl_nan)
+ nl80211_destroy_eloop_handle(&bss->drv->global->nl_nan, 0);
+#endif /* CONFIG_NAN */
}
@@ -2899,18 +3288,87 @@ static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
}
+static int nl80211_set_pr_dev(struct i802_bss *bss, bool start)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_PD :
+ NL80211_CMD_STOP_PD);
+ ret = send_and_recv_cmd(bss->drv, msg);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s PD Device %s (0x%llx): %s",
+ start ? "Start" : "Stop",
+ bss->ifname, (unsigned long long) bss->wdev_id,
+ strerror(-ret));
+ return ret;
+}
+
+
+#ifdef CONFIG_NAN
+static void nl80211_nan_stop(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started)
+ return;
+
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_STOP_NAN);
+ if (!msg) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to alloc NAN stop command");
+ return;
+ }
+
+ if (send_and_recv_resp(drv, msg, NULL, NULL))
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN stop command");
+
+ drv->nan_started = 0;
+}
+#endif /* CONFIG_NAN */
+
+
+static int nl80211_set_nandev(struct i802_bss *bss, int start)
+{
+#ifdef CONFIG_NAN
+ /*
+ * We don't implicitly start NAN.
+ * NAN is started through a dedicated API, however we do need to
+ * stop it.
+ * For rfkill, we rely on ENABLED/DISABLED events.
+ */
+ if (start)
+ return 0;
+
+ nl80211_nan_stop(bss);
+
+#endif /* CONFIG_NAN */
+ return 0;
+}
+
+
static int i802_set_iface_flags(struct i802_bss *bss, int up)
{
enum nl80211_iftype nlmode;
nlmode = nl80211_get_ifmode(bss);
- if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
- return linux_set_iface_flags(bss->drv->global->ioctl_sock,
- bss->ifname, up);
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ /* P2P Device has start/stop which is equivalent */
+ return nl80211_set_p2pdev(bss, up);
}
- /* P2P Device has start/stop which is equivalent */
- return nl80211_set_p2pdev(bss, up);
+ if (nlmode == NL80211_IFTYPE_NAN)
+ return nl80211_set_nandev(bss, up);
+
+ if (nlmode == NL80211_IFTYPE_PD) {
+ /* PR Device has start/stop which is equivalent */
+ return nl80211_set_pr_dev(bss, up);
+ }
+
+ return linux_set_iface_flags(bss->drv->global->ioctl_sock,
+ bss->ifname, up);
}
@@ -2968,33 +3426,113 @@ static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_set_p2p_mode(void *priv, enum wpa_p2p_mode mode)
+{
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+ enum qca_wlan_vendor_p2p_mode drv_mode;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set P2P mode: %d", mode);
+
+ switch (mode) {
+ case WPA_P2P_MODE_WFD_R1:
+ drv_mode = QCA_P2P_MODE_WFD_R1;
+ break;
+ case WPA_P2P_MODE_WFD_R2:
+ drv_mode = QCA_P2P_MODE_WFD_R2;
+ break;
+ case WPA_P2P_MODE_WFD_PCC:
+ drv_mode = QCA_P2P_MODE_WFD_PCC;
+ break;
+ default:
+ return -1;
+ }
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG, drv_mode))
+ goto fail;
+
+ nla_nest_end(msg, container);
+
+ ret = send_and_recv_cmd(bss->drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set P2P Mode: ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
- const u8 *set_addr, int first,
- const char *driver_params)
+wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss, const u8 *set_addr,
+ const char *driver_params,
+ enum wpa_p2p_mode p2p_mode)
{
- struct i802_bss *bss = drv->first_bss;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
int send_rfkill_event = 0;
enum nl80211_iftype nlmode;
+ bool first = drv->first_bss == bss;
- drv->ifindex = if_nametoindex(bss->ifname);
- bss->ifindex = drv->ifindex;
+ bss->ifindex = if_nametoindex(bss->ifname);
+ if (first)
+ drv->ifindex = bss->ifindex;
bss->wdev_id = drv->global->if_add_wdevid;
bss->wdev_id_set = drv->global->if_add_wdevid_set;
+ /*
+ * wdev_id is set for wdev-only interfaces during creation. For netdev
+ * interfaces it is not set, so fetch it via GET_INTERFACE to allow
+ * event routing by exact wdev_id match.
+ */
+ if (!bss->wdev_id_set) {
+ struct nl_msg *msg;
+ struct wiphy_idx_data info;
+
+ os_memset(&info, 0, sizeof(info));
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
+ if (msg &&
+ send_and_recv_resp(drv, msg, netdev_info_handler,
+ &info) == 0 &&
+ info.wdev_id_set) {
+ bss->wdev_id = info.wdev_id;
+ bss->wdev_id_set = 1;
+ wpa_printf(MSG_DEBUG, "nl80211: %s wdev_id=0x%llx",
+ bss->ifname,
+ (unsigned long long) bss->wdev_id);
+ }
+ }
+
bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
- drv->global->if_add_wdevid_set = 0;
+ if (first)
+ drv->global->if_add_wdevid_set = 0;
if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
bss->static_ap = 1;
- if (first &&
- nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+ if (first && nl80211_is_netdev_iftype(nl80211_get_ifmode(bss)) &&
linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
- drv->start_iface_up = 1;
+ bss->start_iface_up = 1;
- if (wpa_driver_nl80211_capa(drv))
+ if (first && wpa_driver_nl80211_capa(drv))
return -1;
if (driver_params && nl80211_set_param(bss, driver_params) < 0)
@@ -3020,16 +3558,21 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
else
nlmode = NL80211_IFTYPE_STATION;
- if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
+ if (first && wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
return -1;
}
- if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ if (!nl80211_is_netdev_iftype(nl80211_get_ifmode(bss)))
nl80211_get_macaddr(bss);
wpa_driver_nl80211_drv_init_rfkill(drv);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (nlmode == NL80211_IFTYPE_P2P_GO)
+ nl80211_set_p2p_mode(bss, p2p_mode);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
if (!rfkill_is_blocked(drv->rfkill)) {
int ret = i802_set_iface_flags(bss, 1);
if (ret) {
@@ -3042,36 +3585,45 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
nl80211_disable_11b_rates(bss->drv,
bss->drv->ifindex, 1);
- if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE ||
+ nlmode == NL80211_IFTYPE_PD)
return ret;
} else {
wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
"interface '%s' due to rfkill", bss->ifname);
if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
- drv->if_disabled = 1;
+ bss->if_disabled = 1;
send_rfkill_event = 1;
}
- if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
- netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+ if (!drv->hostapd && nl80211_is_netdev_iftype(nlmode))
+ netlink_send_oper_ifla(drv->global->netlink, bss->ifindex,
1, IF_OPER_DORMANT);
- if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ if (nl80211_is_netdev_iftype(nlmode)) {
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
bss->addr))
return -1;
- os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+ if (first)
+ os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
}
- if (send_rfkill_event) {
+ if (send_rfkill_event)
eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
- drv, drv->ctx);
- }
+ drv, bss->ctx);
if (drv->vendor_cmd_test_avail)
qca_vendor_test(drv);
+ if (bss->added_if_into_bridge &&
+ linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0) {
+ wpa_printf(MSG_WARNING,
+ "nl80211: Failed to re-add interface %s into bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+
return 0;
}
@@ -3083,7 +3635,7 @@ static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
struct wpa_driver_nl80211_data *drv = bss->drv;
struct i802_link *link = nl80211_get_link(bss, link_id);
- if (!link->beacon_set)
+ if (!link || !link->beacon_set)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
@@ -3140,6 +3692,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
eloop_unregister_read_sock(drv->eapol_tx_sock);
if (drv->eapol_tx_sock >= 0)
close(drv->eapol_tx_sock);
+#ifdef CONFIG_PR
+ nl80211_pd_stop(bss);
+#endif /* CONFIG_PR */
if (bss->nl_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
@@ -3151,9 +3706,6 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
bss->ifname, bss->brname, strerror(errno));
}
- if (drv->rtnl_sk)
- nl_socket_free(drv->rtnl_sk);
-
if (bss->added_bridge) {
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
0) < 0)
@@ -3166,13 +3718,14 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
bss->brname, strerror(errno));
}
- nl80211_remove_monitor_interface(drv);
-
if (is_ap_interface(drv->nlmode)) {
wpa_driver_nl80211_del_beacon_all(bss);
nl80211_remove_links(bss);
}
+ if (drv->rtnl_sk)
+ nl_socket_free(drv->rtnl_sk);
+
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
close(drv->eapol_sock);
@@ -3184,14 +3737,14 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
- netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+ netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, 0,
IF_OPER_UP);
eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- if (!drv->start_iface_up)
+ if (!bss->start_iface_up)
(void) i802_set_iface_flags(bss, 0);
if (drv->addr_changed) {
@@ -3207,15 +3760,16 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
}
}
- if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ if (nl80211_is_netdev_iftype(drv->nlmode)) {
if (drv->start_mode_sta)
wpa_driver_nl80211_set_mode(bss,
NL80211_IFTYPE_STATION);
nl80211_mgmt_unsubscribe(bss, "deinit");
} else {
nl80211_mgmt_unsubscribe(bss, "deinit");
- nl80211_del_p2pdev(bss);
+ nl80211_del_non_netdev(bss);
}
+ nl80211_put_wiphy_data_ap(bss);
nl80211_destroy_bss(drv->first_bss);
@@ -3351,7 +3905,6 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
__AKM(FT_SAE, FT_SAE);
__AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
__AKM(CCKM, CCKM);
- __AKM(OSEN, OSEN);
__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
__AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192);
__AKM(FILS_SHA256, FILS_SHA256);
@@ -3368,17 +3921,41 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
}
+static int wpa_ver_supported(struct wpa_driver_nl80211_data *drv,
+ int key_mgmt_suites, int proto)
+{
+ enum nl80211_wpa_versions ver = 0;
+
+ if (proto & WPA_PROTO_WPA)
+ ver |= NL80211_WPA_VERSION_1;
+ if (proto & WPA_PROTO_RSN) {
+ /*
+ * NL80211_ATTR_SAE_PASSWORD is related and was added
+ * at the same time as NL80211_WPA_VERSION_3.
+ */
+ if (nl80211_attr_supported(drv, NL80211_ATTR_SAE_PASSWORD) &&
+ wpa_key_mgmt_sae(key_mgmt_suites))
+ ver |= NL80211_WPA_VERSION_3;
+ else
+ ver |= NL80211_WPA_VERSION_2;
+ }
+
+ return ver;
+}
+
+
#ifdef CONFIG_DRIVER_NL80211_QCA
-static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
- const u8 *key, size_t key_len)
+static int issue_key_mgmt_set_key(struct i802_bss *bss, const u8 *key,
+ size_t key_len)
{
struct nl_msg *msg;
int ret;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
return 0;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
@@ -3399,12 +3976,12 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
#endif /* CONFIG_DRIVER_NL80211_QCA */
-static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
- const u8 *key, size_t key_len,
+static int nl80211_set_pmk(struct i802_bss *bss, const u8 *key, size_t key_len,
const u8 *addr)
{
struct nl_msg *msg = NULL;
int ret;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
/*
* If the authenticator address is not set, assume it is
@@ -3418,7 +3995,7 @@ static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
MAC2STR(addr));
wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_PMK);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
@@ -3446,6 +4023,7 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
struct nl_msg *key_msg;
int ret;
int skip_set_key = 1;
+ bool is_pd_bss = false;
const char *ifname = params->ifname;
enum wpa_alg alg = params->alg;
const u8 *addr = params->addr;
@@ -3464,6 +4042,20 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
return 0;
ifindex = if_nametoindex(ifname);
+
+#ifdef CONFIG_PR
+ /* Route key operation to PD wdev if own_addr matches */
+ if (drv->pd_bss && params->own_addr &&
+ ether_addr_equal(params->own_addr, drv->pd_bss->addr)) {
+ bss = drv->pd_bss;
+ is_pd_bss = true;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: set_key: routing to PD wdev " MACSTR,
+ MAC2STR(bss->addr));
+ ifindex = 0; /* PD wdev has no ifindex */
+ }
+#endif /* CONFIG_PR */
+
wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
"set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
__func__, ifindex, ifname, alg, addr, key_idx, set_tx,
@@ -3480,19 +4072,27 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
__func__);
- ret = issue_key_mgmt_set_key(drv, key, key_len);
+ ret = issue_key_mgmt_set_key(bss, key, key_len);
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
if (key_flag & KEY_FLAG_PMK) {
if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)
- return nl80211_set_pmk(drv, key, key_len, addr);
+ return nl80211_set_pmk(bss, key, key_len, addr);
/* The driver does not have any offload mechanism for PMK, so
* there is no need to configure this key. */
return 0;
}
+ if (key_flag & KEY_FLAG_NEXT) {
+ /* For now, ignore these since this needs support from the
+ * driver to handle the special cases of two active RX keys. */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: set_key for the next TK for RX-only - ignored");
+ return -EOPNOTSUPP;
+ }
+
ret = -ENOBUFS;
key_msg = nlmsg_alloc();
if (!key_msg)
@@ -3502,7 +4102,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
wpa_printf(MSG_DEBUG,
"nl80211: SET_KEY (pairwise RX/TX modify)");
- msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+ if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss)
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_KEY);
+ else
+ msg = nl80211_ifindex_msg(drv, ifindex, 0,
+ NL80211_CMD_SET_KEY);
if (!msg)
goto fail2;
} else if (alg == WPA_ALG_NONE && (key_flag & KEY_FLAG_RX_TX)) {
@@ -3512,7 +4116,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
goto fail2;
} else if (alg == WPA_ALG_NONE) {
wpa_printf(MSG_DEBUG, "nl80211: DEL_KEY");
- msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+ if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss)
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_KEY);
+ else
+ msg = nl80211_ifindex_msg(drv, ifindex, 0,
+ NL80211_CMD_DEL_KEY);
if (!msg)
goto fail2;
} else {
@@ -3524,7 +4132,12 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
goto fail2;
}
wpa_printf(MSG_DEBUG, "nl80211: NEW_KEY");
- msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+ if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss)
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NEW_KEY);
+ else
+ msg = nl80211_ifindex_msg(drv, ifindex, 0,
+ NL80211_CMD_NEW_KEY);
+
if (!msg)
goto fail2;
if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) ||
@@ -3538,6 +4151,16 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ",
seq, seq_len);
}
+
+ if (params->ltf_keyseed_len && params->ltf_keyseed) {
+ if (nla_put(key_msg, NL80211_KEY_LTF_SEED,
+ params->ltf_keyseed_len,
+ params->ltf_keyseed))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_LTF_SEED",
+ params->ltf_keyseed,
+ params->ltf_keyseed_len);
+ }
}
if (addr && !is_broadcast_ether_addr(addr)) {
@@ -3559,6 +4182,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
NL80211_KEYTYPE_GROUP))
goto fail;
+ } else if (alg == WPA_ALG_NONE &&
+ (key_flag & KEY_FLAG_GROUP_MASK) == KEY_FLAG_GROUP) {
+ if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
+ NL80211_KEYTYPE_GROUP))
+ goto fail;
} else if (!(key_flag & KEY_FLAG_PAIRWISE)) {
wpa_printf(MSG_DEBUG,
" key_flag missing PAIRWISE when setting a pairwise key");
@@ -3621,7 +4249,10 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
if (!key_msg)
return ret;
- msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+ if (!nl80211_is_netdev_iftype(drv->nlmode))
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_KEY);
+ else
+ msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
if (!msg)
goto fail2;
if (!key_msg ||
@@ -3784,7 +4415,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
int ret;
struct nl_msg *msg;
- if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
(local_state_change &&
@@ -3793,7 +4424,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
return -1;
}
- ret = send_and_recv(drv->global, bss->nl_connect, msg,
+ ret = send_and_recv(drv, bss->nl_connect, msg,
NULL, NULL, NULL, NULL, NULL);
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -3834,7 +4465,7 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
nl80211_mark_disconnected(drv);
- return nl80211_leave_ibss(drv, 1);
+ return nl80211_leave_ibss(bss, 1);
}
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
return wpa_driver_nl80211_disconnect(drv, reason_code, bss);
@@ -3955,8 +4586,39 @@ static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
return NL80211_AUTHTYPE_FILS_SK;
if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
return NL80211_AUTHTYPE_FILS_SK_PFS;
+ if (wpa_auth_alg & WPA_AUTH_ALG_EPPKE)
+ return NL80211_AUTHTYPE_EPPKE;
+ if (wpa_auth_alg & WPA_AUTH_ALG_802_1X)
+ return NL80211_AUTHTYPE_IEEE8021X;
+
+ return NL80211_AUTHTYPE_MAX + 1;
+}
+
+
+static int
+nl80211_put_bss_membership_selectors(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg)
+{
+ u8 selectors[ARRAY_SIZE(drv->extra_bss_membership_selectors) + 1];
+ size_t selectors_len;
+
+ if (!nl80211_attr_supported(drv, NL80211_ATTR_SUPPORTED_SELECTORS))
+ return 0;
+
+ for (selectors_len = 0;
+ drv->extra_bss_membership_selectors[selectors_len];
+ selectors_len++) {
+ selectors[selectors_len] =
+ drv->extra_bss_membership_selectors[selectors_len];
+ }
+
+#ifdef CONFIG_SAE
+ /* Always add the SAE H2E selector as it is handled by wpa_supplicant */
+ selectors[selectors_len++] = BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+#endif /* CONFIG_SAE */
- return NL80211_AUTHTYPE_MAX;
+ return nla_put(msg, NL80211_ATTR_SUPPORTED_SELECTORS,
+ selectors_len, selectors);
}
@@ -3995,7 +4657,7 @@ retry:
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_AUTHENTICATE);
if (!msg)
goto fail;
@@ -4052,7 +4714,7 @@ retry:
}
type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
- if (type == NL80211_AUTHTYPE_MAX ||
+ if (type > NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
if (params->local_state_change) {
@@ -4061,6 +4723,10 @@ retry:
goto fail;
}
+ ret = nl80211_put_bss_membership_selectors(drv, msg);
+ if (ret)
+ goto fail;
+
if (params->mld && params->ap_mld_addr) {
wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR,
params->mld_link_id, MAC2STR(params->ap_mld_addr));
@@ -4139,7 +4805,7 @@ retry:
}
} else {
wpa_printf(MSG_DEBUG,
- "nl80211: Authentication request send successfully");
+ "nl80211: Authentication request sent successfully");
}
fail:
@@ -4207,6 +4873,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);
@@ -4249,12 +4931,14 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
- int encrypt = !no_encrypt;
u16 fc;
int use_cookie = 1;
int res;
struct i802_link *link = nl80211_get_link(bss, link_id);
+ if (data_len < 24)
+ return -1;
+
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR " sa=" MACSTR
@@ -4264,6 +4948,17 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
noack, freq, no_cck, offchanok, wait_time,
no_encrypt, fc, fc2str(fc), drv->nlmode);
+#ifdef CONFIG_PR
+ /* Route MLME TX via PD wdev if source address matches */
+ if (drv->pd_bss && ether_addr_equal(mgmt->sa, drv->pd_bss->addr)) {
+ bss = drv->pd_bss;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_mlme - route via PD wdev sa=" MACSTR,
+ MAC2STR(mgmt->sa));
+ link = bss->flink;
+ }
+#endif /* CONFIG_PR */
+
if ((is_sta_interface(drv->nlmode) ||
drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
@@ -4298,21 +4993,9 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
goto send_frame_cmd;
}
- if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
- WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
- /*
- * Only one of the authentication frame types is encrypted.
- * In order for static WEP encryption to work properly (i.e.,
- * to not encrypt the frame), we need to tell mac80211 about
- * the frames that must not be encrypted.
- */
- u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
- u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
- if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
- encrypt = 0;
- }
-
- if (is_sta_interface(drv->nlmode) &&
+ if ((is_sta_interface(drv->nlmode) ||
+ drv->nlmode == NL80211_IFTYPE_P2P_DEVICE ||
+ drv->nlmode == NL80211_IFTYPE_PD) &&
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
if (freq == 0 &&
@@ -4359,29 +5042,17 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
freq = link->freq;
}
- if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
- wpa_printf(MSG_DEBUG,
- "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
- freq, link->freq);
- return nl80211_send_monitor(drv, data, data_len, encrypt,
- noack);
- }
-
if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) &&
link_id == NL80211_DRV_LINK_ID_NA)
use_cookie = 0;
send_frame_cmd:
#ifdef CONFIG_TESTING_OPTIONS
- if (no_encrypt && !encrypt && !drv->use_monitor) {
+ if (no_encrypt) {
wpa_printf(MSG_DEBUG,
"nl80211: Request to send an unencrypted frame - use a monitor interface for this");
- if (nl80211_create_monitor_interface(drv) < 0)
- return -1;
- res = nl80211_send_monitor(drv, data, data_len, encrypt,
- noack);
- nl80211_remove_monitor_interface(drv);
- return res;
+ return nl80211_send_monitor(drv, data, data_len, !no_encrypt,
+ noack);
}
#endif /* CONFIG_TESTING_OPTIONS */
@@ -4405,7 +5076,7 @@ static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
if (!basic_rates)
return 0;
- for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+ for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] > 0; i++)
rates[rates_len++] = basic_rates[i] / 5;
return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
@@ -4484,7 +5155,7 @@ static int wpa_driver_nl80211_set_acl(void *priv,
nla_total_size(acl_nla_sz);
nlmsg_sz = nlmsg_total_size(nla_sz);
if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
- drv->ifindex, 0,
+ bss->ifindex, 0,
NL80211_CMD_SET_MAC_ACL)) ||
nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
@@ -4538,7 +5209,7 @@ static int nl80211_set_mesh_config(void *priv,
struct nl_msg *msg;
int ret;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MESH_CONFIG);
if (!msg)
return -1;
@@ -4727,7 +5398,7 @@ static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
{
u8 sae_pwe;
- wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
+ wpa_printf(MSG_DEBUG, "nl80211: sae_pwe=%d", pwe);
if (pwe == SAE_PWE_HUNT_AND_PECK)
sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
@@ -4809,8 +5480,7 @@ static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
}
-static int nl80211_mbssid(struct nl_msg *msg,
- struct wpa_driver_ap_params *params)
+static int nl80211_mbssid(struct nl_msg *msg, struct mbssid_data *params)
{
struct nlattr *config, *elems;
int ifidx;
@@ -4830,6 +5500,10 @@ static int nl80211_mbssid(struct nl_msg *msg,
nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
ifidx))
return -1;
+ if (params->mbssid_tx_iface_linkid >= 0 &&
+ nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID,
+ params->mbssid_tx_iface_linkid))
+ return -1;
}
if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
@@ -4887,12 +5561,12 @@ static int nl80211_mbssid(struct nl_msg *msg,
#ifdef CONFIG_DRIVER_NL80211_QCA
-static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv,
- const int *freqs, int num_freqs,
- int link_id)
+static void qca_set_allowed_ap_freqs(struct i802_bss *bss, const int *freqs,
+ int num_freqs, int link_id)
{
struct nl_msg *msg;
struct nlattr *params, *freqs_list;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
int i, ret;
if (!drv->set_wifi_conf_vendor_cmd_avail || !drv->qca_ap_allowed_freqs)
@@ -4900,7 +5574,7 @@ static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: Set AP allowed frequency list");
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
@@ -4937,8 +5611,39 @@ err:
#endif /* CONFIG_DRIVER_NL80211_QCA */
+static int nl80211_bw_to_nl(int bw, enum nl80211_chan_width *cw,
+ int center_freq2)
+{
+ switch (bw) {
+ case 20:
+ *cw = NL80211_CHAN_WIDTH_20;
+ break;
+ case 40:
+ *cw = NL80211_CHAN_WIDTH_40;
+ break;
+ case 80:
+ if (center_freq2)
+ *cw = NL80211_CHAN_WIDTH_80P80;
+ else
+ *cw = NL80211_CHAN_WIDTH_80;
+ break;
+ case 160:
+ *cw = NL80211_CHAN_WIDTH_160;
+ break;
+ case 320:
+ *cw = NL80211_CHAN_WIDTH_320;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int nl80211_put_freq_params(struct nl_msg *msg,
- const struct hostapd_freq_params *freq)
+ const struct hostapd_freq_params *freq,
+ struct i802_bss *bss)
{
enum hostapd_hw_mode hw_mode;
int is_24ghz;
@@ -4964,26 +5669,11 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
enum nl80211_chan_width cw;
wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth);
- switch (freq->bandwidth) {
- case 20:
- cw = NL80211_CHAN_WIDTH_20;
- break;
- case 40:
- cw = NL80211_CHAN_WIDTH_40;
- break;
- case 80:
- if (freq->center_freq2)
- cw = NL80211_CHAN_WIDTH_80P80;
- else
- cw = NL80211_CHAN_WIDTH_80;
- break;
- case 160:
- cw = NL80211_CHAN_WIDTH_160;
- break;
- case 320:
- cw = NL80211_CHAN_WIDTH_320;
- break;
- default:
+ if (nl80211_bw_to_nl(freq->bandwidth, &cw,
+ freq->center_freq2) < 0) {
+ wpa_printf(MSG_DEBUG,
+ " * Unsupported bandwidth %d MHz",
+ freq->bandwidth);
return -EINVAL;
}
@@ -4999,6 +5689,20 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
freq->center_freq2)))
return -ENOBUFS;
+
+ if (freq->eht_enabled && freq->punct_bitmap) {
+ if (!bss->drv->puncturing) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Puncture bitmap is set but the driver does dot support puncturing");
+ return -EINVAL;
+ }
+ wpa_printf(MSG_DEBUG, " * punct_bitmap=0x%x",
+ freq->punct_bitmap);
+ if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+ freq->punct_bitmap))
+ return -ENOBUFS;
+
+ }
} else if (freq->ht_enabled) {
enum nl80211_channel_type ct;
@@ -5055,7 +5759,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
int beacon_set;
int num_suites;
u32 suites[20], suite;
- u32 ver;
+ enum nl80211_wpa_versions ver;
#ifdef CONFIG_MESH
struct wpa_driver_mesh_bss_params mesh_params;
#endif /* CONFIG_MESH */
@@ -5081,8 +5785,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
beacon_set);
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
- else if (!drv->device_ap_sme && !drv->use_monitor &&
- !nl80211_get_wiphy_data_ap(bss))
+ else if (!drv->device_ap_sme && !nl80211_get_wiphy_data_ap(bss))
return -ENOBUFS;
wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
@@ -5108,6 +5811,12 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
+ if (params->freq)
+ nl80211_link_set_freq(bss,
+ params->mld_ap ? params->mld_link_id :
+ NL80211_DRV_LINK_ID_NA,
+ params->freq->freq);
+
if (params->mld_ap) {
wpa_printf(MSG_DEBUG, "nl80211: link_id=%u",
params->mld_link_id);
@@ -5115,10 +5824,6 @@ static int wpa_driver_nl80211_set_ap(void *priv,
if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
params->mld_link_id))
goto fail;
-
- if (params->freq)
- nl80211_link_set_freq(bss, params->mld_link_id,
- params->freq->freq);
}
if (params->proberesp && params->proberesp_len) {
@@ -5166,12 +5871,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
goto fail;
}
- wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
- ver = 0;
- if (params->wpa_version & WPA_PROTO_WPA)
- ver |= NL80211_WPA_VERSION_1;
- if (params->wpa_version & WPA_PROTO_RSN)
- ver |= NL80211_WPA_VERSION_2;
+ ver = wpa_ver_supported(drv, params->key_mgmt_suites,
+ params->wpa_version);
+ wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", ver);
if (ver &&
nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
goto fail;
@@ -5326,7 +6028,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nla_nest_end(msg, ftm);
}
- if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+ if (params->freq && nl80211_put_freq_params(msg, params->freq, bss) < 0)
goto fail;
#ifdef CONFIG_IEEE80211AX
@@ -5390,7 +6092,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nl80211_unsol_bcast_probe_resp(bss, msg, &params->ubpr) < 0)
goto fail;
- if (nl80211_mbssid(msg, params) < 0)
+ if (nl80211_mbssid(msg, &params->mbssid) < 0)
goto fail;
#endif /* CONFIG_IEEE80211AX */
@@ -5405,17 +6107,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
goto fail;
#endif /* CONFIG_FILS */
- if (params->punct_bitmap) {
- wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x",
- params->punct_bitmap);
- if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
- params->punct_bitmap))
- goto fail;
- }
-
#ifdef CONFIG_DRIVER_NL80211_QCA
if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs)
- qca_set_allowed_ap_freqs(drv, params->allowed_freqs,
+ qca_set_allowed_ap_freqs(bss, params->allowed_freqs,
int_array_len(params->allowed_freqs),
params->mld_ap ? params->mld_link_id :
NL80211_DRV_LINK_ID_NA);
@@ -5423,7 +6117,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
goto fail;
- ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -5494,9 +6188,9 @@ static int nl80211_set_channel(struct i802_bss *bss,
freq->he_enabled, freq->eht_enabled, freq->bandwidth,
freq->center_freq1, freq->center_freq2);
- msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
NL80211_CMD_SET_WIPHY);
- if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ if (!msg || nl80211_put_freq_params(msg, freq, bss) < 0) {
nlmsg_free(msg);
return -1;
}
@@ -5540,6 +6234,8 @@ static u32 sta_flags_nl80211(int flags)
f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
if (flags & WPA_STA_ASSOCIATED)
f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ if (flags & WPA_STA_SPP_AMSDU)
+ f |= BIT(NL80211_STA_FLAG_SPP_AMSDU);
return f;
}
@@ -5601,7 +6297,7 @@ static int wpa_driver_nl80211_sta_add(void *priv,
wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
cmd_string, MAC2STR(params->addr));
- msg = nl80211_bss_msg(bss, 0, cmd);
+ msg = nl80211_cmd_msg(bss, 0, cmd);
if (!msg)
goto fail;
@@ -5620,8 +6316,11 @@ static int wpa_driver_nl80211_sta_add(void *priv,
params->supp_rates, params->supp_rates_len);
wpa_printf(MSG_DEBUG, " * capability=0x%x",
params->capability);
- if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
- params->supp_rates_len, params->supp_rates) ||
+ if (((params->supp_rates_len > 0 ||
+ (drv->nlmode != NL80211_IFTYPE_NAN &&
+ drv->nlmode != NL80211_IFTYPE_NAN_DATA)) &&
+ nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+ params->supp_rates_len, params->supp_rates)) ||
nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
params->capability))
goto fail;
@@ -5680,6 +6379,15 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+ /* Set EML capabilities of ML STA */
+ if (params->mld_link_addr && params->eml_cap) {
+ wpa_printf(MSG_DEBUG, " * eml_cap =%u",
+ params->eml_cap);
+ if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY,
+ params->eml_cap))
+ goto fail;
+ }
+
if (is_ap_interface(drv->nlmode) &&
nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
params->support_p2p_ps ?
@@ -5725,6 +6433,31 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+#ifdef CONFIG_NAN
+ if (params->set && drv->nlmode == NL80211_IFTYPE_NAN &&
+ (params->flags & WPA_STA_AUTHORIZED)) {
+ if (params->ht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * ht_capabilities",
+ params->ht_capabilities,
+ sizeof(*params->ht_capabilities));
+ if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+ sizeof(*params->ht_capabilities),
+ params->ht_capabilities))
+ goto fail;
+ }
+
+ if (params->vht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * vht_capabilities",
+ params->vht_capabilities,
+ sizeof(*params->vht_capabilities));
+ if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+ sizeof(*params->vht_capabilities),
+ params->vht_capabilities))
+ goto fail;
+ }
+ }
+#endif /* CONFIG_NAN */
+
if (params->vht_opmode_enabled) {
wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode);
if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
@@ -5852,6 +6585,35 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+#ifdef CONFIG_ENC_ASSOC
+ if (params->epp_sta) {
+ wpa_printf(MSG_DEBUG, " * EPP STA");
+ if (nla_put_flag(msg, NL80211_ATTR_EPP_PEER))
+ goto fail;
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
+#ifdef CONFIG_NAN
+ /*
+ * Set the address of the NMI station to which the NDI station belongs.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_NAN_DATA) {
+ if (!params->nmi_addr) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NMI address not provided for NDI station");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, " * nmi_addr=" MACSTR,
+ MAC2STR(params->nmi_addr));
+
+ if (nla_put(msg, NL80211_ATTR_NAN_NMI_MAC, ETH_ALEN,
+ params->nmi_addr))
+ goto fail;
+ }
+#endif /* CONFIG_NAN */
+
ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret)
@@ -5865,13 +6627,15 @@ fail:
}
-static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr,
+ bool is_bridge)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ndmsg nhdr = {
.ndm_state = NUD_PERMANENT,
- .ndm_ifindex = bss->ifindex,
+ .ndm_ifindex = is_bridge ? bss->br_ifindex : bss->ifindex,
.ndm_family = AF_BRIDGE,
+ .ndm_type = is_bridge ? NTF_SELF : 0,
};
struct nl_msg *msg;
int err;
@@ -5888,11 +6652,61 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
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),
- bss->ifindex, nl_geterror(err));
+ MACSTR " ifindex=%d ifname %s failed: %s",
+ MAC2STR(addr),
+ is_bridge ? bss->br_ifindex : bss->ifindex,
+ is_bridge ? bss->brname : bss->ifname,
+ nl_geterror(err));
} else {
- wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
- MACSTR, MAC2STR(addr));
+ wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry "
+ MACSTR " from %s",
+ MAC2STR(addr),
+ is_bridge ? bss->brname : bss->ifname);
+ }
+
+errout:
+ nlmsg_free(msg);
+}
+
+
+static void rtnl_neigh_add_fdb_entry(struct i802_bss *bss, const u8 *addr,
+ bool is_bridge)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = is_bridge ? bss->br_ifindex : bss->ifindex,
+ .ndm_family = AF_BRIDGE,
+ /* TODO: remove this check if this flag needs to be used,
+ * for other interfaces type.
+ */
+ .ndm_flags = is_bridge ? NTF_SELF : 0,
+ };
+ struct nl_msg *msg;
+ int err;
+
+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+ if (!msg)
+ return;
+
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr) ||
+ nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
+ goto errout;
+
+ err = nl_wait_for_ack(drv->rtnl_sk);
+ if (err < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry addition for "
+ MACSTR " ifindex=%d ifname %s failed: %s",
+ MAC2STR(addr),
+ is_bridge ? bss->br_ifindex : bss->ifindex,
+ is_bridge ? bss->brname : bss->ifname,
+ nl_geterror(err));
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: added bridge FDB entry " MACSTR
+ " to %s",
+ MAC2STR(addr),
+ is_bridge ? bss->brname : bss->ifname);
}
errout:
@@ -5907,7 +6721,7 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
struct nl_msg *msg;
int ret;
- if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
(deauth == 0 &&
nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
@@ -5927,7 +6741,7 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
bss->ifname, MAC2STR(addr), ret, strerror(-ret));
if (drv->rtnl_sk)
- rtnl_neigh_delete_fdb_entry(bss, addr);
+ rtnl_neigh_delete_fdb_entry(bss, addr, false);
if (ret == -ENOENT)
return 0;
@@ -5984,7 +6798,11 @@ const char * nl80211_iftype_str(enum nl80211_iftype mode)
case NL80211_IFTYPE_OCB:
return "OCB";
case NL80211_IFTYPE_NAN:
- return "NAN";
+ return "NAN DEVICE";
+ case NL80211_IFTYPE_NAN_DATA:
+ return "NAN_DATA";
+ case NL80211_IFTYPE_PD:
+ return "PD DEVICE";
default:
return "unknown";
}
@@ -6011,19 +6829,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
goto fail;
- if (iftype == NL80211_IFTYPE_MONITOR) {
- struct nlattr *flags;
-
- flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
- if (!flags ||
- nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
- goto fail;
-
- nla_nest_end(msg, flags);
- } else if (wds) {
- if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
- goto fail;
- }
+ if (wds && nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+ goto fail;
/*
* Tell cfg80211 that the interface belongs to the socket that created
@@ -6032,11 +6839,49 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
goto fail;
- if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) &&
+ if ((addr && (iftype == NL80211_IFTYPE_P2P_DEVICE ||
+ iftype == NL80211_IFTYPE_PD ||
+ iftype == NL80211_IFTYPE_NAN)) &&
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
- ret = send_and_recv_resp(drv, msg, handler, arg);
+ /* NAN interface is created on a dedicated socket */
+ if (iftype == NL80211_IFTYPE_NAN) {
+#ifdef CONFIG_NAN
+ if (drv->global->nl_nan) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to create NAN interface %s: socket in use",
+ ifname);
+ goto fail;
+ }
+
+ drv->global->nl_nan = nl_create_handle(drv->global->nl_cb,
+ "nan");
+ if (!drv->global->nl_nan)
+ goto fail;
+
+ ret = send_and_recv(drv, drv->global->nl_nan, msg,
+ handler, arg, NULL, NULL, NULL);
+ if (ret) {
+ nl_destroy_handles(&drv->global->nl_nan);
+ goto fail;
+ }
+
+ /*
+ * NAN events are received on a socket which is used to create
+ * the interface. Note that after this call this socket can't be
+ * used for sending commands anymore.
+ */
+ nl80211_register_eloop_read(&drv->global->nl_nan,
+ wpa_driver_nl80211_event_receive,
+ drv->global->nl_cb, 0);
+#else /* CONFIG_NAN */
+ ret = -EOPNOTSUPP;
+#endif /* CONFIG_NAN */
+ } else {
+ ret = send_and_recv_resp(drv, msg, handler, arg);
+ }
+
msg = NULL;
if (ret) {
fail:
@@ -6046,7 +6891,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
return ret;
}
- if (iftype == NL80211_IFTYPE_P2P_DEVICE)
+ if (!nl80211_is_netdev_iftype(iftype))
return 0;
ifidx = if_nametoindex(ifname);
@@ -6078,6 +6923,24 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
}
+static int nl80211_use_existing_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname,
+ enum nl80211_iftype iftype,
+ const u8 *addr)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
+ ifname);
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr) < 0 &&
+ (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 0) < 0 ||
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr) < 0 ||
+ linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1) < 0))
+ return -1;
+
+ return -ENFILE;
+}
+
+
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
@@ -6086,25 +6949,17 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
{
int ret;
+ if (use_existing && if_nametoindex(ifname))
+ return nl80211_use_existing_iface(drv, ifname, iftype, addr);
+
ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
arg);
/* if error occurred and interface exists already */
- if (ret == -ENFILE && if_nametoindex(ifname)) {
+ if (ret < 0 && if_nametoindex(ifname)) {
if (use_existing) {
- wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
- ifname);
- if (addr && iftype != NL80211_IFTYPE_MONITOR &&
- linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
- addr) < 0 &&
- (linux_set_iface_flags(drv->global->ioctl_sock,
- ifname, 0) < 0 ||
- linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
- addr) < 0 ||
- linux_set_iface_flags(drv->global->ioctl_sock,
- ifname, 1) < 0))
- return -1;
- return -ENFILE;
+ return nl80211_use_existing_iface(drv, ifname, iftype,
+ addr);
}
wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
@@ -6131,8 +6986,8 @@ static int nl80211_setup_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
- bss->ifname, drv->device_ap_sme, drv->use_monitor);
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d",
+ bss->ifname, drv->device_ap_sme);
/*
* Disable Probe Request reporting unless we need it in this way for
@@ -6142,20 +6997,13 @@ static int nl80211_setup_ap(struct i802_bss *bss)
if (!drv->device_ap_sme)
wpa_driver_nl80211_probe_req_report(bss, 0);
- if (!drv->device_ap_sme && !drv->use_monitor)
- if (nl80211_mgmt_subscribe_ap(bss))
- return -1;
-
- if (drv->device_ap_sme && !drv->use_monitor)
- if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
- wpa_printf(MSG_DEBUG,
- "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
-
- if (!drv->device_ap_sme && drv->use_monitor &&
- nl80211_create_monitor_interface(drv) &&
- !drv->device_ap_sme)
+ if (!drv->device_ap_sme && nl80211_mgmt_subscribe_ap(bss))
return -1;
+ if (drv->device_ap_sme && nl80211_mgmt_subscribe_ap_dev_sme(bss))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
+
if (drv->device_ap_sme &&
wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
@@ -6171,16 +7019,14 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
- bss->ifname, drv->device_ap_sme, drv->use_monitor);
+ wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d",
+ bss->ifname, drv->device_ap_sme);
if (drv->device_ap_sme) {
wpa_driver_nl80211_probe_req_report(bss, 0);
- if (!drv->use_monitor)
- nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
- } else if (drv->use_monitor)
- nl80211_remove_monitor_interface(drv);
- else
+ nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+ } else {
nl80211_mgmt_unsubscribe(bss, "AP teardown");
+ }
nl80211_put_wiphy_data_ap(bss);
if (bss->flink)
@@ -6218,7 +7064,7 @@ static int nl80211_tx_control_port(void *priv, const u8 *dest,
os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
ext_arg.ext_data = &cookie;
- ret = send_and_recv(bss->drv->global, bss->drv->global->nl, msg,
+ ret = send_and_recv(bss->drv, bss->drv->global->nl, msg,
NULL, NULL, ack_handler_cookie, &ext_arg, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -6266,8 +7112,6 @@ static int nl80211_send_eapol_data(struct i802_bss *bss,
}
-static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
static int wpa_driver_nl80211_hapd_send_eapol(
void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt, const u8 *own_addr, u32 flags,
@@ -6275,11 +7119,6 @@ static int wpa_driver_nl80211_hapd_send_eapol(
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct ieee80211_hdr *hdr;
- size_t len;
- u8 *pos;
- int res;
- int qos = flags & WPA_STA_WMM;
/* For now, disable EAPOL TX over control port in AP mode by default
* since it does not provide TX status notifications. */
@@ -6289,55 +7128,7 @@ static int wpa_driver_nl80211_hapd_send_eapol(
data, data_len, !encrypt,
link_id);
- if (drv->device_ap_sme || !drv->use_monitor)
- return nl80211_send_eapol_data(bss, addr, data, data_len);
-
- len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
- data_len;
- hdr = os_zalloc(len);
- if (hdr == NULL) {
- wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
- (unsigned long) len);
- return -1;
- }
-
- hdr->frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
- hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
- if (encrypt)
- hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- if (qos) {
- hdr->frame_control |=
- host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
- }
-
- memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
- memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
- pos = (u8 *) (hdr + 1);
-
- if (qos) {
- /* Set highest priority in QoS header */
- pos[0] = 7;
- pos[1] = 0;
- pos += 2;
- }
-
- memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
- pos += sizeof(rfc1042_header);
- WPA_PUT_BE16(pos, ETH_P_PAE);
- pos += 2;
- memcpy(pos, data, data_len);
-
- res = nl80211_send_monitor(drv, hdr, len, encrypt, 0);
- if (res < 0) {
- wpa_printf(MSG_ERROR,
- "hapd_send_eapol - packet len: %lu - failed",
- (unsigned long) len);
- }
- os_free(hdr);
-
- return res;
+ return nl80211_send_eapol_data(bss, addr, data, data_len);
}
@@ -6356,8 +7147,8 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
!!(total_flags & WPA_STA_AUTHORIZED));
- if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_STATION);
+ if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
/*
@@ -6431,20 +7222,20 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
"group (GO)");
nlmode = NL80211_IFTYPE_P2P_GO;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ nl80211_set_p2p_mode(drv->first_bss, params->p2p_mode);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
} else
nlmode = NL80211_IFTYPE_AP;
old_mode = drv->nlmode;
- if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
- nl80211_remove_monitor_interface(drv);
+ if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode))
return -1;
- }
if (params->freq.freq &&
nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
if (old_mode != nlmode)
wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
- nl80211_remove_monitor_interface(drv);
return -1;
}
@@ -6452,14 +7243,13 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
}
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
- int reset_mode)
+static int nl80211_leave_ibss(struct i802_bss *bss, int reset_mode)
{
struct nl_msg *msg;
int ret;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
- ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_IBSS);
+ ret = send_and_recv(bss->drv, bss->nl_connect, msg, NULL,
NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
@@ -6470,8 +7260,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
}
if (reset_mode &&
- wpa_driver_nl80211_set_mode(drv->first_bss,
- NL80211_IFTYPE_STATION)) {
+ wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"station mode");
}
@@ -6536,23 +7325,24 @@ static int nl80211_ht_vht_overrides(struct nl_msg *msg,
}
-static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+static int wpa_driver_nl80211_ibss(struct i802_bss *bss,
struct wpa_driver_associate_params *params)
{
struct nl_msg *msg;
int ret = -1;
int count = 0;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
- if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, &params->freq)) {
+ if (wpa_driver_nl80211_set_mode_ibss(bss, &params->freq)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"IBSS mode");
return -1;
}
retry:
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_IBSS)) ||
params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
goto fail;
@@ -6563,7 +7353,7 @@ retry:
os_memcpy(drv->ssid, params->ssid, params->ssid_len);
drv->ssid_len = params->ssid_len;
- if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+ if (nl80211_put_freq_params(msg, &params->freq, bss) < 0 ||
nl80211_put_beacon_int(msg, params->beacon_int))
goto fail;
@@ -6609,7 +7399,7 @@ retry:
if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
goto fail;
- ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+ ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL,
NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
@@ -6619,7 +7409,7 @@ retry:
if (ret == -EALREADY && count == 1) {
wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
"forced leave");
- nl80211_leave_ibss(drv, 0);
+ nl80211_leave_ibss(bss, 0);
nlmsg_free(msg);
goto retry;
}
@@ -6761,14 +7551,18 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
return -1;
}
- if (params->freq.freq && !params->mld_params.mld_addr) {
- wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
- params->freq.freq))
- return -1;
+ if (params->freq.freq) {
+ if (!params->mld_params.mld_addr) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ params->freq.freq))
+ return -1;
+ }
+
drv->assoc_freq = params->freq.freq;
- } else
+ } else {
drv->assoc_freq = 0;
+ }
if (params->freq_hint) {
wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint);
@@ -6815,23 +7609,10 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
return -1;
if (params->wpa_proto) {
- enum nl80211_wpa_versions ver = 0;
-
- if (params->wpa_proto & WPA_PROTO_WPA)
- ver |= NL80211_WPA_VERSION_1;
- if (params->wpa_proto & WPA_PROTO_RSN) {
- /*
- * NL80211_ATTR_SAE_PASSWORD is related and was added
- * at the same time as NL80211_WPA_VERSION_3.
- */
- if (nl80211_attr_supported(drv,
- NL80211_ATTR_SAE_PASSWORD) &&
- wpa_key_mgmt_sae(params->key_mgmt_suite))
- ver |= NL80211_WPA_VERSION_3;
- else
- ver |= NL80211_WPA_VERSION_2;
- }
+ enum nl80211_wpa_versions ver;
+ ver = wpa_ver_supported(drv, params->key_mgmt_suite,
+ params->wpa_proto);
wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
return -1;
@@ -6864,7 +7645,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
- params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
@@ -6880,7 +7660,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
params->key_mgmt_suite == WPA_KEY_MGMT_DPP ||
- params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_EPPKE) {
u32 *mgmt;
unsigned int akm_count = 1, i;
@@ -6922,9 +7703,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
case WPA_KEY_MGMT_PSK_SHA256:
mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
break;
- case WPA_KEY_MGMT_OSEN:
- mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
- break;
case WPA_KEY_MGMT_SAE:
mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
break;
@@ -6967,6 +7745,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
case WPA_KEY_MGMT_IEEE8021X_SHA384:
mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
break;
+ case WPA_KEY_MGMT_EPPKE:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_EPPKE;
+ break;
case WPA_KEY_MGMT_PSK:
default:
mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
@@ -7062,10 +7843,109 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
return -1;
+ if (params->spp_amsdu) {
+ wpa_printf(MSG_DEBUG, " * SPP A-MSDU");
+ if (nla_put_flag(msg, NL80211_ATTR_ASSOC_SPP_AMSDU))
+ return -1;
+ }
+
return 0;
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void connect_ext_feature_set(u8 *features,
+ enum qca_wlan_connect_ext_features idx)
+{
+ u8 *idx_byte = &features[idx / 8];
+
+ *idx_byte |= BIT(idx % 8);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_connect_ext(struct i802_bss *bss,
+ struct wpa_driver_associate_params *params)
+{
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ struct nl_msg *msg;
+ struct nlattr *attr;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u8 features[(NUM_QCA_CONNECT_EXT_FEATURES + 7) / 8] = {};
+
+ if (!drv->connect_ext_vendor_cmd_avail)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect_ext (ifindex=%d)",
+ bss->ifindex);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ if (params->rsn_overriding) {
+ wpa_printf(MSG_DEBUG, "- RSN overriding");
+ connect_ext_feature_set(features, QCA_CONNECT_EXT_FEATURE_RSNO);
+ }
+
+#ifdef CONFIG_ENC_ASSOC
+ if (params->eppke_supported) {
+ wpa_printf(MSG_DEBUG,
+ "- EPPKE external authentication support");
+ connect_ext_feature_set(features,
+ QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE);
+ }
+#endif /* CONFIG_ENC_ASSOC */
+
+#ifdef CONFIG_IEEE8021X_AUTH
+ if (params->ieee8021x_auth_supported) {
+ wpa_printf(MSG_DEBUG,
+ "- IEEE 802.1X external authentication support");
+ connect_ext_feature_set(features,
+ QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X);
+ }
+#endif /* CONFIG_IEEE8021X_AUTH */
+
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES,
+ sizeof(features), features))
+ goto fail;
+
+ if (params->bssid_filter && params->bssid_filter_count) {
+ struct nlattr *bssid_list;
+ unsigned int i;
+
+ bssid_list = nla_nest_start(
+ msg, QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS);
+ if (!bssid_list)
+ goto fail;
+
+ for (i = 0; i < params->bssid_filter_count; i++) {
+ wpa_printf(MSG_DEBUG, "- bssid_filter[%d]=" MACSTR, i,
+ MAC2STR(&params->bssid_filter[i * ETH_ALEN]));
+ if (nla_put(msg, i + 1, ETH_ALEN,
+ &params->bssid_filter[i * ETH_ALEN]))
+ goto fail;
+ }
+
+ nla_nest_end(msg, bssid_list);
+ }
+
+ nla_nest_end(msg, attr);
+
+ return send_and_recv_cmd(drv, msg);
+fail:
+ nlmsg_free(msg);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ return -1;
+}
+
+
static int wpa_driver_nl80211_try_connect(
struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
@@ -7081,14 +7961,15 @@ static int wpa_driver_nl80211_try_connect(
(wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
- ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+ ret = issue_key_mgmt_set_key(bss, params->psk, 32);
if (ret)
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
+ nl80211_connect_ext(bss, params);
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONNECT);
if (!msg)
return -1;
@@ -7146,6 +8027,8 @@ static int wpa_driver_nl80211_try_connect(
algs++;
if (params->auth_alg & WPA_AUTH_ALG_SAE)
algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_EPPKE)
+ algs++;
if (algs > 1) {
wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
"selection");
@@ -7154,7 +8037,7 @@ static int wpa_driver_nl80211_try_connect(
type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
- if (type == NL80211_AUTHTYPE_MAX ||
+ if (type > NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
@@ -7165,7 +8048,7 @@ skip_auth_type:
if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
goto fail;
- ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
NULL, NULL);
msg = NULL;
if (ret) {
@@ -7176,7 +8059,7 @@ skip_auth_type:
drv->roam_indication_done = false;
#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG,
- "nl80211: Connect request send successfully");
+ "nl80211: Connect request sent successfully");
}
fail:
@@ -7224,7 +8107,7 @@ static int wpa_driver_nl80211_associate(
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct nl80211_err_info err_info;
+ struct nl80211_err_info err_info = { -1 };
int ret = -1;
struct nl_msg *msg;
@@ -7234,7 +8117,7 @@ static int wpa_driver_nl80211_associate(
return wpa_driver_nl80211_ap(drv, params);
if (params->mode == IEEE80211_MODE_IBSS)
- return wpa_driver_nl80211_ibss(drv, params);
+ return wpa_driver_nl80211_ibss(bss, params);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
enum nl80211_iftype nlmode = params->p2p ?
@@ -7250,7 +8133,7 @@ static int wpa_driver_nl80211_associate(
wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
drv->ifindex);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOCIATE);
if (!msg)
return -1;
@@ -7258,6 +8141,10 @@ static int wpa_driver_nl80211_associate(
if (ret)
goto fail;
+ ret = nl80211_put_bss_membership_selectors(drv, msg);
+ if (ret)
+ goto fail;
+
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
goto fail;
@@ -7281,7 +8168,7 @@ static int wpa_driver_nl80211_associate(
if (!TEST_FAIL_TAG("assoc")) {
if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
goto fail;
- ret = send_and_recv(drv->global, drv->first_bss->nl_connect,
+ ret = send_and_recv(drv, drv->first_bss->nl_connect,
msg, NULL, NULL, NULL, NULL, &err_info);
msg = NULL;
} else {
@@ -7316,7 +8203,7 @@ static int wpa_driver_nl80211_associate(
}
} else {
wpa_printf(MSG_DEBUG,
- "nl80211: Association request send successfully");
+ "nl80211: Association request sent successfully");
}
fail:
@@ -7325,27 +8212,26 @@ fail:
}
-static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
- int ifindex, enum nl80211_iftype mode)
+static int nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype mode)
{
struct nl_msg *msg;
int ret = -ENOBUFS;
wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
- ifindex, mode, nl80211_iftype_str(mode));
+ bss->ifindex, mode, nl80211_iftype_str(mode));
- msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_INTERFACE);
if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
goto fail;
- ret = send_and_recv_cmd(drv, msg);
+ ret = send_and_recv_cmd(bss->drv, msg);
msg = NULL;
if (!ret)
return 0;
fail:
nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
- " %d (%s)", ifindex, mode, ret, strerror(-ret));
+ " %d (%s)", bss->ifindex, mode, ret, strerror(-ret));
return ret;
}
@@ -7365,7 +8251,7 @@ static int wpa_driver_nl80211_set_mode_impl(
if (TEST_FAIL())
return -1;
- mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ mode_switch_res = nl80211_set_mode(bss, nlmode);
if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
mode_switch_res = 0;
@@ -7429,7 +8315,7 @@ static int wpa_driver_nl80211_set_mode_impl(
}
/* Try to set the mode again while the interface is down */
- mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ mode_switch_res = nl80211_set_mode(bss, nlmode);
if (mode_switch_res == -EBUSY) {
wpa_printf(MSG_DEBUG,
"nl80211: Delaying mode set while interface going down");
@@ -7444,7 +8330,7 @@ static int wpa_driver_nl80211_set_mode_impl(
wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
"interface is down");
drv->nlmode = nlmode;
- drv->ignore_if_down_event = 1;
+ bss->ignore_if_down_event = 1;
}
/* Bring the interface back up */
@@ -7490,6 +8376,10 @@ done:
nl80211_mgmt_subscribe_mesh(bss))
return -1;
+ if (nlmode == NL80211_IFTYPE_NAN ||
+ nlmode == NL80211_IFTYPE_NAN_DATA)
+ return nl80211_mgmt_subscribe_nan(bss, nlmode);
+
if (!bss->in_deinit && !is_ap_interface(nlmode) &&
!is_mesh_interface(nlmode) &&
nl80211_mgmt_subscribe_non_ap(bss) < 0)
@@ -7563,10 +8453,10 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state)
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
- bss->ifname, drv->operstate, state,
+ bss->ifname, bss->operstate, state,
state ? "UP" : "DORMANT");
- drv->operstate = state;
- return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
+ bss->operstate = state;
+ return netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
@@ -7657,8 +8547,12 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
struct nl_msg *msg;
int res;
- msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
- NL80211_CMD_GET_KEY);
+ if (!nl80211_is_netdev_iftype(drv->nlmode))
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_KEY);
+ else
+ msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+ NL80211_CMD_GET_KEY);
+
if (!msg ||
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
(link_id != NL80211_DRV_LINK_ID_NA &&
@@ -7693,7 +8587,7 @@ static int i802_set_rts(void *priv, int rts)
else
val = rts;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -7721,7 +8615,7 @@ static int i802_set_frag(void *priv, int frag)
else
val = frag;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -7754,6 +8648,8 @@ static int i802_flush(void *priv, int link_id)
* XXX: FIX! this needs to flush all VLANs too
*/
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+ if (!msg)
+ goto fail;
if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
goto fail;
@@ -8077,8 +8973,7 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
}
-int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid,
+int nl80211_get_link_signal(struct i802_bss *bss, const u8 *bssid,
struct hostap_sta_driver_data *data)
{
struct nl_msg *msg;
@@ -8086,13 +8981,13 @@ int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
data->signal = -WPA_INVALID_NOISE;
data->current_tx_rate = 0;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
nlmsg_free(msg);
return -ENOBUFS;
}
- return send_and_recv_resp(drv, msg, get_sta_handler, data);
+ return send_and_recv_resp(bss->drv, msg, get_sta_handler, data);
}
@@ -8254,7 +9149,8 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
HOSTAPD_MODE_IEEE80211AD) {
/* Deauthentication is not used in DMG/IEEE 802.11ad;
* disassociate the STA instead. */
- return i802_sta_disassoc(priv, own_addr, addr, reason);
+ return i802_sta_disassoc(priv, own_addr, addr, reason,
+ link_id);
}
if (is_mesh_interface(drv->nlmode))
@@ -8278,7 +9174,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- u16 reason)
+ u16 reason, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8300,7 +9196,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
- 0, NULL, 0, 0, -1);
+ 0, NULL, 0, 0, link_id);
}
@@ -8338,7 +9234,7 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
wpa_printf(MSG_DEBUG,
"nl80211: Add own interface ifindex %d (ifidx_reason %d)",
ifidx, ifidx_reason);
- if (have_ifidx(drv, ifidx, ifidx_reason)) {
+ if (nl80211_has_ifidx(drv, ifidx, ifidx_reason)) {
wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
ifidx);
return;
@@ -8398,7 +9294,7 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
}
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+int nl80211_has_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason)
{
int i;
@@ -8420,6 +9316,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
struct wpa_driver_nl80211_data *drv = bss->drv;
char name[IFNAMSIZ + 1];
union wpa_event_data event;
+ bool add_br = false;
int ret;
ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
@@ -8441,10 +9338,9 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
bss->addr, 1, NULL, NULL, 0) <
0)
return -1;
- if (bridge_ifname &&
- linux_br_add_if(drv->global->ioctl_sock,
- bridge_ifname, name) < 0)
- return -1;
+
+ if (bridge_ifname)
+ add_br = true;
os_memset(&event, 0, sizeof(event));
event.wds_sta_interface.sta_addr = addr;
@@ -8458,6 +9354,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
"interface %s up", name);
}
+
+ if (add_br &&
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
+ return -1;
+
return i802_set_sta_vlan(priv, addr, name, 0,
NL80211_DRV_LINK_ID_NA);
} else {
@@ -8485,6 +9387,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct i802_bss *bss;
struct sockaddr_ll lladdr;
unsigned char buf[3000];
int len;
@@ -8498,8 +9401,10 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
return;
}
- if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
- drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+ if (nl80211_has_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY)) {
+ for (bss = drv->first_bss; bss; bss = bss->next)
+ drv_event_eapol_rx(bss->ctx, lladdr.sll_addr, buf, len);
+ }
}
@@ -8577,7 +9482,8 @@ static void *i802_init(struct hostapd_data *hapd,
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
params->global_priv, 1,
- params->bssid, params->driver_params);
+ params->bssid, params->driver_params,
+ WPA_P2P_MODE_WFD_R1);
if (bss == NULL)
return NULL;
@@ -8711,6 +9617,12 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
+ case WPA_IF_NAN:
+ return NL80211_IFTYPE_NAN;
+ case WPA_IF_NAN_DATA:
+ return NL80211_IFTYPE_NAN_DATA;
+ case WPA_IF_PD:
+ return NL80211_IFTYPE_PD;
default:
return -1;
}
@@ -8796,26 +9708,28 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
if (addr)
os_memcpy(if_addr, addr, ETH_ALEN);
nlmode = wpa_driver_nl80211_if_type(type);
- if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
- struct wdev_info p2pdev_info;
+ if (!nl80211_is_netdev_iftype(nlmode)) {
+ struct wdev_info nonnetdev_info;
- os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+ os_memset(&nonnetdev_info, 0, sizeof(nonnetdev_info));
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
0, nl80211_wdev_handler,
- &p2pdev_info, use_existing);
- if (!p2pdev_info.wdev_id_set || ifidx != 0) {
- wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
+ &nonnetdev_info, use_existing);
+ if (!nonnetdev_info.wdev_id_set || ifidx != 0) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to create a P2P/NAN Device interface %s",
ifname);
return -1;
}
- drv->global->if_add_wdevid = p2pdev_info.wdev_id;
- drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
- if (!is_zero_ether_addr(p2pdev_info.macaddr))
- os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
- wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+ drv->global->if_add_wdevid = nonnetdev_info.wdev_id;
+ drv->global->if_add_wdevid_set = nonnetdev_info.wdev_id_set;
+ if (!is_zero_ether_addr(nonnetdev_info.macaddr))
+ os_memcpy(if_addr, nonnetdev_info.macaddr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: New P2P/NAN Device interface %s (0x%llx) created",
ifname,
- (long long unsigned int) p2pdev_info.wdev_id);
+ (long long unsigned int) nonnetdev_info.wdev_id);
} else {
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
0, NULL, NULL, use_existing);
@@ -8828,7 +9742,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
}
if (!addr) {
- if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ if (!nl80211_is_netdev_iftype(nlmode))
os_memcpy(if_addr, bss->addr, ETH_ALEN);
else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
ifname, if_addr) < 0) {
@@ -8878,6 +9792,20 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
return -1;
}
+ new_bss->ifindex = ifidx;
+ new_bss->drv = drv;
+ new_bss->next = drv->first_bss->next;
+ new_bss->flink = &new_bss->links[0];
+ new_bss->valid_links = 0;
+
+ new_bss->flink->freq = drv->first_bss->flink->freq;
+ new_bss->ctx = bss_ctx;
+ new_bss->added_if = added;
+
+ /* Set interface mode to NL80211_IFTYPE_AP */
+ if (nl80211_set_mode(new_bss, nlmode))
+ return -1;
+
if (bridge &&
i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
@@ -8898,16 +9826,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
}
os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
- new_bss->ifindex = ifidx;
- new_bss->drv = drv;
- new_bss->next = drv->first_bss->next;
- new_bss->flink = &new_bss->links[0];
- new_bss->valid_links = 0;
os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
- new_bss->flink->freq = drv->first_bss->flink->freq;
- new_bss->ctx = bss_ctx;
- new_bss->added_if = added;
drv->first_bss->next = new_bss;
if (drv_priv)
*drv_priv = new_bss;
@@ -8945,6 +9865,9 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
__func__, type, ifname, ifindex, bss->added_if);
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx);
+
if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
nl80211_remove_iface(drv, ifindex);
else if (ifindex > 0 && !bss->added_if) {
@@ -8973,6 +9896,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
bss->brname, strerror(errno));
}
+ bss->in_deinit = 1;
if (bss != drv->first_bss) {
struct i802_bss *tbss;
@@ -9074,14 +9998,22 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
"cookie 0x%llx", no_ack ? " (no ACK)" : "",
(long long unsigned int) cookie);
- if (save_cookie)
- drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
+ if (drv->nlmode == NL80211_IFTYPE_NAN_DATA || no_ack)
+ drv->send_frame_cookie = (u64) -1;
+ else if (save_cookie)
+ drv->send_frame_cookie = cookie;
- if (!wait) {
- /* There is no need to store this cookie since there
- * is no wait that could be canceled later. */
+ /*
+ * Store the cookie only in case there is a wait that needs to
+ * be cancelled later or in case of a NAN Device interface,
+ * where multiple Action frames might be sent, specifically in
+ * cases that the NAN Discovery Engine (NAN DE) is not managed
+ * by the device.
+ */
+ if (!wait && drv->nlmode != NL80211_IFTYPE_NAN &&
+ drv->nlmode != NL80211_IFTYPE_NAN_DATA)
goto fail;
- }
+
if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) {
wpa_printf(MSG_DEBUG,
"nl80211: Drop oldest pending send frame cookie 0x%llx",
@@ -9109,7 +10041,7 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
const u8 *dst, const u8 *src,
const u8 *bssid,
const u8 *data, size_t data_len,
- int no_cck)
+ int no_cck, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
@@ -9121,14 +10053,16 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
bss->flink->beacon_set)
offchanok = 0;
- if (!freq && is_sta_interface(drv->nlmode))
+ if (!freq && (is_sta_interface(drv->nlmode) ||
+ drv->nlmode == NL80211_IFTYPE_NAN ||
+ drv->nlmode == NL80211_IFTYPE_NAN_DATA))
offchanok = 0;
wpa_printf(MSG_DEBUG,
"nl80211: Send Action frame (ifindex=%d, freq=%u MHz wait=%d ms no_cck=%d offchanok=%d dst="
- MACSTR " src=" MACSTR " bssid=" MACSTR ")",
+ MACSTR " src=" MACSTR " bssid=" MACSTR ", link_id=%d)",
drv->ifindex, freq, wait_time, no_cck, offchanok,
- MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
+ MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), link_id);
buf = os_zalloc(24 + data_len);
if (buf == NULL)
@@ -9157,7 +10091,8 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
int i;
modes = nl80211_get_hw_feature_data(bss, &num_modes,
- &flags, &dfs_domain);
+ &flags, &dfs_domain,
+ NULL, 0);
if (dfs_domain != HOSTAPD_DFS_REGION_ETSI &&
ieee80211_is_dfs(bss->flink->freq, modes, num_modes))
offchanok = 0;
@@ -9171,18 +10106,15 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
}
#endif /* CONFIG_MESH */
- if (is_ap_interface(drv->nlmode) &&
- (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
- (int) freq == bss->flink->freq || drv->device_ap_sme ||
- !drv->use_monitor))
+ if (is_ap_interface(drv->nlmode))
ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
0, freq, no_cck, offchanok,
- wait_time, NULL, 0, 0, -1);
+ wait_time, NULL, 0, 0,
+ link_id);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len, 1, no_cck, 0,
- offchanok, NULL, 0,
- NL80211_DRV_LINK_ID_NA);
+ offchanok, NULL, 0, link_id);
os_free(buf);
return ret;
@@ -9217,6 +10149,10 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
unsigned int i;
u64 cookie;
+ /* As there are no waits for NAN Device, nothing to do here */
+ if (drv->nlmode == NL80211_IFTYPE_NAN)
+ return;
+
/* Cancel the last pending TX cookie */
if (drv->send_frame_cookie != (u64) -1)
nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
@@ -9273,7 +10209,8 @@ static int nl80211_put_any_link_id(struct nl_msg *msg,
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
- unsigned int duration)
+ unsigned int duration,
+ const u8 *filter_addr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9289,12 +10226,30 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
return -1;
}
+ /* Add MAC address filter if provided and supported */
+ if (filter_addr) {
+ if (!(drv->capa.flags2 & WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support ROC address filter");
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, filter_addr)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to add MAC address filter");
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
cookie = 0;
ret = send_and_recv_resp(drv, msg, cookie_handler, &cookie);
if (ret == 0) {
- wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
- "0x%llx for freq=%u MHz duration=%u",
- (long long unsigned int) cookie, freq, duration);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remain-on-channel cookie 0x%llx for freq=%u MHz duration=%u%s",
+ (unsigned long long) cookie, freq, duration,
+ filter_addr ? " (with MAC filter)" : "");
drv->remain_on_chan_cookie = cookie;
drv->pending_remain_on_chan = 1;
return 0;
@@ -9442,13 +10397,31 @@ fail:
}
-static int nl80211_remove_link(struct i802_bss *bss, int link_id)
+void nl80211_update_active_links(struct i802_bss *bss, int link_id)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: Update link (ifindex=%d link_id=%u)",
+ bss->ifindex, link_id);
+
+ if (!(bss->active_links & BIT(link_id))) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: Update link: Link not found");
+ return;
+ }
+
+ wpa_driver_nl80211_del_beacon(bss, link_id);
+
+ bss->active_links &= ~BIT(link_id);
+}
+
+
+int nl80211_remove_link(struct i802_bss *bss, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct i802_link *link;
struct nl_msg *msg;
- size_t i;
int ret;
+ u8 link_addr[ETH_ALEN];
+ size_t i;
wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)",
bss->ifindex, link_id);
@@ -9461,15 +10434,22 @@ static int nl80211_remove_link(struct i802_bss *bss, int link_id)
link = &bss->links[link_id];
- wpa_driver_nl80211_del_beacon(bss, link_id);
+ os_memcpy(link_addr, link->addr, ETH_ALEN);
/* First remove the link locally */
- bss->valid_links &= ~BIT(link_id);
os_memset(link->addr, 0, ETH_ALEN);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx);
+ /* Clear the active links and set the flink */
+ nl80211_update_active_links(bss, link_id);
+ bss->valid_links &= ~BIT(link_id);
+
+ if (bss->scan_link == link)
+ bss->scan_link = NULL;
+
/* Choose new deflink if we are removing that link */
if (bss->flink == link) {
- for_each_link_default(bss->valid_links, i, 0) {
+ for_each_link(bss->valid_links, i) {
bss->flink = &bss->links[i];
break;
}
@@ -9500,6 +10480,9 @@ static int nl80211_remove_link(struct i802_bss *bss, int link_id)
"nl80211: remove link (%d) failed. ret=%d (%s)",
link_id, ret, strerror(-ret));
+ if (drv->rtnl_sk)
+ rtnl_neigh_delete_fdb_entry(bss, link_addr, true);
+
return ret;
}
@@ -9648,13 +10631,13 @@ static int get_channel_width(struct nl_msg *msg, void *arg)
}
-static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+static int nl80211_get_channel_width(struct i802_bss *bss,
struct wpa_signal_info *sig)
{
struct nl_msg *msg;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_resp(drv, msg, get_channel_width, sig);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_resp(bss->drv, msg, get_channel_width, sig);
}
@@ -9665,7 +10648,7 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
int res;
os_memset(si, 0, sizeof(*si));
- res = nl80211_get_link_signal(drv, drv->bssid, &si->data);
+ res = nl80211_get_link_signal(bss, drv->bssid, &si->data);
if (res) {
if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
@@ -9673,11 +10656,11 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
si->data.signal = 0;
}
- res = nl80211_get_channel_width(drv, si);
+ res = nl80211_get_channel_width(bss, si);
if (res != 0)
return res;
- return nl80211_get_link_noise(drv, si);
+ return nl80211_get_link_noise(bss, si);
}
@@ -9729,13 +10712,13 @@ static int get_links_noise(struct nl_msg *msg, void *arg)
}
-static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
+static int nl80211_get_links_noise(struct i802_bss *bss,
struct wpa_mlo_signal_info *mlo_sig)
{
struct nl_msg *msg;
- msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_resp(drv, msg, get_links_noise, mlo_sig);
+ msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ return send_and_recv_resp(bss->drv, msg, get_links_noise, mlo_sig);
}
@@ -9783,13 +10766,14 @@ static int get_links_channel_width(struct nl_msg *msg, void *arg)
}
-static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
+static int nl80211_get_links_channel_width(struct i802_bss *bss,
struct wpa_mlo_signal_info *mlo_sig)
{
struct nl_msg *msg;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_resp(drv, msg, get_links_channel_width, mlo_sig);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_resp(bss->drv, msg, get_links_channel_width,
+ mlo_sig);
}
@@ -9809,11 +10793,23 @@ static int nl80211_mlo_signal_poll(void *priv,
mlo_si->valid_links = drv->sta_mlo_info.valid_links;
for_each_link(mlo_si->valid_links, i) {
- res = nl80211_get_link_signal(drv,
+ /*
+ * This should be requesting it not for each link, but for
+ * the MLD address, and then parse out each link data.
+ * However, that API isn't completed in the kernel yet,
+ * and some non-upstream drivers erroneously implemented
+ * a per-link query here. Be compatible with them for now,
+ * but ignore if this fails since that happens upstream.
+ *
+ * Once a correct query is implemented outside the loop,
+ * this should be skipped if the correct query returned
+ * the necessary data.
+ */
+ res = nl80211_get_link_signal(bss,
drv->sta_mlo_info.links[i].bssid,
&mlo_si->links[i].data);
- if (res != 0)
- return res;
+ if (res)
+ mlo_si->links[i].data.signal = -WPA_INVALID_NOISE;
mlo_si->links[i].center_frq1 = -1;
mlo_si->links[i].center_frq2 = -1;
@@ -9822,11 +10818,11 @@ static int nl80211_mlo_signal_poll(void *priv,
mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
}
- res = nl80211_get_links_channel_width(drv, mlo_si);
+ res = nl80211_get_links_channel_width(bss, mlo_si);
if (res != 0)
return res;
- return nl80211_get_links_noise(drv, mlo_si);
+ return nl80211_get_links_noise(bss, mlo_si);
}
@@ -9834,6 +10830,7 @@ static int nl80211_set_param(void *priv, const char *param)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ const char *pos;
if (param == NULL)
return 0;
@@ -9848,12 +10845,14 @@ static int nl80211_set_param(void *priv, const char *param)
}
#endif /* CONFIG_P2P */
- if (os_strstr(param, "use_monitor=1"))
- drv->use_monitor = 1;
-
if (os_strstr(param, "force_connect_cmd=1")) {
drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
drv->force_connect_cmd = 1;
+ /*
+ * mac80211_hwsim does not implement SPP A-MSDU in
+ * offload mode.
+ */
+ drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_SPP_AMSDU;
}
if (os_strstr(param, "force_bss_selection=1"))
@@ -9899,6 +10898,36 @@ static int nl80211_set_param(void *priv, const char *param)
WPA_DRIVER_FLAGS2_SEC_LTF_AP;
}
+ if (os_strstr(param, "rsn_override_in_driver=1"))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
+
+ pos = os_strstr(param, "extra_bss_membership_selectors=");
+ if (pos) {
+ int i = 0;
+
+ pos += 31;
+
+ while (*pos) {
+ char *end;
+ int sel;
+
+ sel = strtol(pos, &end, 10);
+ if (pos == end)
+ return -EINVAL;
+
+ if (sel > 127 || sel < 0)
+ return -EINVAL;
+ if (i ==
+ ARRAY_SIZE(drv->extra_bss_membership_selectors))
+ return -EINVAL;
+ drv->extra_bss_membership_selectors[i++] = sel;
+
+ pos = end;
+ if (*pos == ',')
+ pos++;
+ }
+ }
+
return 0;
}
@@ -9921,6 +10950,7 @@ static void * nl80211_global_init(void *ctx)
global->ctx = ctx;
global->ioctl_sock = -1;
dl_list_init(&global->interfaces);
+ dl_list_init(&global->pending_events);
global->if_add_ifindex = -1;
cfg = os_zalloc(sizeof(*cfg));
@@ -9965,6 +10995,16 @@ static void nl80211_global_deinit(void *priv)
dl_list_len(&global->interfaces));
}
+ eloop_cancel_timeout(nl80211_deliver_pending_events, global, NULL);
+
+ while (!dl_list_empty(&global->pending_events)) {
+ struct nl80211_pending_event *event =
+ dl_list_first(&global->pending_events,
+ struct nl80211_pending_event, list);
+
+ nl80211_remove_pending_event(event);
+ }
+
if (global->netlink)
netlink_deinit(global->netlink);
@@ -9975,6 +11015,11 @@ static void nl80211_global_deinit(void *priv)
nl_cb_put(global->nl_cb);
+#ifdef CONFIG_PR
+ if (global->nl_pr)
+ nl80211_destroy_eloop_handle(&global->nl_pr, 1);
+#endif /* CONFIG_PR */
+
if (global->ioctl_sock >= 0)
close(global->ioctl_sock);
@@ -10249,13 +11294,15 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
int err;
union wpa_event_data data;
struct survey_results *survey_results;
+ void *ctx = (bss->scan_link && bss->scan_link->ctx) ?
+ bss->scan_link->ctx : bss->ctx;
os_memset(&data, 0, sizeof(data));
survey_results = &data.survey_results;
dl_list_init(&survey_results->survey_list);
- msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
if (!msg)
return -ENOBUFS;
@@ -10271,7 +11318,7 @@ 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(ctx, EVENT_SURVEY, &data);
clean_survey_results(survey_results);
return err;
@@ -10452,15 +11499,29 @@ static int nl80211_start_radar_detection(void *priv,
return -1;
}
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
- nl80211_put_freq_params(msg, freq) < 0) {
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_RADAR_DETECT)) ||
+ nl80211_put_freq_params(msg, freq, bss) < 0) {
nlmsg_free(msg);
return -1;
}
+ if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Radar detection (CAC) on link_id=%d",
+ freq->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
ret = send_and_recv_cmd(drv, msg);
- if (ret == 0)
+ if (ret == 0) {
+ nl80211_link_set_freq(bss, freq->link_id, freq->freq);
return 0;
+ }
+
wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
"%d (%s)", ret, strerror(-ret));
return -1;
@@ -10491,8 +11552,7 @@ static int nl80211_add_peer_capab(struct nl_msg *msg,
static int
-nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv,
- int link_id)
+nl80211_tdls_set_discovery_resp_link(struct i802_bss *bss, int link_id)
{
#ifdef CONFIG_DRIVER_NL80211_QCA
struct nl_msg *msg;
@@ -10501,8 +11561,7 @@ nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG, "nl80211: TDLS Discovery Response Tx link ID %u",
link_id);
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT) ||
@@ -10517,7 +11576,7 @@ nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv,
}
nla_nest_end(msg, params);
- return send_and_recv_cmd(drv, msg);
+ return send_and_recv_cmd(bss->drv, msg);
#else /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_ERROR,
"nl80211: Setting TX link for TDLS Discovery Response not supported");
@@ -10542,13 +11601,13 @@ static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
return -EINVAL;
if (link_id >= 0 &&
- nl80211_tdls_set_discovery_resp_link(drv, link_id) < 0)
+ nl80211_tdls_set_discovery_resp_link(bss, link_id) < 0)
return -EOPNOTSUPP;
if (link_id < 0 && drv->sta_mlo_info.valid_links)
link_id = drv->sta_mlo_info.assoc_link_id;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_MGMT)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
@@ -10603,7 +11662,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
return -EINVAL;
}
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_OPER)) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
nlmsg_free(msg);
@@ -10638,7 +11697,7 @@ nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
- (ret = nl80211_put_freq_params(msg, params))) {
+ (ret = nl80211_put_freq_params(msg, params, bss))) {
nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
return ret;
@@ -10744,9 +11803,12 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+
+ if (type == WPA_IF_AP_VLAN)
+ return wpa_driver_nl80211_if_remove(bss, type, ifname);
- if (type != WPA_IF_AP_BSS ||
- !nl80211_link_valid(bss->valid_links, link_id))
+ if (type != WPA_IF_AP_BSS)
return -1;
wpa_printf(MSG_DEBUG,
@@ -10755,26 +11817,39 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
nl80211_remove_link(bss, link_id);
- bss->ctx = bss->flink->ctx;
-
- if (drv->first_bss == bss && bss->valid_links)
+ if (drv->ctx == bss->ctx) {
+ eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill,
+ drv, drv->ctx);
+ bss->ctx = bss->flink->ctx;
drv->ctx = bss->ctx;
+ } else {
+ bss->ctx = bss->flink->ctx;
+ }
if (!bss->valid_links) {
+ void *ctx = bss->ctx;
+
wpa_printf(MSG_DEBUG,
"nl80211: No more links remaining, so remove interface");
- return wpa_driver_nl80211_if_remove(bss, type, ifname);
+ ret = wpa_driver_nl80211_if_remove(bss, type, ifname);
+ if (ret)
+ return ret;
+
+ /* Notify that the MLD interface is removed */
+ wpa_supplicant_event(ctx, EVENT_MLD_INTERFACE_FREED, NULL);
}
return 0;
}
-static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+static bool nl80211_is_drv_shared(void *priv, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- unsigned int num_bss = 0;
+ unsigned int num_bss = 0, num_links = 0;
+ bool self = false;
+ u8 i;
/* If any other BSS exist, someone else is using this since at this
* time, we would have removed all BSSs created by this driver and only
@@ -10789,13 +11864,23 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
/* This is the only BSS present */
bss = priv;
- /* If only one/no link is there no one is sharing */
- if (bss->valid_links <= 1)
+ for_each_link(bss->valid_links, i) {
+ num_links++;
+ if (i == link_id)
+ self = true;
+ }
+
+ /* More than one links means some one is still sharing */
+ if (num_links > 1)
+ return true;
+
+ /* Even if only one link is there, it should match the given
+ * link ID to assert that no one else is sharing. */
+ if (num_links == 1 && self)
return false;
- /* More than one link means someone is still using. To check if
- * only 1 bit is set, power of 2 condition can be checked. */
- if (!(bss->valid_links & (bss->valid_links - 1)))
+ /* No links are active means no one is sharing */
+ if (num_links == 0)
return false;
return true;
@@ -10850,11 +11935,12 @@ static int driver_nl80211_send_action(void *priv, unsigned int freq,
const u8 *dst, const u8 *src,
const u8 *bssid,
const u8 *data, size_t data_len,
- int no_cck)
+ int no_cck, int link_id)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
- bssid, data, data_len, no_cck);
+ bssid, data, data_len, no_cck,
+ link_id);
}
@@ -10875,7 +11961,7 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
u16 mdid = WPA_GET_LE16(md);
wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_FT_IES)) ||
nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
nlmsg_free(msg);
@@ -10926,7 +12012,7 @@ static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
+ if (nl80211_is_netdev_iftype(drv->nlmode))
return NULL;
return bss->addr;
@@ -11013,16 +12099,13 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"prev_bssid=" MACSTR "\n"
"associated=%d\n"
"assoc_freq=%u\n"
- "monitor_sock=%d\n"
- "monitor_ifidx=%d\n"
- "monitor_refcount=%d\n"
"last_mgmt_freq=%u\n"
"eapol_tx_sock=%d\n"
- "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s",
drv->phyname,
MAC2STR(drv->perm_addr),
drv->ifindex,
- drv->operstate,
+ bss->operstate,
scan_state_str(drv->scan_state),
MAC2STR(drv->auth_bssid),
MAC2STR(drv->auth_attempt_bssid),
@@ -11030,12 +12113,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
MAC2STR(drv->prev_bssid),
drv->associated,
drv->assoc_freq,
- drv->monitor_sock,
- drv->monitor_ifidx,
- drv->monitor_refcount,
drv->last_mgmt_freq,
drv->eapol_tx_sock,
- drv->ignore_if_down_event ?
+ bss->ignore_if_down_event ?
"ignore_if_down_event=1\n" : "",
drv->scan_complete_events ?
"scan_complete_events=1\n" : "",
@@ -11050,7 +12130,6 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
drv->data_tx_status ? "data_tx_status=1\n" : "",
drv->scan_for_auth ? "scan_for_auth=1\n" : "",
drv->retry_auth ? "retry_auth=1\n" : "",
- drv->use_monitor ? "use_monitor=1\n" : "",
drv->ignore_next_local_disconnect ?
"ignore_next_local_disconnect\n" : "",
drv->ignore_next_local_deauth ?
@@ -11124,7 +12203,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"capa.max_sched_scan_plan_interval=%u\n"
"capa.max_sched_scan_plan_iterations=%u\n"
"capa.mbssid_max_interfaces=%u\n"
- "capa.ema_max_periodicity=%u\n",
+ "capa.ema_max_periodicity=%u\n"
+ "capa.max_probe_req_ie_len=%zu\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -11149,7 +12229,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
drv->capa.max_sched_scan_plan_interval,
drv->capa.max_sched_scan_plan_iterations,
drv->capa.mbssid_max_interfaces,
- drv->capa.ema_max_periodicity);
+ drv->capa.ema_max_periodicity,
+ drv->capa.max_probe_req_ie_len);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -11175,8 +12256,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
return pos - buf;
}
-
-static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
+static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings,
+ bool skip_mbssid)
{
if ((settings->head &&
nla_put(msg, NL80211_ATTR_BEACON_HEAD,
@@ -11198,6 +12279,11 @@ static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
settings->probe_resp_len, settings->probe_resp)))
return -ENOBUFS;
+#ifdef CONFIG_IEEE80211AX
+ if (!skip_mbssid && nl80211_mbssid(msg, &settings->mbssid) < 0)
+ return -ENOBUFS;
+#endif /* CONFIG_IEEE80211AX */
+
return 0;
}
@@ -11221,7 +12307,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
settings->freq_params.bandwidth,
settings->freq_params.center_freq1,
settings->freq_params.center_freq2,
- settings->punct_bitmap,
+ settings->freq_params.punct_bitmap,
settings->link_id,
settings->freq_params.ht_enabled ? " ht" : "",
settings->freq_params.vht_enabled ? " vht" : "",
@@ -11291,18 +12377,15 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
settings->cs_count) ||
- (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+ (ret = nl80211_put_freq_params(msg, &settings->freq_params, bss)) ||
(settings->block_tx &&
nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
- (settings->punct_bitmap &&
- nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
- settings->punct_bitmap)) ||
(settings->link_id != NL80211_DRV_LINK_ID_NA &&
nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
goto error;
/* beacon_after params */
- ret = set_beacon_data(msg, &settings->beacon_after);
+ ret = set_beacon_data(msg, &settings->beacon_after, false);
if (ret)
goto error;
@@ -11311,7 +12394,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
if (!beacon_csa)
goto fail;
- ret = set_beacon_data(msg, &settings->beacon_csa);
+ ret = set_beacon_data(msg, &settings->beacon_csa, true);
if (ret)
goto error;
@@ -11388,7 +12471,7 @@ static int nl80211_switch_color(void *priv, struct cca_settings *settings)
goto error;
/* beacon_after params */
- ret = set_beacon_data(msg, &settings->beacon_after);
+ ret = set_beacon_data(msg, &settings->beacon_after, false);
if (ret)
goto error;
@@ -11399,7 +12482,7 @@ static int nl80211_switch_color(void *priv, struct cca_settings *settings)
goto error;
}
- ret = set_beacon_data(msg, &settings->beacon_cca);
+ ret = set_beacon_data(msg, &settings->beacon_cca, true);
if (ret)
goto error;
@@ -11606,7 +12689,7 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
/* This test vendor_cmd can be used with nl80211 commands that
* need the connect nl_sock, so use the variant that takes in
* bss->nl_connect as the handle. */
- ret = send_and_recv(drv->global, bss->nl_connect, msg,
+ ret = send_and_recv(drv, bss->nl_connect, msg,
cmd_reply_handler, buf, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
@@ -11693,7 +12776,7 @@ static int nl80211_get_wowlan(void *priv)
wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_WOWLAN);
ret = send_and_recv_resp(drv, msg, get_wowlan_handler, &wowlan_enabled);
if (ret) {
@@ -11766,7 +12849,7 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
return -1;
}
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
@@ -11797,7 +12880,7 @@ static int nl80211_disable_fils(void *priv, int disable)
if (!drv->set_wifi_conf_vendor_cmd_avail)
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
@@ -11832,7 +12915,7 @@ static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
if (!drv->roam_vendor_cmd_avail)
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
@@ -11886,7 +12969,7 @@ static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
@@ -12035,9 +13118,9 @@ static int nl80211_join_mesh(struct i802_bss *bss,
int ret = -1;
wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_MESH);
if (!msg ||
- nl80211_put_freq_params(msg, &params->freq) ||
+ nl80211_put_freq_params(msg, &params->freq, bss) ||
nl80211_put_basic_rates(msg, params->basic_rates) ||
nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
nl80211_put_beacon_int(msg, params->beacon_int) ||
@@ -12081,7 +13164,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
return -1;
- ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
NULL, NULL);
msg = NULL;
if (ret) {
@@ -12091,7 +13174,7 @@ static int nl80211_join_mesh(struct i802_bss *bss,
}
ret = 0;
drv->assoc_freq = bss->flink->freq = params->freq.freq;
- wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+ wpa_printf(MSG_DEBUG, "nl80211: mesh join request sent successfully");
fail:
nlmsg_free(msg);
@@ -12138,15 +13221,15 @@ static int wpa_driver_nl80211_leave_mesh(void *priv)
int ret;
wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
- ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_MESH);
+ ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG,
- "nl80211: mesh leave request send successfully");
+ "nl80211: mesh leave request sent successfully");
drv->first_bss->flink->freq = 0;
}
@@ -12168,7 +13251,7 @@ static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth,
struct nl_msg *msg;
int ret;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_MESH_LINK);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
@@ -12495,7 +13578,7 @@ static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
}
-static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+static int add_freq_list(struct nl_msg *msg, int attr, const int *freq_list)
{
int i, len, ret;
u32 *freqs;
@@ -12508,26 +13591,26 @@ static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
return -1;
for (i = 0; i < len; i++)
freqs[i] = freq_list[i];
- ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
- sizeof(u32) * len, freqs);
+ ret = nla_put(msg, attr, sizeof(u32) * len, freqs);
os_free(freqs);
return ret;
}
-static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv,
+static int nl80211_qca_do_acs(struct i802_bss *bss,
struct drv_acs_params *params)
{
struct nl_msg *msg;
struct nlattr *data;
int ret;
int mode;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
mode = hw_mode_to_qca_acs(params->hw_mode);
if (mode < 0)
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
@@ -12544,7 +13627,8 @@ static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv,
nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
params->ch_width) ||
add_acs_ch_list(msg, params->freq_list) ||
- add_acs_freq_list(msg, params->freq_list) ||
+ add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+ params->freq_list) ||
(params->edmg_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED)) ||
(params->link_id != NL80211_DRV_LINK_ID_NA &&
@@ -12605,7 +13689,7 @@ static int nl80211_set_band(void *priv, u32 band_mask)
"nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
qca_band_mask, qca_band_value);
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
@@ -12802,7 +13886,7 @@ static int nl80211_get_pref_freq_list(void *priv,
param.num = *num;
param.freq_list = freq_list;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -12856,7 +13940,7 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
"nl80211: Set P2P probable operating freq %u for ifindex %d",
freq, bss->ifindex);
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
@@ -12906,7 +13990,7 @@ static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
@@ -12958,7 +14042,7 @@ static int nl80211_p2p_lo_stop(void *priv)
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
@@ -12989,7 +14073,7 @@ static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
else
tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
@@ -13126,7 +14210,7 @@ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
static struct wpa_bss_candidate_info *
-nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+qca_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -13159,7 +14243,7 @@ nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
*/
info->num = params->n_candidates;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
@@ -13233,7 +14317,7 @@ static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
if (!drv->set_wifi_conf_vendor_cmd_avail)
return -1;
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
@@ -13281,7 +14365,7 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: PASN authentication response for %d entries",
params->num_peers);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
if (!msg ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13297,6 +14381,8 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
goto fail;
for (i = 0; i < params->num_peers; i++) {
+ int akmp, cipher;
+
attr1 = nla_nest_start(msg, i);
if (!attr1 ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR,
@@ -13310,12 +14396,33 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
goto fail;
+ akmp = params->peer[i].akmp;
+ cipher = params->peer[i].cipher;
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM,
+ wpa_akm_to_suite(akmp)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER,
+ wpa_cipher_to_cipher_suite(cipher)))
+ goto fail;
+
+ if (params->peer[i].comeback_after &&
+ params->peer[i].comeback_len &&
+ params->peer[i].comeback &&
+ (nla_put_u16(msg,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER,
+ params->peer[i].comeback_after) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE,
+ params->peer[i].comeback_len,
+ params->peer[i].comeback)))
+ goto fail;
+
wpa_printf(MSG_DEBUG,
"nl80211: Own address[%u]: " MACSTR
- " Peer address[%u]: " MACSTR " Status: %s",
+ " Peer address[%u]: " MACSTR
+ " Status: %s AKMP: 0x%x cipher: 0x%x",
i, MAC2STR(params->peer[i].own_addr), i,
MAC2STR(params->peer[i].peer_addr),
- params->peer[i].status ? "Fail" : "Success");
+ params->peer[i].status ? "Fail" : "Success",
+ akmp, cipher);
nla_nest_end(msg, attr1);
}
@@ -13364,7 +14471,7 @@ static int nl80211_set_secure_ranging_ctx(void *priv,
"nl80211: Secure ranging context for " MACSTR,
MAC2STR(params->peer_addr));
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
if (!msg ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13431,6 +14538,318 @@ fail:
#endif /* CONFIG_PASN */
+#ifdef CONFIG_NAN_USD
+
+static int nl80211_nan_flush(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: NAN USD flush");
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH))
+ goto fail;
+
+ nla_nest_end(msg, container);
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD flush");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static const u8 qca_usd_default_network_id[ETH_ALEN] = {
+ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00
+};
+
+static int nl80211_nan_publish(void *priv, const u8 *src, int publish_id,
+ const char *service_name, const u8 *service_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ struct nan_publish_params *params,
+ const u8 *network_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container, *attr;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Start NAN USD publish: default freq=%u, ttl=%u",
+ params->freq, params->ttl);
+ wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR, ETH_ALEN, src) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, publish_id) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID,
+ NAN_SERVICE_ID_LEN, service_id) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE,
+ srv_proto_type) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_USD_TTL, params->ttl) ||
+ (elems && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER,
+ wpabuf_len(elems), wpabuf_head(elems))) ||
+ (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI,
+ wpabuf_len(ssi), wpabuf_head(ssi))) ||
+ (network_id &&
+ !ether_addr_equal(network_id, qca_usd_default_network_id) &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID, ETH_ALEN,
+ network_id)))
+ goto fail;
+
+ attr = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG);
+ if (!attr)
+ goto fail;
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ,
+ params->freq) ||
+ add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST,
+ params->freq_list))
+ goto fail;
+ nla_nest_end(msg, attr);
+
+ nla_nest_end(msg, container);
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD publish");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_nan_cancel_publish(void *priv, int publish_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel publish");
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID,
+ publish_id))
+ goto fail;
+
+ nla_nest_end(msg, container);
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD cancel publish");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_nan_update_publish(void *priv, int publish_id,
+ const struct wpabuf *ssi)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: NAN USD update publish: id=%d",
+ publish_id);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID,
+ publish_id) ||
+ (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI,
+ wpabuf_len(ssi), wpabuf_head(ssi))))
+ goto fail;
+
+ nla_nest_end(msg, container);
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD update publish");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_nan_subscribe(void *priv, const u8 *src, int subscribe_id,
+ const char *service_name, const u8 *service_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ struct nan_subscribe_params *params,
+ const u8 *network_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container, *attr;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Start NAN USD subscribe: freq=%u, ttl=%u",
+ params->freq, params->ttl);
+ wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR, ETH_ALEN, src) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID,
+ subscribe_id) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID,
+ NAN_SERVICE_ID_LEN, service_id) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE,
+ srv_proto_type) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_USD_TTL, params->ttl) ||
+ (elems && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER,
+ wpabuf_len(elems), wpabuf_head(elems))) ||
+ (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI,
+ wpabuf_len(ssi), wpabuf_head(ssi))) ||
+ (network_id &&
+ !ether_addr_equal(network_id, qca_usd_default_network_id) &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID, ETH_ALEN,
+ network_id)))
+ goto fail;
+
+ attr = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG);
+ if (!attr ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ,
+ params->freq) ||
+ add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST,
+ params->freq_list))
+ goto fail;
+ nla_nest_end(msg, attr);
+
+ nla_nest_end(msg, container);
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD subscribe");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_nan_cancel_subscribe(void *priv, int subscribe_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel subscribe");
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_USD))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID,
+ subscribe_id))
+ goto fail;
+
+ nla_nest_end(msg, container);
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to send NAN USD cancel subscribe");
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+#endif /* CONFIG_NAN_USD */
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -13496,7 +14915,7 @@ static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
#ifdef CONFIG_DRIVER_NL80211_QCA
if (drv->qca_do_acs)
- return nl80211_qca_do_acs(drv, params);
+ return nl80211_qca_do_acs(bss, params);
#endif /* CONFIG_DRIVER_NL80211_QCA */
#ifdef CONFIG_DRIVER_NL80211_BRCM
@@ -13508,6 +14927,60 @@ static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
}
+#ifdef CONFIG_MBO
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+ /* This only exists for testing purposes, disable unless requested */
+ if (TEST_FAIL_TAG("simulate")) {
+ struct wpa_bss_candidate_info *info;
+ int i;
+
+ info = os_zalloc(sizeof(*info));
+ if (!info)
+ return NULL;
+
+ info->candidates = os_calloc(params->n_candidates,
+ sizeof(*info->candidates));
+ if (!info->candidates) {
+ os_free(info);
+ return NULL;
+ }
+
+ info->num = params->n_candidates;
+ for (i = 0; i < params->n_candidates; i++) {
+ char bssid_str[ETH_ALEN * 3];
+
+ os_memcpy(info->candidates[i].bssid,
+ &params->bssid[i * ETH_ALEN], ETH_ALEN);
+
+ os_snprintf(bssid_str, sizeof(bssid_str), MACSTR,
+ MAC2STR(info->candidates[i].bssid));
+
+ if (TEST_FAIL_TAG(bssid_str)) {
+ info->candidates[i].is_accept = 0;
+ info->candidates[i].reject_reason =
+ MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+ } else {
+ info->candidates[i].is_accept = 1;
+ info->candidates[i].reject_reason = 0;
+ }
+ }
+
+ return info;
+ }
+#endif /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ return qca_get_bss_transition_status(priv, params);
+#else /* CONFIG_DRIVER_NL80211_QCA */
+ return NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+#endif /* CONFIG_MBO */
+
+
static int nl80211_write_to_file(const char *name, unsigned int val)
{
int fd, len;
@@ -13552,8 +15025,9 @@ static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
char path[128];
int ret;
- /* P2P-Device has no netdev that can (or should) be configured here */
- if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE)
+ /* P2P/NAN-Device has no netdev that can (or should) be configured here
+ */
+ if (!nl80211_is_netdev_iftype(nl80211_get_ifmode(bss)))
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
@@ -13700,7 +15174,7 @@ static int nl80211_update_connection_params(
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
return 0;
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
if (!msg)
goto fail;
@@ -13717,7 +15191,7 @@ static int nl80211_update_connection_params(
if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
type = get_nl_auth_type(params->auth_alg);
- if (type == NL80211_AUTHTYPE_MAX ||
+ if (type > NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
@@ -13757,10 +15231,49 @@ static int nl80211_send_external_auth_status(void *priv,
(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
return -1;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->qca_vendor_ext_auth) {
+ struct nlattr *attr;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: QCA vendor: External auth status: %u",
+ params->status);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr ||
+ nla_put_u16(msg,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE,
+ params->status) ||
+ (params->ssid && params->ssid_len &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID,
+ params->ssid_len, params->ssid)) ||
+ (params->pmkid &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID,
+ PMKID_LEN, params->pmkid)) ||
+ (params->bssid &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID,
+ ETH_ALEN, params->bssid)) ||
+ (params->kck && params->kck_len &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK,
+ params->kck_len, params->kck)))
+ goto fail;
+
+ nla_nest_end(msg, attr);
+ goto send_cmd;
+ }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: External auth status: %u", params->status);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_EXTERNAL_AUTH);
if (!msg ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
(params->ssid && params->ssid_len &&
@@ -13770,6 +15283,11 @@ static int nl80211_send_external_auth_status(void *priv,
(params->bssid &&
nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
goto fail;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+send_cmd:
+ drv->qca_vendor_ext_auth = 0;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
@@ -13878,7 +15396,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
return -EINVAL;
}
- if (bss->valid_links & BIT(link_id)) {
+ if (bss->active_links & BIT(link_id)) {
wpa_printf(MSG_DEBUG,
"nl80211: MLD: Link %u already set", link_id);
return -EINVAL;
@@ -13910,19 +15428,26 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN);
/* The new link is the first one, make it the default */
- if (!bss->valid_links)
+ if (!bss->active_links)
bss->flink = &bss->links[link_id];
bss->valid_links |= BIT(link_id);
+ bss->active_links |= BIT(link_id);
bss->links[link_id].ctx = bss_ctx;
- wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
- bss->valid_links, bss->ifname);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: valid_links=0x%04x active_links=0x%04x on %s",
+ bss->valid_links, bss->active_links, bss->ifname);
+
+ if (drv->rtnl_sk)
+ rtnl_neigh_add_fdb_entry(bss, addr, true);
+
return 0;
}
#ifdef CONFIG_IEEE80211BE
+
static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
const u8 *addr)
{
@@ -13949,6 +15474,195 @@ static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
return ret;
}
+
+
+static int wpa_driver_get_wiphy_name_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_driver_nl80211_data *drv = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_WIPHY_NAME])
+ return NL_SKIP;
+
+ os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+ sizeof(drv->phyname));
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_get_phyname(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ u32 nl_flags = 0;
+ u32 feat = get_nl80211_protocol_features(drv);
+
+ if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+ nl_flags = NLM_F_DUMP;
+
+ if (!(msg = nl80211_cmd_msg(drv->first_bss, nl_flags,
+ NL80211_CMD_GET_WIPHY)) ||
+ nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (send_and_recv_resp(drv, msg, wpa_driver_get_wiphy_name_handler,
+ drv))
+ return -1;
+
+ return 0;
+}
+
+
+static bool
+wpa_driver_nl80211_name_match(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_nl80211_data **match_drv)
+{
+ struct wpa_driver_nl80211_data *drv2;
+
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (os_strcmp(drv2->phyname, drv->phyname) == 0) {
+ if (match_drv)
+ *match_drv = drv2;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static bool wpa_driver_nl80211_can_share_drv(void *ctx,
+ struct wpa_init_params *params,
+ void **hapd)
+{
+ struct wpa_driver_nl80211_data *drv, *match_drv = NULL;
+ struct i802_bss *bss;
+ bool ret = false;
+
+ if (!params->global_priv)
+ return false;
+
+ /* Create a temporary drv to read the phyname */
+ drv = os_zalloc(sizeof(*drv));
+ if (!drv)
+ return false;
+ drv->global = params->global_priv;
+ drv->ctx = ctx;
+
+ drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+ if (!drv->first_bss) {
+ os_free(drv);
+ return false;
+ }
+
+ bss = drv->first_bss;
+ bss->drv = drv;
+ bss->ctx = ctx;
+
+ os_strlcpy(bss->ifname, params->ifname, sizeof(bss->ifname));
+
+ if (nl80211_init_bss(bss))
+ goto free_all;
+
+ drv->ifindex = if_nametoindex(bss->ifname);
+ bss->ifindex = drv->ifindex;
+
+ if (wpa_driver_get_phyname(drv) ||
+ !wpa_driver_nl80211_name_match(drv, &match_drv) ||
+ !match_drv)
+ goto free_all;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Driver for phy %s already exist",
+ match_drv->phyname);
+
+ *hapd = match_drv->first_bss->ctx;
+ ret = true;
+
+free_all:
+ nl80211_destroy_bss(bss);
+ os_free(bss);
+ os_free(drv);
+ return ret;
+}
+
+
+static int
+nl80211_send_link_reconfig_request(void *priv,
+ struct wpa_mlo_reconfig_info *info)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *add_links, *attr;
+ int ret = -1;
+ u8 link_id;
+
+ if (!drv->sta_mlo_info.valid_links)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send ML Link Reconfiguration Request");
+
+ if (info->add_links)
+ wpa_printf(MSG_DEBUG, "Add Setup Links Bitmask: 0x%x",
+ info->add_links);
+
+ if (info->delete_links)
+ wpa_printf(MSG_DEBUG, "Remove Setup Links Bitmask: 0x%x",
+ info->delete_links);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOC_MLO_RECONF);
+ if (!msg)
+ goto error;
+
+ add_links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!add_links)
+ goto error;
+
+ for_each_link(info->add_links, link_id) {
+ attr = nla_nest_start(msg, 0);
+ if (!attr)
+ goto error;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ info->add_link_bssid[link_id]) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ info->add_link_freq[link_id]))
+ goto error;
+
+ nla_nest_end(msg, attr);
+ }
+
+ nla_nest_end(msg, add_links);
+
+ if (nla_put_u16(msg, NL80211_ATTR_MLO_RECONF_REM_LINKS,
+ info->delete_links))
+ goto error;
+
+ ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL,
+ NULL);
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to send Link Reconfiguration Request err=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+ }
+ return 0;
+
+error:
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR,
+ "nl80211: Could not build Link Reconfiguration Request");
+ return ret;
+}
+
#endif /* CONFIG_IEEE80211BE */
@@ -13991,12 +15705,982 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
return wpa_driver_nl80211_mlme(drv, drv->bssid,
NL80211_CMD_DEAUTHENTICATE,
WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
- drv->first_bss);
+ bss);
}
#endif /* CONFIG_TESTING_OPTIONS */
+static struct hostapd_multi_hw_info *
+wpa_driver_get_multi_hw_info(void *priv, unsigned int *num_multi_hws)
+{
+ struct i802_bss *bss = priv;
+
+ return nl80211_get_multi_hw_info(bss, num_multi_hws);
+}
+
+
+#ifdef CONFIG_NAN
+
+static int nl80211_nan_config(struct i802_bss *bss,
+ struct wpa_driver_nl80211_data *drv,
+ const struct nan_cluster_config *params,
+ enum nl80211_commands cmd)
+{
+ struct nl_msg *msg;
+ struct nlattr *conf, *bands_conf, *band;
+ u32 bands = 0;
+
+ if (params->dual_band > 1)
+ return -EINVAL;
+
+ bands |= BIT(NL80211_BAND_2GHZ);
+
+ if (params->dual_band) {
+ if (drv->capa.nan_capa.drv_flags &
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND) {
+ bands |= BIT(NL80211_BAND_5GHZ);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver doesn't support NAN dual band operation");
+ return -EINVAL;
+ }
+ }
+
+ msg = nl80211_cmd_msg(bss, 0, cmd);
+ if (!msg || nla_put_u8(msg, NL80211_ATTR_NAN_MASTER_PREF,
+ params->master_pref) ||
+ (bands && nla_put_u32(msg, NL80211_ATTR_BANDS, bands))) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to build start NAN command");
+ goto fail;
+ }
+
+ conf = nla_nest_start(msg, NL80211_ATTR_NAN_CONFIG);
+ if (!conf)
+ goto fail;
+
+ if (params->enable_dw_notif) {
+ if (!(drv->capa.nan_capa.drv_flags &
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver doesn't support NAN DW notifications");
+ goto fail;
+ }
+
+ if (nla_put_flag(msg, NL80211_NAN_CONF_NOTIFY_DW))
+ goto fail;
+ }
+
+ if (!is_zero_ether_addr(params->cluster_id) &&
+ nla_put(msg, NL80211_NAN_CONF_CLUSTER_ID,
+ ETH_ALEN, params->cluster_id))
+ goto fail;
+
+ if (params->scan_period &&
+ nla_put_u16(msg, NL80211_NAN_CONF_SCAN_PERIOD,
+ params->scan_period))
+ goto fail;
+
+ if (params->scan_dwell_time &&
+ nla_put_u16(msg, NL80211_NAN_CONF_SCAN_DWELL_TIME,
+ params->scan_dwell_time))
+ goto fail;
+
+ if (params->discovery_beacon_interval &&
+ nla_put_u8(msg, NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL,
+ params->discovery_beacon_interval))
+ goto fail;
+
+ bands_conf = nla_nest_start(msg, NL80211_NAN_CONF_BAND_CONFIGS);
+ if (!bands_conf)
+ goto fail;
+
+ band = nla_nest_start(msg, 1);
+ if (!band)
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_NAN_BAND_CONF_BAND,
+ NL80211_BAND_2GHZ) ||
+ nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_CLOSE,
+ params->low_band_cfg.rssi_close) ||
+ nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_MIDDLE,
+ params->low_band_cfg.rssi_middle) ||
+ nla_put_u8(msg, NL80211_NAN_BAND_CONF_WAKE_DW,
+ params->low_band_cfg.awake_dw_interval) ||
+ (params->low_band_cfg.disable_scan &&
+ nla_put_flag(msg, NL80211_NAN_BAND_CONF_DISABLE_SCAN)))
+ goto fail;
+
+ nla_nest_end(msg, band);
+
+ if (params->dual_band) {
+ band = nla_nest_start(msg, 2);
+ if (!band)
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_NAN_BAND_CONF_BAND,
+ NL80211_BAND_5GHZ) ||
+ nla_put_u16(msg, NL80211_NAN_BAND_CONF_FREQ,
+ params->high_band_cfg.frequency) ||
+ nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_CLOSE,
+ params->high_band_cfg.rssi_close) ||
+ nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_MIDDLE,
+ params->high_band_cfg.rssi_middle) ||
+ nla_put_u8(msg, NL80211_NAN_BAND_CONF_WAKE_DW,
+ params->high_band_cfg.awake_dw_interval) ||
+ (params->high_band_cfg.disable_scan &&
+ nla_put_flag(msg, NL80211_NAN_BAND_CONF_DISABLE_SCAN)))
+ goto fail;
+
+ nla_nest_end(msg, band);
+ }
+
+ nla_nest_end(msg, bands_conf);
+
+ if (params->extra_nan_attrs && params->extra_nan_attrs_len &&
+ nla_put(msg, NL80211_NAN_CONF_EXTRA_ATTRS,
+ params->extra_nan_attrs_len,
+ params->extra_nan_attrs))
+ goto fail;
+
+ if (params->vendor_elems && params->vendor_elems_len &&
+ nla_put(msg, NL80211_NAN_CONF_VENDOR_ELEMS,
+ params->vendor_elems_len, params->vendor_elems))
+ goto fail;
+
+ nla_nest_end(msg, conf);
+
+ return send_and_recv_resp(drv, msg, NULL, NULL);
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_nan_start(void *priv,
+ const struct nan_cluster_config *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+
+ if (drv->nlmode != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (drv->nan_started)
+ return -EALREADY;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Start/join NAN cluster");
+ ret = nl80211_nan_config(bss, drv, params, NL80211_CMD_START_NAN);
+ if (!ret)
+ drv->nan_started = 1;
+
+ return ret;
+}
+
+
+static int
+wpa_driver_nl80211_nan_change_config(void *priv,
+ const struct nan_cluster_config *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nlmode != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!drv->nan_started)
+ return -EINVAL;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Update NAN configuration");
+ return nl80211_nan_config(bss, drv, params,
+ NL80211_CMD_CHANGE_NAN_CONFIG);
+}
+
+
+static void wpa_driver_nl80211_nan_stop(void *priv)
+{
+ struct i802_bss *bss = priv;
+
+ nl80211_nan_stop(bss);
+}
+
+
+static int
+wpa_driver_nl80211_nan_set_channel(struct wpa_driver_nl80211_data *drv,
+ struct nan_schedule_channel *chan,
+ struct nl_msg *msg)
+{
+ struct nlattr *chan_attr;
+ enum nl80211_chan_width cw;
+
+ chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL);
+ if (!chan)
+ return -ENOBUFS;
+
+ if (nl80211_bw_to_nl(chan->bandwidth, &cw, chan->center_freq2))
+ return -EINVAL;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->freq) ||
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chan->center_freq1) ||
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chan->center_freq2) ||
+ nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+ nla_put(msg, NL80211_ATTR_NAN_CHANNEL_ENTRY,
+ sizeof(chan->chan_entry), chan->chan_entry)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Failed to put channel attributes");
+ return -EINVAL;
+ }
+
+ if (chan->rx_nss &&
+ nla_put_u8(msg, NL80211_ATTR_NAN_RX_NSS, chan->rx_nss)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Failed to put RX NSS attribute");
+ return -EINVAL;
+ }
+
+ nla_nest_end(msg, chan_attr);
+ return 0;
+}
+
+
+static int
+wpa_driver_nl80211_nan_set_slots(struct wpa_driver_nl80211_data *drv,
+ struct wpabuf *chan_time_bitmap,
+ u8 *slots, u8 chan_idx)
+{
+ const u8 *time_bitmap;
+ size_t time_bitmap_len, idx;
+
+ if (!chan_time_bitmap)
+ return 0;
+
+ time_bitmap = wpabuf_head(chan_time_bitmap);
+ time_bitmap_len = wpabuf_len(chan_time_bitmap);
+ if (!time_bitmap || !time_bitmap_len)
+ return 0;
+
+ /*
+ * nl80211 always uses a period of 512 TUs with slot duration of
+ * 16 TUs, so the map length must be equal to 4.
+ */
+ if (time_bitmap_len != 4) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Invalid time bitmap len=%zu",
+ time_bitmap_len);
+ return -EINVAL;
+ }
+
+ for (idx = 0; idx < time_bitmap_len * 8; idx++) {
+ size_t byte_idx = idx / 8;
+ size_t bit_idx = idx % 8;
+
+ if (time_bitmap[byte_idx] & BIT(bit_idx))
+ slots[idx] = chan_idx;
+ }
+
+ return 0;
+}
+
+
+static int
+wpa_driver_nl80211_nan_set_schedule(struct wpa_driver_nl80211_data *drv,
+ struct nan_schedule_config *sched,
+ struct nl_msg *msg)
+{
+ u8 slots[32];
+ size_t i;
+
+ os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT, sizeof(slots));
+
+ for (i = 0; i < sched->num_channels; i++) {
+ int ret;
+
+ ret = wpa_driver_nl80211_nan_set_channel(
+ drv, &sched->channels[i], msg);
+ if (ret)
+ return ret;
+
+ ret = wpa_driver_nl80211_nan_set_slots(
+ drv, sched->channels[i].time_bitmap, slots, i);
+ if (ret)
+ return ret;
+ }
+
+ return nla_put(msg, NL80211_ATTR_NAN_TIME_SLOTS, sizeof(slots), slots);
+}
+
+
+static int
+wpa_driver_nl80211_nan_config_schedule(void *priv, u8 map_id,
+ struct nan_schedule_config *sched)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started)
+ return -EOPNOTSUPP;
+
+ if (!sched)
+ return -EINVAL;
+
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_LOCAL_SCHED);
+ if (!msg) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to alloc NAN local schedule command");
+ return -ENOMEM;
+ }
+
+ if (sched->deferred &&
+ nla_put_flag(msg, NL80211_ATTR_NAN_SCHED_DEFERRED)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Failed to put deferred attribute");
+ goto fail;
+ }
+
+ ret = wpa_driver_nl80211_nan_set_schedule(drv, sched, msg);
+ if (ret)
+ goto fail;
+
+ if (sched->avail_attr &&
+ nl80211_attr_supported(drv, NL80211_ATTR_NAN_AVAIL_BLOB) &&
+ nla_put(msg, NL80211_ATTR_NAN_AVAIL_BLOB,
+ wpabuf_len(sched->avail_attr),
+ wpabuf_head(sched->avail_attr)))
+ goto fail;
+
+ ret = send_and_recv_cmd(bss->drv, msg);
+ if (ret)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to send NAN local schedule command: %d",
+ ret);
+
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int
+wpa_driver_nl80211_nan_config_peer_schedule(
+ void *priv, const u8 *peer, u16 cdw, u8 sequence_id,
+ u16 max_chan_switch_time, const struct wpabuf *ulw,
+ struct nan_peer_schedule_config *sched)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nan_schedule_channel *channels[MAX_NUM_NAN_MAPS *
+ MAX_NUM_NAN_SCHEDULE_CHANNELS];
+ u8 chan_remap[MAX_NUM_NAN_MAPS][MAX_NUM_NAN_SCHEDULE_CHANNELS];
+ unsigned int map_idx, chan_idx, n_channels = 0;
+ struct nl_msg *msg;
+ struct nlattr *maps;
+ int ret;
+
+ if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started)
+ return -EOPNOTSUPP;
+
+ if (!sched)
+ return -EINVAL;
+
+ os_memset(channels, 0, sizeof(channels));
+ os_memset(chan_remap, 0, sizeof(chan_remap));
+
+ /*
+ * Kernel expects channels at the top level, and peer maps reference
+ * them by index. Build a global channel list from all maps.
+ */
+ for (map_idx = 0; map_idx < sched->n_maps; map_idx++) {
+ struct nan_schedule_map *map = &sched->maps[map_idx];
+
+ for (chan_idx = 0; chan_idx < map->sched.num_channels;
+ chan_idx++) {
+ unsigned int i;
+
+ /* Check if we already have this channel */
+ for (i = 0; i < n_channels; i++) {
+ if (os_memcmp(&map->sched.channels[chan_idx].chan_entry,
+ channels[i]->chan_entry,
+ sizeof(channels[i]->chan_entry)) == 0)
+ break;
+ }
+
+ if (i < n_channels) {
+ chan_remap[map_idx][chan_idx] = i;
+ continue;
+ }
+
+ channels[n_channels] = &map->sched.channels[chan_idx];
+ chan_remap[map_idx][chan_idx] = n_channels++;
+ }
+ }
+
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_PEER_SCHED);
+ if (!msg) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to alloc NAN peer schedule command");
+ return -ENOMEM;
+ }
+
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+ nla_put_u16(msg, NL80211_ATTR_NAN_COMMITTED_DW, cdw) ||
+ nla_put_u16(msg, NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME,
+ max_chan_switch_time) ||
+ (sched->n_maps &&
+ nla_put_u8(msg, NL80211_ATTR_NAN_SEQ_ID, sequence_id))) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Failed to put peer schedule attributes");
+ goto fail;
+ }
+
+ if (ulw && wpabuf_len(ulw)) {
+ if (nla_put(msg, NL80211_ATTR_NAN_ULW,
+ wpabuf_len(ulw), wpabuf_head(ulw))) {
+ wpa_printf(MSG_INFO,
+ "nl80211: NAN: Failed to put ULW attribute");
+ goto fail;
+ }
+ }
+
+ /* Set the channels */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN: Setting %u channels for peer schedule",
+ n_channels);
+
+ for (chan_idx = 0; chan_idx < n_channels; chan_idx++) {
+ ret = wpa_driver_nl80211_nan_set_channel(drv,
+ channels[chan_idx],
+ msg);
+ if (ret)
+ goto fail;
+ }
+
+ /* Set the maps */
+ maps = nla_nest_start(msg, NL80211_ATTR_NAN_PEER_MAPS);
+ if (!maps)
+ goto fail;
+
+ for (map_idx = 0; map_idx < sched->n_maps; map_idx++) {
+ struct nlattr *map = nla_nest_start(msg, map_idx + 1);
+ u8 slots[32];
+
+ if (!map)
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_NAN_PEER_MAP_ATTR_MAP_ID,
+ sched->maps[map_idx].map_id))
+ goto fail;
+
+ os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT,
+ sizeof(slots));
+
+ for (chan_idx = 0;
+ chan_idx < sched->maps[map_idx].sched.num_channels;
+ chan_idx++) {
+ struct nan_schedule_channel *chan =
+ &sched->maps[map_idx].sched.channels[chan_idx];
+
+ ret = wpa_driver_nl80211_nan_set_slots(
+ drv, chan->time_bitmap, slots,
+ chan_remap[map_idx][chan_idx]);
+ if (ret)
+ goto fail;
+ }
+
+ if (nla_put(msg, NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS,
+ sizeof(slots), slots))
+ goto fail;
+
+ nla_nest_end(msg, map);
+ }
+
+ nla_nest_end(msg, maps);
+
+ ret = send_and_recv_cmd(bss->drv, msg);
+ if (ret)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to send NAN peer schedule command: %d",
+ ret);
+
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+#endif /* CONFIG_NAN */
+
+
+#ifdef CONFIG_PR
+
+/**
+ * nl80211_pd_start - Create interface for Proximity Detection
+ */
+static int nl80211_pd_start(void *priv, const u8 *addr, u8 *pd_addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_bss *pd_bss;
+ struct wdev_info info;
+ int ret;
+
+ if (drv->pd_bss) {
+ wpa_printf(MSG_DEBUG, "nl80211: PD wdev already active");
+ if (pd_addr)
+ os_memcpy(pd_addr, drv->pd_bss->addr, ETH_ALEN);
+ return 0;
+ }
+
+ pd_bss = os_zalloc(sizeof(*pd_bss));
+ if (!pd_bss)
+ return -1;
+
+ pd_bss->drv = drv;
+ pd_bss->ctx = bss->ctx; /* parent wpa_s */
+ pd_bss->flink = &pd_bss->links[0];
+ os_strlcpy(pd_bss->ifname, "pd-wdev", sizeof(pd_bss->ifname));
+
+ os_memset(&info, 0, sizeof(info));
+ ret = nl80211_create_iface(drv, "pd-wdev", NL80211_IFTYPE_PD, addr,
+ 0, nl80211_wdev_handler, &info, 0);
+ if (ret || !info.wdev_id_set) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to create PD wdev (ret=%d)", ret);
+ os_free(pd_bss);
+ return -1;
+ }
+
+ pd_bss->wdev_id = info.wdev_id;
+ pd_bss->wdev_id_set = 1;
+ if (!is_zero_ether_addr(info.macaddr))
+ os_memcpy(pd_bss->addr, info.macaddr, ETH_ALEN);
+ else if (addr)
+ os_memcpy(pd_bss->addr, addr, ETH_ALEN);
+ os_memcpy(pd_bss->flink->addr, pd_bss->addr, ETH_ALEN);
+
+ /* Activate the PD device */
+ ret = nl80211_set_pr_dev(pd_bss, 1);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to start PD device (ret=%d)", ret);
+ goto failed;
+ }
+
+ if (nl80211_init_bss(pd_bss)) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to init PD BSS");
+ goto failed_stop;
+ }
+
+ if (nl80211_mgmt_subscribe_non_ap(pd_bss))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to register frame processing for PD interface - ignore for now");
+
+ drv->pd_bss = pd_bss;
+
+ if (pd_addr)
+ os_memcpy(pd_addr, pd_bss->addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: PD wdev created wdev_id=0x%llx addr=" MACSTR,
+ (unsigned long long) pd_bss->wdev_id,
+ MAC2STR(pd_bss->addr));
+ return 0;
+
+failed_stop:
+ nl80211_set_pr_dev(pd_bss, 0);
+failed:
+ nl80211_del_non_netdev(pd_bss);
+ os_free(pd_bss);
+ return -1;
+}
+
+
+/**
+ * nl80211_pd_stop - Stop interface for Proximity Detection
+ */
+static void nl80211_pd_stop(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->pd_bss)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Stopping PD wdev wdev_id=0x%llx addr=" MACSTR,
+ (unsigned long long) drv->pd_bss->wdev_id,
+ MAC2STR(drv->pd_bss->addr));
+
+ nl80211_set_pr_dev(drv->pd_bss, 0);
+ nl80211_destroy_bss(drv->pd_bss);
+ nl80211_del_non_netdev(drv->pd_bss);
+ os_free(drv->pd_bss);
+ drv->pd_bss = NULL;
+}
+
+
+static u8 get_pr_preamble(u8 ranging_type, u8 format_bw)
+{
+ /* Determine preamble based on ranging type and format_bw */
+ if (ranging_type & PR_EDCA_BASED_RANGING) {
+ /* EDCA format_bw maps to different preambles */
+ switch (format_bw) {
+ case EDCA_FORMAT_AND_BW_HT40:
+ return NL80211_PREAMBLE_HT;
+ case EDCA_FORMAT_AND_BW_VHT20:
+ case EDCA_FORMAT_AND_BW_VHT40:
+ case EDCA_FORMAT_AND_BW_VHT80:
+ case EDCA_FORMAT_AND_BW_VHT80P80:
+ case EDCA_FORMAT_AND_BW_VHT160_DUAL_LO:
+ case EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO:
+ return NL80211_PREAMBLE_VHT;
+ default:
+ return NL80211_PREAMBLE_VHT;
+ }
+ } else if (ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING |
+ PR_NTB_OPEN_BASED_RANGING)) {
+ /* NTB format_bw all use HE preamble */
+ return NL80211_PREAMBLE_HE;
+ }
+
+ /* Default fallback */
+ return NL80211_PREAMBLE_VHT;
+}
+
+
+/**
+ * nl80211_start_peer_measurement - Send NL80211_CMD_PEER_MEASUREMENT_START
+ */
+static int nl80211_start_peer_measurement(void *priv, const u8 *peer_addr,
+ int freq, u8 channel, int bw,
+ struct pr_pasn_ranging_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *pmsr_attr, *peers_attr, *peer_attr, *chan_attr,
+ *req_attr, *ftm_attr;
+ struct nlattr *data_attr;
+ int ret = -1, center_freq1 = 0, center_freq2 = 0;
+ struct nl80211_ack_ext_arg ack_arg;
+ enum nl80211_chan_width width;
+ u32 preamble;
+ u64 cookie;
+
+ if (!peer_addr || !params) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Invalid parameters for peer measurement");
+ return -1;
+ }
+
+ /* Create dedicated ranging socket if not already created */
+ if (drv->global->nl_pr) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: PR ranging socket already in use");
+ return -1;
+ }
+
+ drv->global->nl_pr = nl_create_handle(drv->global->nl_cb, "pr");
+ if (!drv->global->nl_pr) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to create PR ranging socket");
+ return -1;
+ }
+
+ /* Route via PD wdev if src_addr matches */
+ if (drv->pd_bss && !is_zero_ether_addr(params->src_addr) &&
+ ether_addr_equal(params->src_addr, drv->pd_bss->addr)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Peer measurement routed via PD wdev addr="
+ MACSTR, MAC2STR(drv->pd_bss->addr));
+ bss = drv->pd_bss;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Start peer measurement for " MACSTR
+ " freq=%d ch=%u bw=%d",
+ MAC2STR(peer_addr), freq, channel, bw);
+
+ /* Use center frequencies from params (calculated in
+ * wpas_pr_trigger_ranging()) */
+ center_freq1 = params->center_freq1;
+ center_freq2 = params->center_freq2;
+
+ /* channel_width is in MHz */
+ switch (params->channel_width) {
+ case 20:
+ width = NL80211_CHAN_WIDTH_20;
+ break;
+ case 40:
+ width = NL80211_CHAN_WIDTH_40;
+ break;
+ case 80:
+ width = NL80211_CHAN_WIDTH_80;
+ break;
+ case 160:
+ width = NL80211_CHAN_WIDTH_160;
+ break;
+ case 320:
+ width = NL80211_CHAN_WIDTH_320;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "nl80211: Unsupported channel width %u MHz",
+ params->channel_width);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Using center_freq1=%u, center_freq2=%u, width=%d",
+ center_freq1, center_freq2, width);
+
+ /*
+ * PD interfaces are non-netdev (ifindex=0); use nl80211_cmd_msg()
+ * which emits NL80211_ATTR_WDEV instead of NL80211_ATTR_IFINDEX.
+ */
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_PEER_MEASUREMENT_START);
+ if (!msg)
+ return -1;
+
+ /* Add timeout if specified */
+ if (params->ranging_timeout > 0) {
+ if (nla_put_u32(msg, NL80211_ATTR_TIMEOUT,
+ params->ranging_timeout))
+ goto fail;
+ wpa_printf(MSG_DEBUG, "nl80211: Added timeout %u ms",
+ params->ranging_timeout);
+ }
+
+ /* Add peer measurements attribute */
+ pmsr_attr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
+ if (!pmsr_attr)
+ goto fail;
+
+ /* Add peers array */
+ peers_attr = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
+ if (!peers_attr)
+ goto fail;
+
+ /* Add single peer */
+ peer_attr = nla_nest_start(msg, 0);
+ if (!peer_attr)
+ goto fail;
+
+ /* Peer MAC address */
+ if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, peer_addr))
+ goto fail;
+
+ /* Add proximity detection request type */
+ if (nla_put_u32(msg, NL80211_PMSR_PEER_ATTR_REQ_TYPE,
+ NL80211_PMSR_FTM_REQ_TYPE_PD))
+ goto fail;
+
+ /* Channel information */
+ chan_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_CHAN);
+ if (!chan_attr)
+ goto fail;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+ nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, width) ||
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq1))
+ goto fail;
+
+ if (center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, center_freq2))
+ goto fail;
+
+ nla_nest_end(msg, chan_attr);
+
+
+ /* Request attributes */
+ req_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_REQ);
+ if (!req_attr)
+ goto fail;
+
+ /* Add data attribute as required by nl80211 specification */
+ data_attr = nla_nest_start(msg, NL80211_PMSR_REQ_ATTR_DATA);
+ if (!data_attr)
+ goto fail;
+
+ /* FTM request */
+ ftm_attr = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
+ if (!ftm_attr)
+ goto fail;
+
+ preamble = get_pr_preamble(params->ranging_type, params->format_bw);
+ if (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, preamble))
+ goto fail;
+
+ /* Map ranging parameters to NL80211 attributes based on protocol type
+ */
+ if (params->ranging_type & PR_EDCA_BASED_RANGING) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Adding EDCA ranging parameters");
+
+ /* ASAP and BURST_PERIOD are always set for EDCA */
+ if (nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_ASAP) ||
+ nla_put_u16(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+ params->burst_period))
+ goto fail;
+
+ /* Other EDCA parameters are only set for ISTA */
+ if (params->ranging_role == PR_ISTA_SUPPORT &&
+ (nla_put_u8(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+ params->num_bursts_exp) ||
+ nla_put_u8(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+ params->ftms_per_burst) ||
+ nla_put_u8(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+ params->ftmr_retries) ||
+ nla_put_u8(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+ params->burst_duration)))
+ goto fail;
+ }
+
+ if (params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING |
+ PR_NTB_OPEN_BASED_RANGING)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Adding NTB ranging parameters");
+
+ /* Set non-trigger based flag */
+ if (nla_put_flag(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED))
+ goto fail;
+
+ if (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME,
+ params->nominal_time))
+ goto fail;
+
+ /* ISTA-specific NTB parameters */
+ if (params->ranging_role == PR_ISTA_SUPPORT &&
+ (nla_put_u32(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS,
+ params->min_time_between_measurements) ||
+ nla_put_u32(msg,
+ NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS,
+ params->max_time_between_measurements) ||
+ nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION,
+ params->availability_window) ||
+ nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+ params->ftms_per_burst)))
+ goto fail;
+ }
+
+ /* Location requests */
+ if (params->request_lci &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI))
+ goto fail;
+
+ if (params->request_civicloc &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC))
+ goto fail;
+
+ /* RSTA mode if responder role */
+ if (params->ranging_role == PR_RSTA_SUPPORT &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_RSTA))
+ goto fail;
+
+ /*
+ * LMR feedback: set for RSTA role in NTB ranging so that
+ * the RSTA reports its measurement results back to the ISTA.
+ * Only valid when NON_TRIGGER_BASED is set.
+ */
+ if (params->ranging_role == PR_RSTA_SUPPORT &&
+ (params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING |
+ PR_NTB_OPEN_BASED_RANGING)) &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK))
+ goto fail;
+
+ /*
+ * PD ingress/egress thresholds: only valid when
+ * NL80211_PMSR_PEER_ATTR_REQ_TYPE is NL80211_PMSR_FTM_REQ_TYPE_PD
+ * (always the case here). Thresholds are in millimeters.
+ */
+ if (params->ingress_threshold &&
+ nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_INGRESS,
+ params->ingress_threshold))
+ goto fail;
+
+ if (params->egress_threshold &&
+ nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_EGRESS,
+ params->egress_threshold))
+ goto fail;
+
+ /*
+ * PD suppress results: suppress ranging results for PD requests.
+ */
+ if (params->pr_suppress_results &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS))
+ goto fail;
+
+ nla_nest_end(msg, ftm_attr);
+ nla_nest_end(msg, data_attr);
+ nla_nest_end(msg, req_attr);
+ nla_nest_end(msg, peer_attr);
+ nla_nest_end(msg, peers_attr);
+ nla_nest_end(msg, pmsr_attr);
+
+ cookie = 0;
+ os_memset(&ack_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+ ack_arg.ext_data = &cookie;
+ ret = send_and_recv(drv, drv->global->nl_pr, msg, NULL, NULL,
+ ack_handler_cookie, &ack_arg, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Peer measurement start failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ nl_destroy_handles(&drv->global->nl_pr);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Peer measurement started successfully addr="
+ MACSTR " cookie=%llu", MAC2STR(peer_addr),
+ (unsigned long long) cookie);
+ params->cookie = cookie;
+ /*
+ * Register the PR socket with eloop so that
+ * NL80211_CMD_PEER_MEASUREMENT_RESULT and
+ * NL80211_CMD_PEER_MEASUREMENT_COMPLETE events are delivered
+ * to the existing nl80211 event handler.
+ */
+ nl80211_register_eloop_read(&drv->global->nl_pr,
+ wpa_driver_nl80211_event_receive,
+ drv->global->nl_cb, 1);
+ }
+
+ return ret;
+
+fail:
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to build peer measurement message");
+ nlmsg_free(msg);
+ nl_destroy_handles(&drv->global->nl_pr);
+ return -1;
+}
+
+
+static void nl80211_stop_peer_measurement(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->global->nl_pr)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Stopping PR ranging socket");
+ nl80211_destroy_eloop_handle(&drv->global->nl_pr, 1);
+}
+
+#endif /* CONFIG_PR */
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -14124,7 +16808,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_default_scan_ies = nl80211_set_default_scan_ies,
.set_tdls_mode = nl80211_set_tdls_mode,
#ifdef CONFIG_MBO
- .get_bss_transition_status = nl80211_get_bss_transition_status,
.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
#endif /* CONFIG_MBO */
.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
@@ -14133,8 +16816,19 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.send_pasn_resp = nl80211_send_pasn_resp,
.set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx,
#endif /* CONFIG_PASN */
+#ifdef CONFIG_NAN_USD
+ .nan_flush = nl80211_nan_flush,
+ .nan_publish = nl80211_nan_publish,
+ .nan_cancel_publish = nl80211_nan_cancel_publish,
+ .nan_update_publish = nl80211_nan_update_publish,
+ .nan_subscribe = nl80211_nan_subscribe,
+ .nan_cancel_subscribe = nl80211_nan_cancel_subscribe,
+#endif /* CONFIG_NAN_USD */
#endif /* CONFIG_DRIVER_NL80211_QCA */
.do_acs = nl80211_do_acs,
+#ifdef CONFIG_MBO
+ .get_bss_transition_status = nl80211_get_bss_transition_status,
+#endif /* CONFIG_MBO */
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
.get_mld_capab = nl80211_get_mld_capab,
@@ -14150,9 +16844,25 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.link_remove = driver_nl80211_link_remove,
.is_drv_shared = nl80211_is_drv_shared,
.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+ .can_share_drv = wpa_driver_nl80211_can_share_drv,
+ .setup_link_reconfig = nl80211_send_link_reconfig_request,
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
.radio_disable = testing_nl80211_radio_disable,
#endif /* CONFIG_TESTING_OPTIONS */
+ .get_multi_hw_info = wpa_driver_get_multi_hw_info,
+#ifdef CONFIG_NAN
+ .nan_start = wpa_driver_nl80211_nan_start,
+ .nan_stop = wpa_driver_nl80211_nan_stop,
+ .nan_change_config = wpa_driver_nl80211_nan_change_config,
+ .nan_config_schedule = wpa_driver_nl80211_nan_config_schedule,
+ .nan_config_peer_schedule = wpa_driver_nl80211_nan_config_peer_schedule,
+#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ .pd_start = nl80211_pd_start,
+ .pd_stop = nl80211_pd_stop,
+ .start_peer_measurement = nl80211_start_peer_measurement,
+ .stop_peer_measurement = nl80211_stop_peer_measurement,
+#endif /* CONFIG_PR */
};
diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h
index 618746e67722..580de5a3a95e 100644
--- a/contrib/wpa/src/drivers/driver_nl80211.h
+++ b/contrib/wpa/src/drivers/driver_nl80211.h
@@ -22,6 +22,8 @@
nla_nest_start(msg, NLA_F_NESTED | (attrtype))
#endif
+struct nl_msg;
+
struct nl80211_global {
void *ctx;
struct dl_list interfaces;
@@ -37,6 +39,25 @@ struct nl80211_global {
int ioctl_sock; /* socket for ioctl() use */
struct nl_sock *nl_event;
+
+ /* Handling of sync replies */
+ bool sync_reply_handling;
+ u32 reply_seq;
+ int (*reply_handler)(struct nl_msg *, void *);
+ void *reply_data;
+
+ /* pending events that happened while waiting for a sync reply */
+ struct dl_list pending_events;
+ /* set while nl80211_deliver_pending_events() is running */
+ bool delivering_pending_events;
+#ifdef CONFIG_NAN
+ /* Dedicated socket for NAN interface creation and events */
+ struct nl_sock *nl_nan;
+#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ /* Dedicated socket for PR peer measurement commands and events */
+ struct nl_sock *nl_pr;
+#endif /* CONFIG_PR */
};
struct nl80211_wiphy_data {
@@ -65,12 +86,18 @@ struct i802_bss {
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
+ /* The links which are physically present */
u16 valid_links;
+ /* The links which are in UP state */
+ u16 active_links;
struct i802_link links[MAX_NUM_MLD_LINKS];
- struct i802_link *flink;
+ struct i802_link *flink, *scan_link;
int ifindex;
+ int ignore_if_down_event;
int br_ifindex;
+ int if_removed;
+ int if_disabled;
u64 wdev_id;
char ifname[IFNAMSIZ + 1];
char brname[IFNAMSIZ];
@@ -86,6 +113,7 @@ struct i802_bss {
u8 prev_addr[ETH_ALEN];
int if_dynamic;
+ int operstate;
void *ctx;
struct nl_sock *nl_preq, *nl_mgmt, *nl_connect;
@@ -94,6 +122,8 @@ struct i802_bss {
struct nl80211_wiphy_data *wiphy_data;
struct dl_list wiphy_list;
u8 rand_addr[ETH_ALEN];
+
+ unsigned int start_iface_up:1;
};
struct drv_nl80211_if_info {
@@ -111,9 +141,6 @@ struct wpa_driver_nl80211_data {
u8 perm_addr[ETH_ALEN];
void *ctx;
int ifindex;
- int if_removed;
- int if_disabled;
- int ignore_if_down_event;
struct rfkill_data *rfkill;
struct wpa_driver_capa capa;
u8 *extended_capa, *extended_capa_mask;
@@ -126,12 +153,10 @@ struct wpa_driver_nl80211_data {
u16 mld_capa_and_ops;
} iface_capa[NL80211_IFTYPE_MAX];
unsigned int num_iface_capa;
+ unsigned int unique_drv_id;
int has_capability;
int has_driver_key_mgmt;
-
- int operstate;
-
int scan_complete_events;
enum scan_states {
NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
@@ -151,10 +176,6 @@ struct wpa_driver_nl80211_data {
enum nl80211_iftype ap_scan_as_station;
unsigned int assoc_freq;
- int monitor_sock;
- int monitor_ifidx;
- int monitor_refcount;
-
unsigned int disabled_11b_rates:1;
unsigned int pending_remain_on_chan:1;
unsigned int in_interface_list:1;
@@ -163,10 +184,8 @@ struct wpa_driver_nl80211_data {
unsigned int data_tx_status:1;
unsigned int scan_for_auth:1;
unsigned int retry_auth:1;
- unsigned int use_monitor:1;
unsigned int hostapd:1;
unsigned int start_mode_sta:1;
- unsigned int start_iface_up:1;
unsigned int test_use_roc_tx:1;
unsigned int ignore_deauth_event:1;
unsigned int vendor_cmd_test_avail:1;
@@ -200,6 +219,10 @@ struct wpa_driver_nl80211_data {
unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
unsigned int puncturing:1;
unsigned int qca_ap_allowed_freqs:1;
+ unsigned int connect_ext_vendor_cmd_avail:1;
+ unsigned int support_ap_scan:1;
+
+ u8 extra_bss_membership_selectors[8];
u32 ignore_next_local_disconnect;
u32 ignore_next_local_deauth;
@@ -264,10 +287,16 @@ struct wpa_driver_nl80211_data {
size_t pending_t2lm_data_len;
u8 *pending_link_reconfig_data;
size_t pending_link_reconfig_data_len;
+ unsigned int qca_vendor_ext_auth:1;
#endif /* CONFIG_DRIVER_NL80211_QCA */
-};
-struct nl_msg;
+#ifdef CONFIG_NAN
+ unsigned int nan_started:1;
+#endif /* CONFIG_NAN */
+#ifdef CONFIG_PR
+ struct i802_bss *pd_bss; /* PD wdev; not in the BSS list */
+#endif /* CONFIG_PR */
+};
struct nl80211_err_info {
int link_id;
@@ -280,19 +309,38 @@ struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
uint8_t cmd);
struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
-int send_and_recv(struct nl80211_global *global,
- struct nl_sock *nl_handle, struct nl_msg *msg,
- int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data,
- int (*ack_handler_custom)(struct nl_msg *, void *),
- void *ack_data,
- struct nl80211_err_info *err_info);
+int nl80211_reply_hook(struct nl80211_global *global, struct nl_msg *msg,
+ int (*delayed_handler)(struct nl_msg *, void *),
+ void *delayed_data);
+
+int send_and_recv_glb(struct nl80211_global *global,
+ struct wpa_driver_nl80211_data *drv, /* may be NULL */
+ struct nl_sock *nl_handle, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data,
+ struct nl80211_err_info *err_info);
+
+static inline int
+send_and_recv(struct wpa_driver_nl80211_data *drv,
+ struct nl_sock *nl_handle, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data,
+ struct nl80211_err_info *err_info)
+{
+ return send_and_recv_glb(drv->global, drv, nl_handle, msg,
+ valid_handler, valid_data,
+ ack_handler_custom, ack_data, err_info);
+}
static inline int
send_and_recv_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg)
{
- return send_and_recv(drv->global, drv->global->nl, msg,
+ return send_and_recv(drv, drv->global->nl, msg,
NULL, NULL, NULL, NULL, NULL);
}
@@ -302,7 +350,7 @@ send_and_recv_resp(struct wpa_driver_nl80211_data *drv,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data)
{
- return send_and_recv(drv->global, drv->global->nl, msg,
+ return send_and_recv(drv, drv->global->nl, msg,
valid_handler, valid_data, NULL, NULL, NULL);
}
@@ -313,6 +361,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
void *arg, int use_existing);
void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+const u8 * nl80211_get_assoc_bssid(struct i802_bss *bss);
int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
enum chan_width convert2width(int width);
void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
@@ -321,10 +370,9 @@ struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
int is_ap_interface(enum nl80211_iftype nlmode);
int is_sta_interface(enum nl80211_iftype nlmode);
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
-int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- const u8 *bssid,
+int nl80211_get_link_signal(struct i802_bss *bss, const u8 *bssid,
struct hostap_sta_driver_data *data);
-int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+int nl80211_get_link_noise(struct i802_bss *bss,
struct wpa_signal_info *sig_change);
int nl80211_get_wiphy_index(struct i802_bss *bss);
int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
@@ -334,8 +382,6 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
int local_state_change,
struct i802_bss *bss);
-int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
-void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
const void *data, size_t len,
int encrypt, int noack);
@@ -343,7 +389,7 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
struct hostapd_hw_modes *
nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
- u8 *dfs_domain);
+ u8 *dfs_domain, char *alpha2, size_t alpha2_len);
int process_global_event(struct nl_msg *msg, void *arg);
int process_bss_event(struct nl_msg *msg, void *arg);
@@ -352,6 +398,11 @@ 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);
+int nl80211_remove_link(struct i802_bss *bss, int link_id);
+void nl80211_update_active_links(struct i802_bss *bss, int link_id);
+int nl80211_has_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
static inline bool nl80211_link_valid(u16 links, s8 link_id)
{
@@ -409,5 +460,10 @@ int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
+struct hostapd_multi_hw_info *
+nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws);
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv);
+
+int get_sta_mlo_interface_info(struct i802_bss *bss);
#endif /* DRIVER_NL80211_H */
diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c
index dc16bd4458da..245f643e2c72 100644
--- a/contrib/wpa/src/drivers/driver_nl80211_capa.c
+++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_common.h"
+#include "common/proximity_ranging.h"
#include "common/qca-vendor.h"
#include "common/qca-vendor-attr.h"
#include "common/brcm_vendor.h"
@@ -36,7 +37,7 @@ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
}
-static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
{
u32 feat = 0;
struct nl_msg *msg;
@@ -62,6 +63,7 @@ struct wiphy_info_data {
struct wpa_driver_capa *capa;
unsigned int num_multichan_concurrent;
+ unsigned int num_multichan_concurrent_nan;
unsigned int error:1;
unsigned int device_ap_sme:1;
@@ -82,6 +84,11 @@ struct wiphy_info_data {
unsigned int update_ft_ies_supported:1;
unsigned int has_key_mgmt:1;
unsigned int has_key_mgmt_iftype:1;
+ unsigned int support_ap_scan:1;
+
+ unsigned int nan_supported:1;
+ unsigned int nan_ndp_supported:1;
+ unsigned int nan_phy_capabilities_valid:1;
};
@@ -132,6 +139,9 @@ static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
case NL80211_IFTYPE_P2P_CLIENT:
info->p2p_client_supported = 1;
break;
+ case NL80211_IFTYPE_NAN:
+ info->capa->flags2 |= WPA_DRIVER_FLAGS2_SUPPORT_NAN;
+ break;
}
}
}
@@ -157,6 +167,8 @@ static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
};
+ bool combination_has_nan = false, combination_has_nan_ndp = false;
+ unsigned int num_channels;
err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
nl_combi, iface_combination_policy);
@@ -184,20 +196,29 @@ static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
combination_has_p2p = 1;
if (ift == NL80211_IFTYPE_STATION)
combination_has_mgd = 1;
+ if (ift == NL80211_IFTYPE_NAN)
+ combination_has_nan = true;
+ if (ift == NL80211_IFTYPE_NAN_DATA)
+ combination_has_nan_ndp = true;
}
- if (combination_has_p2p && combination_has_mgd)
- break;
}
+ num_channels = nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
if (combination_has_p2p && combination_has_mgd) {
- unsigned int num_channels =
- nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
info->p2p_concurrent = 1;
if (info->num_multichan_concurrent < num_channels)
info->num_multichan_concurrent = num_channels;
}
+ if (combination_has_nan) {
+ info->nan_supported = 1;
+ info->nan_ndp_supported = combination_has_nan_ndp;
+
+ if (info->num_multichan_concurrent_nan < num_channels)
+ info->num_multichan_concurrent_nan = num_channels;
+ }
+
return 0;
}
@@ -303,9 +324,6 @@ static unsigned int get_akm_suites_info(struct nlattr *tb)
case RSN_AUTH_KEY_MGMT_CCKM:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
break;
- case RSN_AUTH_KEY_MGMT_OSEN:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
- break;
case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
break;
@@ -586,6 +604,9 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ROC_ADDR_FILTER))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER;
+ if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
if (ext_feature_isset(ext_features, len,
@@ -720,6 +741,24 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SAE_OFFLOAD_AP))
capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SPP_AMSDU;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_EPPKE) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_EPPKE;
+ }
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_IEEE8021X_AUTH))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_802_1X_AUTH;
}
@@ -746,8 +785,10 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
- if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+ if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) {
capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE;
+ }
if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
@@ -786,6 +827,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+
+ if (flags & NL80211_FEATURE_AP_SCAN)
+ info->support_ap_scan = 1;
}
@@ -937,6 +981,352 @@ static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
}
+#ifdef CONFIG_NAN
+
+static void wiphy_info_nan_capa_handler(struct wpa_driver_capa *capa,
+ struct wiphy_info_data *info,
+ struct nlattr *attr)
+{
+ static struct nla_policy
+ nan_capa_policy[NL80211_NAN_CAPABILITIES_MAX + 1] = {
+ [NL80211_NAN_CAPA_CONFIGURABLE_SYNC] = { .type = NLA_FLAG },
+ [NL80211_NAN_CAPA_USERSPACE_DE] = { .type = NLA_FLAG },
+ };
+ struct nlattr *tb_nan_capa[NL80211_NAN_CAPABILITIES_MAX + 1];
+ static struct nla_policy
+ nan_capa_phy_policy[NL80211_NAN_PHY_CAP_ATTR_MAX + 1] = {
+ [NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] = { .type = NLA_U16 },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] = {
+ .type = NLA_U8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY] = {
+ .type = NLA_U8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 16, .maxlen = 16,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA] = { .type = NLA_U32 },
+ [NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 8, .maxlen = 8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_MAC] = {
+ .type = NLA_BINARY, .minlen = 6, .maxlen = 6
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_PHY] = {
+ .type = NLA_BINARY, .minlen = 11, .maxlen = 11
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 12, .maxlen = 12,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_PPE] = {
+ .type = NLA_BINARY, .maxlen = 255,
+ },
+ };
+ struct nlattr *tb_phy[NL80211_NAN_PHY_CAP_ATTR_MAX + 1];
+
+ if (!attr)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Parsing NAN capabilities");
+
+ if (nla_parse_nested(tb_nan_capa, NL80211_NAN_CAPABILITIES_MAX, attr,
+ nan_capa_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse NAN capabilities");
+ return;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_CONFIGURABLE_SYNC]) {
+ wpa_printf(MSG_DEBUG, "nl80211: NAN sync offload supported");
+ capa->nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_USERSPACE_DE]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN user space DE is supported");
+ capa->nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME])
+ capa->nan_capa.max_channel_switch_time =
+ nla_get_u16(tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS])
+ capa->nan_capa.num_antennas =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_OP_MODE])
+ capa->nan_capa.op_modes =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_OP_MODE]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES])
+ capa->nan_capa.dev_capa =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES]);
+
+ if (!tb_nan_capa[NL80211_NAN_CAPA_PHY])
+ return;
+
+ if (nla_parse_nested(tb_phy, NL80211_NAN_PHY_CAP_ATTR_MAX,
+ tb_nan_capa[NL80211_NAN_CAPA_PHY],
+ nan_capa_phy_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse NAN PHY capabilities");
+ return;
+ }
+
+ /* HT Capabilities */
+ if (!tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY])
+ return;
+
+ capa->nan_capa.ht_capab = nla_get_u16(
+ tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA]);
+ capa->nan_capa.ht_ampdu_params = nla_get_u8(
+ tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR]) & 0x3;
+ capa->nan_capa.ht_ampdu_params |= (nla_get_u8(
+ tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY]) & 0x7) << 2;
+ os_memcpy(capa->nan_capa.ht_mcs_set,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET]));
+
+ info->nan_phy_capabilities_valid = 1;
+
+ /* VHT Capabilities */
+ if (tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] &&
+ tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]) {
+ capa->nan_capa.vht_capab = nla_get_u32(
+ tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]);
+ os_memcpy(capa->nan_capa.vht_mcs_set,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET]));
+ capa->nan_capa.vht_valid = true;
+ }
+
+ /* HE Capabilities */
+ if (!tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] ||
+ !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE])
+ goto out;
+
+ os_memcpy(capa->nan_capa.he_capab.mac_cap,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC]));
+ os_memcpy(capa->nan_capa.he_capab.phy_cap,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY]));
+
+ os_memcpy(capa->nan_capa.he_capab.mcs,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET]));
+ os_memcpy(capa->nan_capa.he_capab.ppet,
+ nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]),
+ nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]));
+
+ capa->nan_capa.he_valid = true;
+out:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN PHY capabilities vht_valid=%u, he_valid=%u",
+ capa->nan_capa.vht_valid, capa->nan_capa.he_valid);
+}
+
+#endif /* CONFIG_NAN */
+
+
+#ifdef CONFIG_PR
+
+static u32 nl80211_to_pd_bw_bitmap(u32 nl_bitmap)
+{
+ u32 bandwidths = 0;
+
+ if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_20))
+ bandwidths |= BIT(WPA_PR_CHAN_WIDTH_20);
+ if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_40))
+ bandwidths |= BIT(WPA_PR_CHAN_WIDTH_40);
+ if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_80))
+ bandwidths |= BIT(WPA_PR_CHAN_WIDTH_80);
+ if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_80P80))
+ bandwidths |= BIT(WPA_PR_CHAN_WIDTH_80P80);
+ if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_160))
+ bandwidths |= BIT(WPA_PR_CHAN_WIDTH_160);
+ return bandwidths;
+}
+
+
+static u32 nl80211_to_pd_preamble_bitmap(u32 nl_bitmap)
+{
+ u32 preambles = 0;
+
+ if (nl_bitmap & BIT(NL80211_PREAMBLE_HT))
+ preambles |= BIT(WPA_PR_PREAMBLE_HT);
+ if (nl_bitmap & BIT(NL80211_PREAMBLE_VHT))
+ preambles |= BIT(WPA_PR_PREAMBLE_VHT);
+ if (nl_bitmap & BIT(NL80211_PREAMBLE_HE))
+ preambles |= BIT(WPA_PR_PREAMBLE_HE);
+ return preambles;
+}
+
+
+static void pmsr_type_ftm_handler(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *capa)
+{
+ u32 max_rx_sts, max_tx_sts;
+ struct nlattr *tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+ struct nlattr *ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+ struct nlattr *type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1];
+ struct nlattr *rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+ if (nla_parse_nested(tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, capa, NULL))
+ return;
+
+ /* Parse ISTA capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS] &&
+ nla_parse_nested(ista_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS],
+ NULL) == 0) {
+ drv->capa.ista.support_ntb =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+ drv->capa.ista.support_tb =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+ drv->capa.ista.support_edca =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+ if (ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE])
+ drv->capa.ista.max_peers =
+ nla_get_u32(ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]);
+ }
+
+ /* Parse ranging type capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS] &&
+ !nla_parse_nested(type_caps, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS],
+ NULL)) {
+ drv->capa.ranging_type.infra_support =
+ !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT];
+ drv->capa.ranging_type.pd_support =
+ !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT];
+ }
+
+ drv->capa.concurrent_ista_rsta =
+ !!tb[NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT];
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP])
+ drv->capa.max_tx_ltf_repetations =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP])
+ drv->capa.max_rx_ltf_repetations =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP]);
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]) {
+ max_rx_sts = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]);
+ drv->capa.max_rx_sts_le_80 = max_rx_sts;
+ drv->capa.max_rx_sts_gt_80 = max_rx_sts;
+ }
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]) {
+ max_tx_sts = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]);
+ drv->capa.max_tx_sts_le_80 = max_tx_sts;
+ drv->capa.max_tx_sts_gt_80 = max_tx_sts;
+ }
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX])
+ drv->capa.max_tx_ltf_total =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX])
+ drv->capa.max_rx_ltf_total =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST])
+ drv->capa.max_ftms_per_burst =
+ nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST]);
+
+ /* Parse RSTA capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT] &&
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS] &&
+ !nla_parse_nested(rsta_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS],
+ NULL)) {
+ drv->capa.rsta.support_ntb =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+ drv->capa.rsta.support_tb =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+ drv->capa.rsta.support_edca =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+ if (rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE])
+ drv->capa.rsta.max_peers =
+ nla_get_u32(rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]);
+ }
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS])
+ drv->capa.max_tx_antenna =
+ nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS])
+ drv->capa.max_rx_antenna =
+ nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]);
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA])
+ drv->capa.edca_min_ranging_interval =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB])
+ drv->capa.ntb_min_ranging_interval =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]);
+
+ /* Parse additional ranging capabilities */
+ drv->capa.support_6ghz = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_6GHZ_SUPPORT];
+
+ drv->capa.asap_support = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_ASAP];
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES])
+ drv->capa.pd_preambles = nl80211_to_pd_preamble_bitmap(
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]));
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS])
+ drv->capa.pd_bandwidths = nl80211_to_pd_bw_bitmap(
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]));
+}
+
+
+static void wiphy_info_pmsr_type_capa(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *attr)
+{
+ struct nlattr *pos;
+ int rem;
+
+ nla_for_each_nested(pos, attr, rem) {
+ if (nla_type(pos) == NL80211_PMSR_TYPE_FTM)
+ pmsr_type_ftm_handler(drv, pos);
+ }
+}
+
+
+static void wiphy_info_pmsr_capa(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ struct nlattr *pmsr_capa[NL80211_PMSR_ATTR_MAX + 1];
+ static struct nla_policy
+ pmsr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
+ [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_U32 },
+ [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_NESTED },
+ };
+
+ if (nla_parse_nested(pmsr_capa, NL80211_PMSR_ATTR_MAX,
+ tb[NL80211_ATTR_PEER_MEASUREMENTS],
+ pmsr_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to parse PMSR capabilities");
+ return;
+ }
+
+ if (pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS])
+ drv->capa.pmsr_max_peers =
+ nla_get_u32(pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS]);
+ if (pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA])
+ wiphy_info_pmsr_type_capa(drv,
+ pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA]);
+}
+
+#endif /* CONFIG_PR */
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -959,6 +1349,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
capa->max_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+ if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
+ capa->max_probe_req_ie_len =
+ nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
+
if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
@@ -1059,6 +1453,11 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+#ifdef CONFIG_PR
+ if (tb[NL80211_ATTR_PEER_MEASUREMENTS])
+ wiphy_info_pmsr_capa(drv, tb);
+#endif /* CONFIG_PR */
+
if (tb[NL80211_ATTR_VENDOR_DATA]) {
struct nlattr *nl;
int rem;
@@ -1120,6 +1519,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT:
drv->secure_ranging_ctx_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT:
+ drv->connect_ext_vendor_cmd_avail = 1;
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
#ifdef CONFIG_DRIVER_NL80211_BRCM
@@ -1181,6 +1583,23 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
if (tb[NL80211_ATTR_MLO_SUPPORT])
capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+#ifdef CONFIG_NAN
+ if (tb[NL80211_ATTR_BANDS]) {
+ u32 bands;
+
+ bands = nla_get_u32(tb[NL80211_ATTR_BANDS]);
+ wpa_printf(MSG_DEBUG, "nl80211: NAN supported bands 0x%x",
+ bands);
+ if ((bands & BIT(NL80211_BAND_2GHZ)) &&
+ (bands & BIT(NL80211_BAND_5GHZ)))
+ capa->nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND;
+ }
+
+ wiphy_info_nan_capa_handler(capa, info,
+ tb[NL80211_ATTR_NAN_CAPABILITIES]);
+#endif /* CONFIG_NAN */
+
return NL_SKIP;
}
@@ -1196,6 +1615,10 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
info->capa = &drv->capa;
info->drv = drv;
+ /* Default to large buffer of extra IE(s) to maintain previous behavior
+ * if the driver does not support reporting an accurate limit. */
+ info->capa->max_probe_req_ie_len = 1500;
+
feat = get_nl80211_protocol_features(drv);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
flags = NLM_F_DUMP;
@@ -1262,6 +1685,49 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
if (!drv->capa.max_num_akms)
drv->capa.max_num_akms = NL80211_MAX_NR_AKM_SUITES;
+#ifdef CONFIG_NAN
+ if (info->nan_supported) {
+ if (info->nan_ndp_supported) {
+ if (info->nan_phy_capabilities_valid) {
+ wpa_printf(MSG_DEBUG, "nl80211: NAN supported");
+ drv->capa.nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN NDP supported but NAN PHY capabilities not valid");
+ }
+ }
+
+ /* TODO: Currently support only a single radio */
+ drv->capa.nan_capa.num_radios = 1;
+ drv->capa.nan_capa.sched_chans =
+ info->num_multichan_concurrent_nan;
+
+ /*
+ * nl80211 supports only a single configuration that uses 16 TU
+ * slots and a period of 512 TUs.
+ */
+ drv->capa.nan_capa.slot_duration = 16;
+ drv->capa.nan_capa.schedule_period = 512;
+
+ /*
+ * Support NAN beacon protection only if at least one of the NAN
+ * management group ciphers is supported and the driver supports
+ * beacon protection.
+ */
+ if ((info->capa->enc & (WPA_DRIVER_CAPA_ENC_BIP |
+ WPA_DRIVER_CAPA_ENC_BIP_GMAC_256)) &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ drv->capa.nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT;
+
+ if (drv->capa.nan_capa.drv_flags &
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN: beacon protection supported");
+ }
+#endif /* CONFIG_NAN */
+
return 0;
}
@@ -1443,6 +1909,28 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
drv->qca_ap_allowed_freqs = 1;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER, &info))
drv->capa.flags2 |= WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA, &info)) {
+ wpa_printf(MSG_DEBUG,
+ "The driver supports RSN overriding in STA mode");
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
+ }
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_V2, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_V2;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_PCC_MODE, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_ASSISTED_DFS;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PMKSA_PRIVACY;
+
os_free(info.flags);
}
@@ -1470,6 +1958,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
WPA_DRIVER_CAPA_KEY_MGMT_OWE |
WPA_DRIVER_CAPA_KEY_MGMT_DPP;
@@ -1485,6 +1974,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY |
WPA_DRIVER_CAPA_KEY_MGMT_SAE;
else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
drv->capa.key_mgmt |=
@@ -1553,22 +2043,23 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
if (info.set_qos_map_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
drv->have_low_prio_scan = info.have_low_prio_scan;
+ drv->support_ap_scan = info.support_ap_scan;
/*
- * If poll command and tx status are supported, mac80211 is new enough
- * to have everything we need to not need monitor interfaces.
- */
- drv->use_monitor = !info.device_ap_sme &&
- (!info.poll_command_supported || !info.data_tx_status);
-
- /*
- * If we aren't going to use monitor interfaces, but the
- * driver doesn't support data TX status, we won't get TX
+ * If the driver doesn't support data TX status, we won't get TX
* status for EAPOL frames.
*/
- if (!drv->use_monitor && !info.data_tx_status)
+ if (!info.data_tx_status)
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ /* Enable P2P2 and PCC mode capabilities by default for the drivers
+ * for which SME runs in wpa_supplicant
+ */
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_V2;
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE;
+ }
+
#ifdef CONFIG_DRIVER_NL80211_QCA
if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
qca_nl80211_check_dfs_capa(drv);
@@ -1602,6 +2093,7 @@ struct phy_info_arg {
int last_mode, last_chan_idx;
int failed;
u8 dfs_domain;
+ char alpha2[3]; /* Country code from NL80211_ATTR_REG_ALPHA2 */
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1713,6 +2205,8 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_320MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_320;
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
@@ -1814,6 +2308,8 @@ static int phy_info_freqs(struct phy_info_arg *phy_info,
[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_320MHZ] = { .type = NLA_FLAG },
+
};
int new_channels = 0;
struct hostapd_channel_data *channel;
@@ -1972,12 +2468,10 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
len